mirror of
https://github.com/standardnotes/app
synced 2026-09-20 12:13:48 -04:00
Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e07fed267f | ||
|
|
1726302615 | ||
|
|
73ca12275f | ||
|
|
1ee6be4e5c | ||
|
|
42fc1d073d | ||
|
|
c32541c6e1 | ||
|
|
34d9100af6 | ||
|
|
7f2074a6ec | ||
|
|
0e95b451d6 | ||
|
|
0e886bf0f1 | ||
|
|
9b3f11d85b | ||
|
|
349fd7204e | ||
|
|
afa9aafa3e | ||
|
|
b57786eeeb | ||
|
|
f30d0898ec | ||
|
|
1729bc4292 | ||
|
|
abe2b17cb4 | ||
|
|
d51de34daa | ||
|
|
ec0f0a2b18 | ||
|
|
a417e5ba8d | ||
|
|
02eb969e07 | ||
|
|
767d354780 | ||
|
|
052e7d2f90 | ||
|
|
e3ec8503a5 | ||
|
|
cc2762a29d | ||
|
|
96e8dfdd31 | ||
|
|
60cbd7aed2 | ||
|
|
2b0c9b188c | ||
|
|
4181a4cca0 | ||
|
|
d51ab36bbf | ||
|
|
81774d74c6 | ||
|
|
148aee441b | ||
|
|
db3069eab3 | ||
|
|
c045eb1459 | ||
|
|
e19184c2da | ||
|
|
7f6c53dfcb | ||
|
|
1c9ad49aea | ||
|
|
6fc1bb5e84 |
@@ -160,7 +160,6 @@ jobs:
|
||||
run:
|
||||
working-directory: packages/desktop
|
||||
steps:
|
||||
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
@@ -169,6 +168,8 @@ jobs:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
|
||||
|
||||
- run: yarn install --immutable
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.20.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.20.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.20.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.20.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.20.11",
|
||||
"version": "1.20.14",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Disable all-caps for H2/H3 headings in Super notes ([9b3f11d](https://github.com/standardnotes/app/commit/9b3f11d85b10914a03aab93803513766670c47fe))
|
||||
* Super note block drag-n-drop on mobile ([#2072](https://github.com/standardnotes/app/issues/2072)) ([349fd72](https://github.com/standardnotes/app/commit/349fd7204e209d56b720a3b0cc6b2bfc0fc50f47))
|
||||
|
||||
## [1.9.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/blocks-editor
|
||||
|
||||
## [1.9.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/blocks-editor
|
||||
|
||||
## [1.9.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Make super note styling consistent with plaintext notes ([#2068](https://github.com/standardnotes/app/issues/2068)) ([a417e5b](https://github.com/standardnotes/app/commit/a417e5ba8d3fc224b1527d0a9cfb0f25d197f9de))
|
||||
|
||||
## [1.9.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Only autofocus super notes when creating new note ([#2063](https://github.com/standardnotes/app/issues/2063)) ([96e8dfd](https://github.com/standardnotes/app/commit/96e8dfdd310fad88a360c4ac984b376b51a618d1))
|
||||
|
||||
## [1.9.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/blocks-editor
|
||||
|
||||
# [1.9.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/blocks-editor",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.6",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
|
||||
@@ -2,7 +2,6 @@ import {FunctionComponent, useCallback, useState} from 'react';
|
||||
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
|
||||
import {ContentEditable} from '@lexical/react/LexicalContentEditable';
|
||||
import {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin';
|
||||
import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
|
||||
import {CheckListPlugin} from '@lexical/react/LexicalCheckListPlugin';
|
||||
import {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin';
|
||||
import {MarkdownShortcutPlugin} from '@lexical/react/LexicalMarkdownShortcutPlugin';
|
||||
@@ -25,7 +24,6 @@ import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugi
|
||||
import {truncateString} from './Utils';
|
||||
import {SuperEditorContentId} from './Constants';
|
||||
import {classNames} from '@standardnotes/utils';
|
||||
import {EditorLineHeight} from '@standardnotes/snjs';
|
||||
import {MarkdownTransformers} from './MarkdownTransformers';
|
||||
|
||||
type BlocksEditorProps = {
|
||||
@@ -35,7 +33,6 @@ type BlocksEditorProps = {
|
||||
previewLength?: number;
|
||||
spellcheck?: boolean;
|
||||
ignoreFirstChange?: boolean;
|
||||
lineHeight?: EditorLineHeight;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
@@ -46,7 +43,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
previewLength,
|
||||
spellcheck,
|
||||
ignoreFirstChange = false,
|
||||
lineHeight,
|
||||
readonly,
|
||||
}) => {
|
||||
const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false);
|
||||
@@ -104,7 +100,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
id={SuperEditorContentId}
|
||||
className={classNames(
|
||||
'ContentEditable__root overflow-y-auto',
|
||||
lineHeight && `leading-${lineHeight.toLowerCase()}`,
|
||||
className,
|
||||
)}
|
||||
spellCheck={spellcheck}
|
||||
@@ -121,7 +116,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
<OnChangePlugin onChange={handleChange} ignoreSelectionChange={true} />
|
||||
<HistoryPlugin />
|
||||
<HorizontalRulePlugin />
|
||||
<AutoFocusPlugin />
|
||||
<ClearEditorPlugin />
|
||||
<CheckListPlugin />
|
||||
<CodeHighlightPlugin />
|
||||
|
||||
@@ -17,8 +17,16 @@ import {
|
||||
DRAGOVER_COMMAND,
|
||||
DROP_COMMAND,
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
} from 'lexical';
|
||||
import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
|
||||
import {
|
||||
DragEvent as ReactDragEvent,
|
||||
TouchEvent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {createPortal} from 'react-dom';
|
||||
import {BlockIcon} from '@standardnotes/icons';
|
||||
|
||||
@@ -26,7 +34,7 @@ import {isHTMLElement} from '../../Utils/guard';
|
||||
import {Point} from '../../Utils/point';
|
||||
import {ContainsPointReturn, Rect} from '../../Utils/rect';
|
||||
|
||||
const SPACE = 4;
|
||||
const DRAGGABLE_BLOCK_MENU_LEFT_SPACE = -2;
|
||||
const TARGET_LINE_HALF_HEIGHT = 2;
|
||||
const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu';
|
||||
const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
|
||||
@@ -57,11 +65,10 @@ function getTopLevelNodeKeys(editor: LexicalEditor): string[] {
|
||||
function elementContainingEventLocation(
|
||||
anchorElem: HTMLElement,
|
||||
element: HTMLElement,
|
||||
event: MouseEvent,
|
||||
eventLocation: Point,
|
||||
): {contains: ContainsPointReturn; element: HTMLElement} {
|
||||
const anchorElementRect = anchorElem.getBoundingClientRect();
|
||||
|
||||
const eventLocation = new Point(event.x, event.y);
|
||||
const elementDomRect = Rect.fromDOM(element);
|
||||
const {marginTop, marginBottom} = window.getComputedStyle(element);
|
||||
|
||||
@@ -87,7 +94,7 @@ function elementContainingEventLocation(
|
||||
const childResult = elementContainingEventLocation(
|
||||
anchorElem,
|
||||
child as HTMLElement,
|
||||
event,
|
||||
eventLocation,
|
||||
);
|
||||
|
||||
if (childResult.contains.result) {
|
||||
@@ -102,7 +109,7 @@ function elementContainingEventLocation(
|
||||
function getBlockElement(
|
||||
anchorElem: HTMLElement,
|
||||
editor: LexicalEditor,
|
||||
event: MouseEvent,
|
||||
eventLocation: Point,
|
||||
): HTMLElement | null {
|
||||
const topLevelNodeKeys = getTopLevelNodeKeys(editor);
|
||||
|
||||
@@ -121,7 +128,7 @@ function getBlockElement(
|
||||
const {contains, element} = elementContainingEventLocation(
|
||||
anchorElem,
|
||||
elem,
|
||||
event,
|
||||
eventLocation,
|
||||
);
|
||||
|
||||
if (contains.result) {
|
||||
@@ -172,7 +179,7 @@ function setMenuPosition(
|
||||
(parseInt(targetStyle.lineHeight, 10) - floatingElemRect.height) / 2 -
|
||||
anchorElementRect.top;
|
||||
|
||||
const left = SPACE;
|
||||
const left = DRAGGABLE_BLOCK_MENU_LEFT_SPACE;
|
||||
|
||||
floatingElem.style.opacity = '1';
|
||||
floatingElem.style.transform = `translate(${left}px, ${top}px)`;
|
||||
@@ -214,11 +221,12 @@ function setTargetLine(
|
||||
}
|
||||
|
||||
const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT;
|
||||
const left = TEXT_BOX_HORIZONTAL_PADDING - SPACE;
|
||||
const left = TEXT_BOX_HORIZONTAL_PADDING - DRAGGABLE_BLOCK_MENU_LEFT_SPACE;
|
||||
|
||||
targetLineElem.style.transform = `translate(${left}px, ${top}px)`;
|
||||
targetLineElem.style.width = `${
|
||||
anchorWidth - (TEXT_BOX_HORIZONTAL_PADDING - SPACE) * 2
|
||||
anchorWidth -
|
||||
(TEXT_BOX_HORIZONTAL_PADDING - DRAGGABLE_BLOCK_MENU_LEFT_SPACE) * 2
|
||||
}px`;
|
||||
targetLineElem.style.opacity = '.6';
|
||||
}
|
||||
@@ -240,6 +248,7 @@ function useDraggableBlockMenu(
|
||||
const targetLineRef = useRef<HTMLDivElement>(null);
|
||||
const [draggableBlockElem, setDraggableBlockElem] =
|
||||
useState<HTMLElement | null>(null);
|
||||
const dragDataRef = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
function onMouseMove(event: MouseEvent) {
|
||||
@@ -253,7 +262,11 @@ function useDraggableBlockMenu(
|
||||
return;
|
||||
}
|
||||
|
||||
const _draggableBlockElem = getBlockElement(anchorElem, editor, event);
|
||||
const _draggableBlockElem = getBlockElement(
|
||||
anchorElem,
|
||||
editor,
|
||||
new Point(event.clientX, event.clientY),
|
||||
);
|
||||
|
||||
setDraggableBlockElem(_draggableBlockElem);
|
||||
}
|
||||
@@ -277,54 +290,13 @@ function useDraggableBlockMenu(
|
||||
}
|
||||
}, [anchorElem, draggableBlockElem]);
|
||||
|
||||
useEffect(() => {
|
||||
function onDragover(event: DragEvent): boolean {
|
||||
const [isFileTransfer] = eventFiles(event);
|
||||
if (isFileTransfer) {
|
||||
return false;
|
||||
}
|
||||
const {pageY, target} = event;
|
||||
if (!isHTMLElement(target)) {
|
||||
return false;
|
||||
}
|
||||
const targetBlockElem = getBlockElement(anchorElem, editor, event);
|
||||
const targetLineElem = targetLineRef.current;
|
||||
if (targetBlockElem === null || targetLineElem === null) {
|
||||
return false;
|
||||
}
|
||||
setTargetLine(targetLineElem, targetBlockElem, pageY, anchorElem);
|
||||
// Prevent default event to be able to trigger onDrop events
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
function onDrop(event: DragEvent): boolean {
|
||||
const [isFileTransfer] = eventFiles(event);
|
||||
if (isFileTransfer) {
|
||||
return false;
|
||||
}
|
||||
const {target, dataTransfer, pageY} = event;
|
||||
const dragData = dataTransfer?.getData(DRAG_DATA_FORMAT) || '';
|
||||
const draggedNode = $getNodeByKey(dragData);
|
||||
if (!draggedNode) {
|
||||
return false;
|
||||
}
|
||||
if (!isHTMLElement(target)) {
|
||||
return false;
|
||||
}
|
||||
const targetBlockElem = getBlockElement(anchorElem, editor, event);
|
||||
if (!targetBlockElem) {
|
||||
return false;
|
||||
}
|
||||
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
|
||||
|
||||
if (!targetNode) {
|
||||
return false;
|
||||
}
|
||||
if (targetNode === draggedNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const insertDraggedNode = useCallback(
|
||||
(
|
||||
draggedNode: LexicalNode,
|
||||
targetNode: LexicalNode,
|
||||
targetBlockElem: HTMLElement,
|
||||
pageY: number,
|
||||
) => {
|
||||
let nodeToInsert = draggedNode;
|
||||
const targetParent = targetNode.getParent();
|
||||
const sourceParent = draggedNode.getParent();
|
||||
@@ -342,6 +314,71 @@ function useDraggableBlockMenu(
|
||||
} else {
|
||||
targetNode.insertBefore(nodeToInsert);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
function onDragover(event: DragEvent): boolean {
|
||||
const [isFileTransfer] = eventFiles(event);
|
||||
if (isFileTransfer) {
|
||||
return false;
|
||||
}
|
||||
const {pageY, target} = event;
|
||||
if (!isHTMLElement(target)) {
|
||||
return false;
|
||||
}
|
||||
const targetBlockElem = getBlockElement(
|
||||
anchorElem,
|
||||
editor,
|
||||
new Point(event.pageX, pageY),
|
||||
);
|
||||
const targetLineElem = targetLineRef.current;
|
||||
if (targetBlockElem === null || targetLineElem === null) {
|
||||
return false;
|
||||
}
|
||||
setTargetLine(targetLineElem, targetBlockElem, pageY, anchorElem);
|
||||
// Prevent default event to be able to trigger onDrop events
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
function onDrop(event: DragEvent): boolean {
|
||||
const [isFileTransfer] = eventFiles(event);
|
||||
if (isFileTransfer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {target, dataTransfer, pageY} = event;
|
||||
if (!isHTMLElement(target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dragData = dataTransfer?.getData(DRAG_DATA_FORMAT) || '';
|
||||
const draggedNode = $getNodeByKey(dragData);
|
||||
if (!draggedNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetBlockElem = getBlockElement(
|
||||
anchorElem,
|
||||
editor,
|
||||
new Point(event.pageX, pageY),
|
||||
);
|
||||
if (!targetBlockElem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
|
||||
if (!targetNode) {
|
||||
return false;
|
||||
}
|
||||
if (targetNode === draggedNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
insertDraggedNode(draggedNode, targetNode, targetBlockElem, event.pageY);
|
||||
|
||||
setDraggableBlockElem(null);
|
||||
|
||||
return true;
|
||||
@@ -363,7 +400,7 @@ function useDraggableBlockMenu(
|
||||
COMMAND_PRIORITY_HIGH,
|
||||
),
|
||||
);
|
||||
}, [anchorElem, editor]);
|
||||
}, [anchorElem, editor, insertDraggedNode]);
|
||||
|
||||
function onDragStart(event: ReactDragEvent<HTMLDivElement>): void {
|
||||
const dataTransfer = event.dataTransfer;
|
||||
@@ -385,6 +422,79 @@ function useDraggableBlockMenu(
|
||||
hideTargetLine(targetLineRef.current);
|
||||
}
|
||||
|
||||
function onTouchStart(): void {
|
||||
if (!draggableBlockElem) {
|
||||
return;
|
||||
}
|
||||
editor.update(() => {
|
||||
const node = $getNearestNodeFromDOMNode(draggableBlockElem);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const nodeKey = node.getKey();
|
||||
dragDataRef.current = nodeKey;
|
||||
});
|
||||
}
|
||||
|
||||
function onTouchMove(event: TouchEvent) {
|
||||
const {pageX, pageY} = event.targetTouches[0];
|
||||
const rootElement = editor.getRootElement();
|
||||
if (rootElement) {
|
||||
const {top, bottom} = rootElement.getBoundingClientRect();
|
||||
const scrollOffset = 20;
|
||||
if (pageY - top < scrollOffset) {
|
||||
rootElement.scrollTop -= scrollOffset;
|
||||
} else if (bottom - pageY < scrollOffset) {
|
||||
rootElement.scrollTop += scrollOffset;
|
||||
}
|
||||
}
|
||||
const targetBlockElem = getBlockElement(
|
||||
anchorElem,
|
||||
editor,
|
||||
new Point(pageX, pageY),
|
||||
);
|
||||
const targetLineElem = targetLineRef.current;
|
||||
if (targetBlockElem === null || targetLineElem === null) {
|
||||
return;
|
||||
}
|
||||
setTargetLine(targetLineElem, targetBlockElem, pageY, anchorElem);
|
||||
}
|
||||
|
||||
function onTouchEnd(event: TouchEvent): void {
|
||||
hideTargetLine(targetLineRef.current);
|
||||
|
||||
editor.update(() => {
|
||||
const {pageX, pageY} = event.changedTouches[0];
|
||||
|
||||
const dragData = dragDataRef.current || '';
|
||||
const draggedNode = $getNodeByKey(dragData);
|
||||
if (!draggedNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetBlockElem = getBlockElement(
|
||||
anchorElem,
|
||||
editor,
|
||||
new Point(pageX, pageY),
|
||||
);
|
||||
if (!targetBlockElem) {
|
||||
return;
|
||||
}
|
||||
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
|
||||
|
||||
if (!targetNode) {
|
||||
return;
|
||||
}
|
||||
if (targetNode === draggedNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertDraggedNode(draggedNode, targetNode, targetBlockElem, pageY);
|
||||
});
|
||||
|
||||
setDraggableBlockElem(null);
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<>
|
||||
<div
|
||||
@@ -392,7 +502,10 @@ function useDraggableBlockMenu(
|
||||
ref={menuRef}
|
||||
draggable={true}
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}>
|
||||
onDragEnd={onDragEnd}
|
||||
onTouchStart={onTouchStart}
|
||||
onTouchMove={onTouchMove}
|
||||
onTouchEnd={onTouchEnd}>
|
||||
<div className={isEditable ? 'icon' : ''}>
|
||||
<BlockIcon className="text-text pointer-events-none" />
|
||||
</div>
|
||||
|
||||
@@ -30,12 +30,10 @@
|
||||
color: var(--sn-stylekit-editor-foreground-color);
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.Lexical__h3 {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.Lexical__textBold {
|
||||
font-weight: bold;
|
||||
|
||||
@@ -3,6 +3,60 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.103.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.103.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
# [3.103.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
### Features
|
||||
|
||||
* screen presentation and dismiss animations for mobile ([#2073](https://github.com/standardnotes/app/issues/2073)) ([7f2074a](https://github.com/standardnotes/app/commit/7f2074a6ec59068f75dae76d31721d65e61c9673))
|
||||
|
||||
## [3.102.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.102.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -22,7 +22,7 @@ declare global {
|
||||
plansUrl: string
|
||||
purchaseUrl: string
|
||||
startApplication: StartApplication
|
||||
zip: any
|
||||
zip: unknown
|
||||
electronMainEvents: any
|
||||
}
|
||||
}
|
||||
@@ -88,15 +88,15 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
|
||||
/*
|
||||
Title bar events
|
||||
*/
|
||||
document.getElementById('menu-btn')!.addEventListener('click', () => {
|
||||
document.getElementById('menu-btn')?.addEventListener('click', () => {
|
||||
remoteBridge.displayAppMenu()
|
||||
})
|
||||
|
||||
document.getElementById('min-btn')!.addEventListener('click', () => {
|
||||
document.getElementById('min-btn')?.addEventListener('click', () => {
|
||||
remoteBridge.minimizeWindow()
|
||||
})
|
||||
|
||||
document.getElementById('max-btn')!.addEventListener('click', async () => {
|
||||
document.getElementById('max-btn')?.addEventListener('click', async () => {
|
||||
if (remoteBridge.isWindowMaximized()) {
|
||||
remoteBridge.unmaximizeWindow()
|
||||
} else {
|
||||
@@ -104,15 +104,12 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
|
||||
}
|
||||
})
|
||||
|
||||
document.getElementById('close-btn')!.addEventListener('click', () => {
|
||||
document.getElementById('close-btn')?.addEventListener('click', () => {
|
||||
remoteBridge.closeWindow()
|
||||
})
|
||||
|
||||
// For Mac inset window
|
||||
const sheet = document.styleSheets[0]
|
||||
if (isMacOS) {
|
||||
sheet.insertRule('#navigation-content { padding-top: 25px !important; }', sheet.cssRules.length)
|
||||
}
|
||||
|
||||
if (isMacOS || useSystemMenuBar) {
|
||||
// !important is important here because #desktop-title-bar has display: flex.
|
||||
@@ -141,7 +138,7 @@ window.electronMainEvents.handlePerformAutomatedBackup(() => {
|
||||
void window.device.downloadBackup()
|
||||
})
|
||||
|
||||
window.electronMainEvents.handleFinishedSavingBackup((_: IpcRendererEvent, data: any) => {
|
||||
window.electronMainEvents.handleFinishedSavingBackup((_: IpcRendererEvent, data: { success: boolean }) => {
|
||||
window.webClient.didFinishBackup(data.success)
|
||||
})
|
||||
|
||||
|
||||
@@ -10,18 +10,14 @@
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.mac-desktop .app-column.selected {
|
||||
padding-top: 18px;
|
||||
.mac-desktop .app-pane {
|
||||
padding-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.mac-desktop #app.collapsed-notes.collapsed-navigation #editor-column {
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
.mac-desktop #app.collapsed-navigation #items-column {
|
||||
padding-top: 18px;
|
||||
.mac-desktop .app-pane-1 {
|
||||
padding-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.102.8",
|
||||
"version": "3.103.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.19.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.19.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.19.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.19.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.19.19",
|
||||
"version": "1.19.22",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.26.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.26.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.26.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.26.2",
|
||||
"version": "1.26.5",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.13.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.13.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.13.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.13.2",
|
||||
"version": "1.13.5",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add new line after embedding files ([7f6c53d](https://github.com/standardnotes/app/commit/7f6c53dfcbc8952ca9eee76b9c4af70fea3c1bf1))
|
||||
|
||||
# [1.6.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/icons",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.99992 15C8.67384 15 7.40207 14.4732 6.46438 13.5355C5.5267 12.5978 4.99992 11.3261 4.99992 9.99998C4.99992 9.16665 5.20825 8.35831 5.58325 7.66665L4.36659 6.44998C3.71659 7.47498 3.33325 8.69165 3.33325 9.99998C3.33325 11.7681 4.03563 13.4638 5.28587 14.714C6.53612 15.9643 8.23181 16.6666 9.99992 16.6666V19.1666L13.3333 15.8333L9.99992 12.5V15ZM9.99992 3.33331V0.833313L6.66658 4.16665L9.99992 7.49998V4.99998C11.326 4.99998 12.5978 5.52676 13.5355 6.46445C14.4731 7.40213 14.9999 8.6739 14.9999 9.99998C14.9999 10.8333 14.7916 11.6416 14.4166 12.3333L15.6333 13.55C16.2833 12.525 16.6666 11.3083 16.6666 9.99998C16.6666 8.23187 15.9642 6.53618 14.714 5.28593C13.4637 4.03569 11.768 3.33331 9.99992 3.33331Z" />
|
||||
<path d="M9.99999 15C11.3261 15 12.5978 14.4732 13.5355 13.5355C14.4732 12.5979 15 11.3261 15 10C15 9.16666 14.7917 8.35833 14.4167 7.66667L15.6333 6.45C16.2833 7.475 16.6667 8.69166 16.6667 10C16.6667 11.7681 15.9643 13.4638 14.714 14.714C13.4638 15.9643 11.7681 16.6667 9.99999 16.6667V19.1667L6.66666 15.8333L9.99999 12.5V15ZM9.99999 3.33333V0.833333L13.3333 4.16667L9.99999 7.5V5C8.67391 5 7.40214 5.52678 6.46446 6.46447C5.52677 7.40215 4.99999 8.67392 4.99999 10C4.99999 10.8333 5.20832 11.6417 5.58332 12.3333L4.36666 13.55C3.71666 12.525 3.33332 11.3083 3.33332 10C3.33332 8.23189 4.0357 6.5362 5.28594 5.28595C6.53619 4.03571 8.23188 3.33333 9.99999 3.33333Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 810 B After Width: | Height: | Size: 756 B |
@@ -3,6 +3,62 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.48.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.48.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.48.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.48.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.48.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.48.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.48.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Features
|
||||
|
||||
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
|
||||
|
||||
## [3.47.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.47.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.47.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.47.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Super editor popover menus on iOS ([#2061](https://github.com/standardnotes/app/issues/2061)) ([148aee4](https://github.com/standardnotes/app/commit/148aee441b816c58599d95db2e5b0f3f93414511))
|
||||
|
||||
## [3.47.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.47.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.47.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.47.8",
|
||||
"version": "3.48.6",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.38.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
# [1.38.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Features
|
||||
|
||||
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
|
||||
|
||||
## [1.37.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* exclude files from being counted in 'Notes' view ([#2062](https://github.com/standardnotes/app/issues/2062)) ([2b0c9b1](https://github.com/standardnotes/app/commit/2b0c9b188cd75477c0c1611443daadb8ffa29306))
|
||||
|
||||
## [1.37.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.37.8",
|
||||
"version": "1.38.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ export type TagItemCountChangeObserver = (tagUuid: Uuid | AllNotesUuidSignifier)
|
||||
export class TagItemsIndex implements SNIndex {
|
||||
private tagToItemsMap: Partial<Record<Uuid, Set<Uuid>>> = {}
|
||||
private allCountableItems = new Set<Uuid>()
|
||||
private countableItemsByType = new Map<ContentType, Set<Uuid>>()
|
||||
|
||||
constructor(private collection: ItemCollection, public observers: TagItemCountChangeObserver[] = []) {}
|
||||
|
||||
@@ -41,6 +42,14 @@ export class TagItemsIndex implements SNIndex {
|
||||
return this.allCountableItems.size
|
||||
}
|
||||
|
||||
public allCountableNotesCount(): number {
|
||||
return this.countableItemsByType.get(ContentType.Note)?.size || 0
|
||||
}
|
||||
|
||||
public allCountableFilesCount(): number {
|
||||
return this.countableItemsByType.get(ContentType.File)?.size || 0
|
||||
}
|
||||
|
||||
public countableItemsForTag(tag: SNTag): number {
|
||||
return this.tagToItemsMap[tag.uuid]?.size || 0
|
||||
}
|
||||
@@ -75,10 +84,18 @@ export class TagItemsIndex implements SNIndex {
|
||||
|
||||
for (const item of items) {
|
||||
const isCountable = this.isItemCountable(item)
|
||||
|
||||
if (isCountable) {
|
||||
this.allCountableItems.add(item.uuid)
|
||||
|
||||
if (!this.countableItemsByType.has(item.content_type)) {
|
||||
this.countableItemsByType.set(item.content_type, new Set())
|
||||
}
|
||||
|
||||
this.countableItemsByType.get(item.content_type)?.add(item.uuid)
|
||||
} else {
|
||||
this.allCountableItems.delete(item.uuid)
|
||||
this.countableItemsByType.get(item.content_type)?.delete(item.uuid)
|
||||
}
|
||||
|
||||
const associatedTagUuids = this.collection.uuidsThatReferenceUuid(item.uuid)
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { dateFromDSLDateString } from './Utils'
|
||||
import { addDaysToDate, addHoursToDate } from '@standardnotes/utils'
|
||||
|
||||
describe('Predicate Utils', () => {
|
||||
describe('dateFromDSLDateString', () => {
|
||||
it('should return a date object with the correct day', () => {
|
||||
const date = dateFromDSLDateString('1.days.ago')
|
||||
expect(date.getDate()).toEqual(new Date().getDate() - 1)
|
||||
expect(date.getDate()).toEqual(addDaysToDate(new Date(), -1).getDate())
|
||||
})
|
||||
|
||||
it('should return a date object with the correct hour', () => {
|
||||
const date = dateFromDSLDateString('1.hours.ago')
|
||||
expect(date.getHours()).toEqual(new Date().getHours() - 1)
|
||||
expect(date.getHours()).toEqual(addHoursToDate(new Date(), -1).getHours())
|
||||
})
|
||||
|
||||
it('should return a date object with the correct month', () => {
|
||||
|
||||
@@ -12,10 +12,7 @@ import { PayloadTimestampDefaults } from '../../Abstract/Payload'
|
||||
import { FilterDisplayOptions } from '../../Runtime/Display'
|
||||
import { FileItem } from '../File'
|
||||
|
||||
export function BuildSmartViews(
|
||||
options: FilterDisplayOptions,
|
||||
{ supportsFileNavigation = false }: { supportsFileNavigation: boolean },
|
||||
): SmartView[] {
|
||||
export function BuildSmartViews(options: FilterDisplayOptions): SmartView[] {
|
||||
const notes = new SmartView(
|
||||
new DecryptedPayload({
|
||||
uuid: SystemViewId.AllNotes,
|
||||
@@ -88,11 +85,7 @@ export function BuildSmartViews(
|
||||
}),
|
||||
)
|
||||
|
||||
if (supportsFileNavigation) {
|
||||
return [notes, starred, files, archived, trash, untagged]
|
||||
} else {
|
||||
return [notes, starred, archived, trash, untagged]
|
||||
}
|
||||
return [notes, files, starred, archived, trash, untagged]
|
||||
}
|
||||
|
||||
function allNotesPredicate(options: FilterDisplayOptions) {
|
||||
|
||||
@@ -3,6 +3,58 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.22",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.48.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
# [1.48.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Features
|
||||
|
||||
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
|
||||
|
||||
## [1.47.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.47.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.47.1",
|
||||
"version": "1.48.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -60,6 +60,7 @@ export interface ItemsClientInterface {
|
||||
addNoteCountChangeObserver(observer: TagItemCountChangeObserver): () => void
|
||||
|
||||
allCountableNotesCount(): number
|
||||
allCountableFilesCount(): number
|
||||
|
||||
countableNotesForTag(tag: SNTag | SmartView): number
|
||||
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.154.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
# [2.154.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Features
|
||||
|
||||
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
|
||||
|
||||
# [2.153.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Features
|
||||
|
||||
* convert Super notes to Markdown behind the scenes while publishing to Listed ([#2064](https://github.com/standardnotes/app/issues/2064)) ([cc2762a](https://github.com/standardnotes/app/commit/cc2762a29d83e2e4cd646345f9d3935571b27c62))
|
||||
|
||||
## [2.152.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* exclude files from being counted in 'Notes' view ([#2062](https://github.com/standardnotes/app/issues/2062)) ([2b0c9b1](https://github.com/standardnotes/app/commit/2b0c9b188cd75477c0c1611443daadb8ffa29306))
|
||||
|
||||
## [2.152.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
@@ -295,6 +295,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
||||
return this.diskStorageService
|
||||
}
|
||||
|
||||
public get actions(): InternalServices.SNActionsService {
|
||||
return this.actionsManager
|
||||
}
|
||||
|
||||
public computePrivateUsername(username: string): Promise<string | undefined> {
|
||||
return ComputePrivateUsername(this.options.crypto, username)
|
||||
}
|
||||
@@ -1379,7 +1383,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
||||
}
|
||||
|
||||
private createItemManager() {
|
||||
this.itemManager = new InternalServices.ItemManager(this.payloadManager, this.options, this.internalEventBus)
|
||||
this.itemManager = new InternalServices.ItemManager(this.payloadManager, this.internalEventBus)
|
||||
this.services.push(this.itemManager)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { ApplicationDisplayOptions, ApplicationSyncOptions } from './OptionalOptions'
|
||||
import { ApplicationSyncOptions } from './OptionalOptions'
|
||||
|
||||
export interface ApplicationOptionsWhichHaveDefaults {
|
||||
loadBatchSize: ApplicationSyncOptions['loadBatchSize']
|
||||
supportsFileNavigation: ApplicationDisplayOptions['supportsFileNavigation']
|
||||
}
|
||||
|
||||
export const ApplicationOptionsDefaults: ApplicationOptionsWhichHaveDefaults = {
|
||||
loadBatchSize: 700,
|
||||
supportsFileNavigation: false,
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@ export interface ApplicationSyncOptions {
|
||||
loadBatchSize: number
|
||||
}
|
||||
|
||||
export interface ApplicationDisplayOptions {
|
||||
supportsFileNavigation: boolean
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface ApplicationDisplayOptions {}
|
||||
|
||||
export interface ApplicationOptionalConfiguratioOptions {
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { SNRootKey } from '@standardnotes/encryption'
|
||||
import { ChallengeService } from '../Challenge'
|
||||
import { ListedService } from '../Listed/ListedService'
|
||||
@@ -18,6 +19,8 @@ import {
|
||||
isErrorDecryptingPayload,
|
||||
CreateEncryptedBackupFileContextPayload,
|
||||
EncryptedTransferPayload,
|
||||
TransferPayload,
|
||||
ItemContent,
|
||||
} from '@standardnotes/models'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { PayloadManager } from '../Payloads/PayloadManager'
|
||||
@@ -34,6 +37,8 @@ import {
|
||||
Challenge,
|
||||
} from '@standardnotes/services'
|
||||
|
||||
type PayloadRequestHandler = (uuid: string) => TransferPayload | undefined
|
||||
|
||||
/**
|
||||
* The Actions Service allows clients to interact with action-based extensions.
|
||||
* Action-based extensions are mostly RESTful actions that can push a local value or
|
||||
@@ -50,6 +55,7 @@ import {
|
||||
*/
|
||||
export class SNActionsService extends AbstractService {
|
||||
private previousPasswords: string[] = []
|
||||
private payloadRequestHandlers: PayloadRequestHandler[] = []
|
||||
|
||||
constructor(
|
||||
private itemManager: ItemManager,
|
||||
@@ -77,10 +83,19 @@ export class SNActionsService extends AbstractService {
|
||||
;(this.challengeService as unknown) = undefined
|
||||
;(this.protocolService as unknown) = undefined
|
||||
;(this.syncService as unknown) = undefined
|
||||
this.payloadRequestHandlers.length = 0
|
||||
this.previousPasswords.length = 0
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
public addPayloadRequestHandler(handler: PayloadRequestHandler) {
|
||||
this.payloadRequestHandlers.push(handler)
|
||||
|
||||
return () => {
|
||||
removeFromArray(this.payloadRequestHandlers, handler)
|
||||
}
|
||||
}
|
||||
|
||||
public getExtensions(): SNActionsExtension[] {
|
||||
const extensionItems = this.itemManager.getItems<SNActionsExtension>(ContentType.ActionsExtension)
|
||||
const excludingListed = extensionItems.filter((extension) => !extension.isListedExtension)
|
||||
@@ -312,7 +327,16 @@ export class SNActionsService extends AbstractService {
|
||||
return {} as ActionResponse
|
||||
}
|
||||
|
||||
private async outgoingPayloadForItem(item: DecryptedItemInterface, decrypted = false) {
|
||||
private async outgoingPayloadForItem(
|
||||
item: DecryptedItemInterface,
|
||||
decrypted = false,
|
||||
): Promise<TransferPayload<ItemContent>> {
|
||||
const payloadFromHandler = this.getPayloadFromRequestHandlers(item.uuid)
|
||||
|
||||
if (payloadFromHandler) {
|
||||
return payloadFromHandler
|
||||
}
|
||||
|
||||
if (decrypted) {
|
||||
return item.payload.ejected()
|
||||
}
|
||||
@@ -323,4 +347,15 @@ export class SNActionsService extends AbstractService {
|
||||
|
||||
return CreateEncryptedBackupFileContextPayload(encrypted)
|
||||
}
|
||||
|
||||
private getPayloadFromRequestHandlers(uuid: string): TransferPayload | undefined {
|
||||
for (const handler of this.payloadRequestHandlers) {
|
||||
const payload = handler(uuid)
|
||||
if (payload) {
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('itemManager', () => {
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () => {
|
||||
return new ItemManager(payloadManager, { supportsFileNavigation: false }, internalEventBus)
|
||||
return new ItemManager(payloadManager, internalEventBus)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -8,7 +8,6 @@ import * as Models from '@standardnotes/models'
|
||||
import * as Services from '@standardnotes/services'
|
||||
import { PayloadManagerChangeData } from '../Payloads'
|
||||
import { DiagnosticInfo, ItemsClientInterface, ItemRelationshipDirection } from '@standardnotes/services'
|
||||
import { ApplicationDisplayOptions } from '@Lib/Application/Options/OptionalOptions'
|
||||
import { CollectionSort, DecryptedItemInterface, ItemContent, SmartViewDefaultIconName } from '@standardnotes/models'
|
||||
|
||||
type ItemsChangeObserver<I extends Models.DecryptedItemInterface = Models.DecryptedItemInterface> = {
|
||||
@@ -44,7 +43,6 @@ export class ItemManager
|
||||
|
||||
constructor(
|
||||
private payloadManager: PayloadManager,
|
||||
private readonly options: ApplicationDisplayOptions = { supportsFileNavigation: false },
|
||||
protected override internalEventBus: Services.InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
@@ -55,7 +53,7 @@ export class ItemManager
|
||||
}
|
||||
|
||||
private rebuildSystemSmartViews(criteria: Models.FilterDisplayOptions): Models.SmartView[] {
|
||||
this.systemSmartViews = Models.BuildSmartViews(criteria, this.options)
|
||||
this.systemSmartViews = Models.BuildSmartViews(criteria)
|
||||
return this.systemSmartViews
|
||||
}
|
||||
|
||||
@@ -68,7 +66,7 @@ export class ItemManager
|
||||
{
|
||||
sortBy: 'created_at',
|
||||
sortDirection: 'dsc',
|
||||
hiddenContentTypes: !this.options.supportsFileNavigation ? [ContentType.File] : [],
|
||||
hiddenContentTypes: [],
|
||||
},
|
||||
)
|
||||
this.tagDisplayController = new Models.ItemDisplayController(this.collection, [ContentType.Tag], {
|
||||
@@ -184,12 +182,7 @@ export class ItemManager
|
||||
public getDisplayableNotes(): Models.SNNote[] {
|
||||
assert(this.navigationDisplayController.contentTypes.length === 2)
|
||||
|
||||
const fileContentTypeHidden = !this.options.supportsFileNavigation
|
||||
if (fileContentTypeHidden) {
|
||||
return this.navigationDisplayController.items() as Models.SNNote[]
|
||||
} else {
|
||||
return this.navigationDisplayController.items().filter(Models.isNote)
|
||||
}
|
||||
return this.navigationDisplayController.items().filter(Models.isNote)
|
||||
}
|
||||
|
||||
public getDisplayableFiles(): Models.FileItem[] {
|
||||
@@ -197,7 +190,6 @@ export class ItemManager
|
||||
}
|
||||
|
||||
public getDisplayableNotesAndFiles(): (Models.SNNote | Models.FileItem)[] {
|
||||
assert(this.options.supportsFileNavigation)
|
||||
return this.navigationDisplayController.items()
|
||||
}
|
||||
|
||||
@@ -215,7 +207,6 @@ export class ItemManager
|
||||
|
||||
public override deinit(): void {
|
||||
this.unsubChangeObserver()
|
||||
;(this.options as unknown) = undefined
|
||||
;(this.unsubChangeObserver as unknown) = undefined
|
||||
;(this.payloadManager as unknown) = undefined
|
||||
;(this.collection as unknown) = undefined
|
||||
@@ -289,13 +280,17 @@ export class ItemManager
|
||||
}
|
||||
|
||||
public allCountableNotesCount(): number {
|
||||
return this.tagItemsIndex.allCountableItemsCount()
|
||||
return this.tagItemsIndex.allCountableNotesCount()
|
||||
}
|
||||
|
||||
public allCountableFilesCount(): number {
|
||||
return this.tagItemsIndex.allCountableFilesCount()
|
||||
}
|
||||
|
||||
public countableNotesForTag(tag: Models.SNTag | Models.SmartView): number {
|
||||
if (tag instanceof Models.SmartView) {
|
||||
if (tag.uuid === Models.SystemViewId.AllNotes) {
|
||||
return this.tagItemsIndex.allCountableItemsCount()
|
||||
return this.tagItemsIndex.allCountableNotesCount()
|
||||
}
|
||||
|
||||
throw Error('countableItemsForTag is not meant to be used for smart views.')
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('mutator service', () => {
|
||||
internalEventBus.publish = jest.fn()
|
||||
|
||||
payloadManager = new PayloadManager(internalEventBus)
|
||||
itemManager = new ItemManager(payloadManager, { supportsFileNavigation: false }, internalEventBus)
|
||||
itemManager = new ItemManager(payloadManager, internalEventBus)
|
||||
|
||||
mutatorService = new MutatorService(
|
||||
itemManager,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.152.5",
|
||||
"version": "2.154.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/toast
|
||||
|
||||
## [1.3.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/toast
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/toast",
|
||||
"version": "1.3.11",
|
||||
"version": "1.3.12",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.17.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.17.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.17.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.17.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.17.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.17.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.17.1",
|
||||
"version": "1.17.6",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -7,3 +7,7 @@ export function isMacPlatform(platform: Platform) {
|
||||
export function isWindowsPlatform(platform: Platform) {
|
||||
return platform === Platform.WindowsDesktop || platform === Platform.WindowsWeb
|
||||
}
|
||||
|
||||
export function isMobilePlatform(platform: Platform) {
|
||||
return platform === Platform.Ios || platform === Platform.Android
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/utils
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/utils",
|
||||
"version": "1.13.0",
|
||||
"version": "1.13.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/** Negative numbers are supported as well */
|
||||
export function addDaysToDate(date: Date, days: number) {
|
||||
const result = new Date(date)
|
||||
result.setDate(result.getDate() + days)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function addHoursToDate(date: Date, hours: number) {
|
||||
const result = new Date(date)
|
||||
result.setHours(result.getHours() + hours)
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './Utils/Utils'
|
||||
export * from './Date/DateUtils'
|
||||
export * from './Deferred/Deferred'
|
||||
export * from './Utils/ClassNames'
|
||||
export * from './Utils/Utils'
|
||||
export * from './Uuid/Utils'
|
||||
export * from './Uuid/UuidGenerator'
|
||||
export * from './Uuid/UuidMap'
|
||||
export * from './Uuid/Utils'
|
||||
export * from './Deferred/Deferred'
|
||||
|
||||
@@ -3,6 +3,89 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.115.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixes issue where Dynamic Theme did not properly work with new grid system ([1726302](https://github.com/standardnotes/app/commit/1726302615c77b21bc10165a76476dadd53d5bb5))
|
||||
|
||||
## [3.115.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Embedded image aspect ratio in Super notes ([#2075](https://github.com/standardnotes/app/issues/2075)) ([c32541c](https://github.com/standardnotes/app/commit/c32541c6e162737663c65bdf43105345e45d9988))
|
||||
|
||||
# [3.115.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
### Features
|
||||
|
||||
* Move editor apperance preferences to "Appearance" tab ([0e886bf](https://github.com/standardnotes/app/commit/0e886bf0f1cfab3c094b1b0beb1003c4e14397eb))
|
||||
* screen presentation and dismiss animations for mobile ([#2073](https://github.com/standardnotes/app/issues/2073)) ([7f2074a](https://github.com/standardnotes/app/commit/7f2074a6ec59068f75dae76d31721d65e61c9673))
|
||||
|
||||
## [3.114.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* "Prevent editing" option not working correctly for Super notes ([f30d089](https://github.com/standardnotes/app/commit/f30d0898ec629c392472311a6eeb418764ed9013))
|
||||
|
||||
## [3.114.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Render super note content in revision history modal ([d51de34](https://github.com/standardnotes/app/commit/d51de34daa1dc92c3c02d8bd46b05d905ddf8744))
|
||||
|
||||
## [3.114.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Make super note styling consistent with plaintext notes ([#2068](https://github.com/standardnotes/app/issues/2068)) ([a417e5b](https://github.com/standardnotes/app/commit/a417e5ba8d3fc224b1527d0a9cfb0f25d197f9de))
|
||||
|
||||
# [3.114.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Features
|
||||
|
||||
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
|
||||
|
||||
# [3.113.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Only autofocus Super notes when creating new note ([#2063](https://github.com/standardnotes/app/issues/2063)) ([96e8dfd](https://github.com/standardnotes/app/commit/96e8dfdd310fad88a360c4ac984b376b51a618d1))
|
||||
|
||||
### Features
|
||||
|
||||
* convert Super notes to Markdown behind the scenes while publishing to Listed ([#2064](https://github.com/standardnotes/app/issues/2064)) ([cc2762a](https://github.com/standardnotes/app/commit/cc2762a29d83e2e4cd646345f9d3935571b27c62))
|
||||
|
||||
## [3.112.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.112.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
# [3.112.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Super editor popover menus on iOS ([#2061](https://github.com/standardnotes/app/issues/2061)) ([148aee4](https://github.com/standardnotes/app/commit/148aee441b816c58599d95db2e5b0f3f93414511))
|
||||
|
||||
### Features
|
||||
|
||||
* Paste files in Super notes to upload and embed them ([#2060](https://github.com/standardnotes/app/issues/2060)) ([db3069e](https://github.com/standardnotes/app/commit/db3069eab373636e3fa5ebdd15324644579dd93c))
|
||||
|
||||
## [3.111.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add new line after embedding files ([7f6c53d](https://github.com/standardnotes/app/commit/7f6c53dfcbc8952ca9eee76b9c4af70fea3c1bf1))
|
||||
|
||||
## [3.111.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* navigation footer padding ([6fc1bb5](https://github.com/standardnotes/app/commit/6fc1bb5e841543c7c07814764edfc68af6b53f5c))
|
||||
|
||||
# [3.111.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,5 +1,191 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "3.115.2",
|
||||
"title": "[3.115.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* fixes issue where Dynamic Theme did not properly work with new grid system ([1726302](https://github.com/standardnotes/app/commit/1726302615c77b21bc10165a76476dadd53d5bb5))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"fixes issue where Dynamic Theme did not properly work with new grid system (1726302)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"fixes issue where Dynamic Theme did not properly work with new grid system (1726302)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.115.1",
|
||||
"title": "[3.115.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Embedded image aspect ratio in Super notes ([#2075](https://github.com/standardnotes/app/issues/2075)) ([c32541c](https://github.com/standardnotes/app/commit/c32541c6e162737663c65bdf43105345e45d9988))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Embedded image aspect ratio in Super notes (#2075) (c32541c)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Embedded image aspect ratio in Super notes (#2075) (c32541c)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.115.0",
|
||||
"title": "[3.115.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)",
|
||||
"date": null,
|
||||
"body": "### Features\n\n* Move editor apperance preferences to \"Appearance\" tab ([0e886bf](https://github.com/standardnotes/app/commit/0e886bf0f1cfab3c094b1b0beb1003c4e14397eb))\n* screen presentation and dismiss animations for mobile ([#2073](https://github.com/standardnotes/app/issues/2073)) ([7f2074a](https://github.com/standardnotes/app/commit/7f2074a6ec59068f75dae76d31721d65e61c9673))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Move editor apperance preferences to \"Appearance\" tab (0e886bf)",
|
||||
"screen presentation and dismiss animations for mobile (#2073) (7f2074a)"
|
||||
],
|
||||
"Features": [
|
||||
"Move editor apperance preferences to \"Appearance\" tab (0e886bf)",
|
||||
"screen presentation and dismiss animations for mobile (#2073) (7f2074a)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.114.3",
|
||||
"title": "[3.114.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* \"Prevent editing\" option not working correctly for Super notes ([f30d089](https://github.com/standardnotes/app/commit/f30d0898ec629c392472311a6eeb418764ed9013))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"\"Prevent editing\" option not working correctly for Super notes (f30d089)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"\"Prevent editing\" option not working correctly for Super notes (f30d089)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.114.2",
|
||||
"title": "[3.114.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Render super note content in revision history modal ([d51de34](https://github.com/standardnotes/app/commit/d51de34daa1dc92c3c02d8bd46b05d905ddf8744))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Render super note content in revision history modal (d51de34)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Render super note content in revision history modal (d51de34)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.114.1",
|
||||
"title": "[3.114.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Make super note styling consistent with plaintext notes ([#2068](https://github.com/standardnotes/app/issues/2068)) ([a417e5b](https://github.com/standardnotes/app/commit/a417e5ba8d3fc224b1527d0a9cfb0f25d197f9de))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Make super note styling consistent with plaintext notes (#2068) (a417e5b)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Make super note styling consistent with plaintext notes (#2068) (a417e5b)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.114.0",
|
||||
"title": "[3.114.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "### Features\n\n* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"display number of files for 'Files' view (#2065) (767d354)"
|
||||
],
|
||||
"Features": [
|
||||
"display number of files for 'Files' view (#2065) (767d354)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.113.0",
|
||||
"title": "[3.113.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Only autofocus Super notes when creating new note ([#2063](https://github.com/standardnotes/app/issues/2063)) ([96e8dfd](https://github.com/standardnotes/app/commit/96e8dfdd310fad88a360c4ac984b376b51a618d1))\n\n### Features\n\n* convert Super notes to Markdown behind the scenes while publishing to Listed ([#2064](https://github.com/standardnotes/app/issues/2064)) ([cc2762a](https://github.com/standardnotes/app/commit/cc2762a29d83e2e4cd646345f9d3935571b27c62))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Only autofocus Super notes when creating new note (#2063) (96e8dfd)",
|
||||
"convert Super notes to Markdown behind the scenes while publishing to Listed (#2064) (cc2762a)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Only autofocus Super notes when creating new note (#2063) (96e8dfd)"
|
||||
],
|
||||
"Features": [
|
||||
"convert Super notes to Markdown behind the scenes while publishing to Listed (#2064) (cc2762a)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.112.2",
|
||||
"title": "[3.112.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.112.1",
|
||||
"title": "[3.112.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.112.0",
|
||||
"title": "[3.112.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Super editor popover menus on iOS ([#2061](https://github.com/standardnotes/app/issues/2061)) ([148aee4](https://github.com/standardnotes/app/commit/148aee441b816c58599d95db2e5b0f3f93414511))\n\n### Features\n\n* Paste files in Super notes to upload and embed them ([#2060](https://github.com/standardnotes/app/issues/2060)) ([db3069e](https://github.com/standardnotes/app/commit/db3069eab373636e3fa5ebdd15324644579dd93c))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Super editor popover menus on iOS (#2061) (148aee4)",
|
||||
"Paste files in Super notes to upload and embed them (#2060) (db3069e)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Super editor popover menus on iOS (#2061) (148aee4)"
|
||||
],
|
||||
"Features": [
|
||||
"Paste files in Super notes to upload and embed them (#2060) (db3069e)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.111.2",
|
||||
"title": "[3.111.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* add new line after embedding files ([7f6c53d](https://github.com/standardnotes/app/commit/7f6c53dfcbc8952ca9eee76b9c4af70fea3c1bf1))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"add new line after embedding files (7f6c53d)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"add new line after embedding files (7f6c53d)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.111.1",
|
||||
"title": "[3.111.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* navigation footer padding ([6fc1bb5](https://github.com/standardnotes/app/commit/6fc1bb5e841543c7c07814764edfc68af6b53f5c))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"navigation footer padding (6fc1bb5)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"navigation footer padding (6fc1bb5)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.111.0",
|
||||
"title": "[3.111.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.111.0",
|
||||
"version": "3.115.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes",
|
||||
@@ -12,8 +12,8 @@
|
||||
"build": "yarn clean && yarn copy:components && webpack --config web.webpack.prod.js && yarn tsc",
|
||||
"clean": "rm -fr dist",
|
||||
"format": "prettier --write src/javascripts",
|
||||
"lint": "NODE_OPTIONS=\"--max-old-space-size=4096\" eslint src/javascripts",
|
||||
"lint:fix": "NODE_OPTIONS=\"--max-old-space-size=4096\" eslint src/javascripts --fix",
|
||||
"lint": "eslint src/javascripts && yarn tsc",
|
||||
"lint:fix": "eslint src/javascripts --fix",
|
||||
"start": "webpack-dev-server --config web.webpack.dev.js",
|
||||
"start-secure": "yarn start --server-type https",
|
||||
"test": "jest --config jest.config.js --coverage",
|
||||
@@ -81,7 +81,6 @@
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-react": "^7.31.10",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
#app {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
flex: none !important;
|
||||
width: 10px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
#navigation:hover {
|
||||
flex: initial;
|
||||
width: 200px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
#items-column {
|
||||
flex: none !important;
|
||||
width: 270px !important;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
#items-column:hover {
|
||||
flex: initial;
|
||||
width: 380px !important;
|
||||
|
||||
@@ -16,6 +16,7 @@ declare global {
|
||||
webClient?: DesktopManagerInterface
|
||||
electronRemoteBridge?: unknown
|
||||
reactNativeDevice?: WebDevice
|
||||
platform?: Platform
|
||||
|
||||
application?: WebApplication
|
||||
mainApplicationGroup?: ApplicationGroup
|
||||
@@ -25,7 +26,7 @@ declare global {
|
||||
|
||||
import { disableIosTextFieldZoom } from '@/Utils'
|
||||
import { IsWebPlatform, WebAppVersion } from '@/Constants/Version'
|
||||
import { DesktopManagerInterface, SNLog } from '@standardnotes/snjs'
|
||||
import { DesktopManagerInterface, Platform, SNLog } from '@standardnotes/snjs'
|
||||
import ApplicationGroupView from './Components/ApplicationGroupView/ApplicationGroupView'
|
||||
import { WebDevice } from './Application/Device/WebDevice'
|
||||
import { StartApplication } from './Application/Device/StartApplication'
|
||||
@@ -100,6 +101,7 @@ if (IsWebPlatform) {
|
||||
|
||||
setTimeout(() => {
|
||||
const device = window.reactNativeDevice || new WebDevice(WebAppVersion)
|
||||
window.platform = device.platform
|
||||
|
||||
startApplication(window.defaultSyncServer, device, window.enabledUnfinishedFeatures, window.websocketUrl).catch(
|
||||
console.error,
|
||||
|
||||
@@ -74,7 +74,6 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
defaultHost: defaultSyncServerHost,
|
||||
appVersion: deviceInterface.appVersion,
|
||||
webSocketUrl: webSocketUrl,
|
||||
supportsFileNavigation: true,
|
||||
})
|
||||
|
||||
makeObservable(this, {
|
||||
@@ -167,11 +166,13 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
}
|
||||
}
|
||||
|
||||
publishPanelDidResizeEvent(name: string, collapsed: boolean) {
|
||||
publishPanelDidResizeEvent(name: string, width: number, collapsed: boolean) {
|
||||
const data: PanelResizedData = {
|
||||
panel: name,
|
||||
collapsed: collapsed,
|
||||
collapsed,
|
||||
width,
|
||||
}
|
||||
|
||||
this.notifyWebEvent(WebAppEvent.PanelResized, data)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,31 @@
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { getPlatformString } from '@/Utils'
|
||||
import { ApplicationEvent, Challenge, removeFromArray, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants'
|
||||
import { alertDialog, RouteType } from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import Navigation from '@/Components/Tags/Navigation'
|
||||
import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView'
|
||||
import Footer from '@/Components/Footer/Footer'
|
||||
import SessionsModal from '@/Components/SessionsModal/SessionsModal'
|
||||
import PreferencesViewWrapper from '@/Components/Preferences/PreferencesViewWrapper'
|
||||
import ChallengeModal from '@/Components/ChallengeModal/ChallengeModal'
|
||||
import NotesContextMenu from '@/Components/NotesContextMenu/NotesContextMenu'
|
||||
import PurchaseFlowWrapper from '@/Components/PurchaseFlow/PurchaseFlowWrapper'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import RevisionHistoryModal from '@/Components/RevisionHistoryModal/RevisionHistoryModal'
|
||||
import PremiumModalProvider from '@/Hooks/usePremiumModal'
|
||||
import ConfirmSignoutContainer from '@/Components/ConfirmSignoutModal/ConfirmSignoutModal'
|
||||
import { ToastContainer } from '@standardnotes/toast'
|
||||
import FilePreviewModalWrapper from '@/Components/FilePreview/FilePreviewModal'
|
||||
import ContentListView from '@/Components/ContentListView/ContentListView'
|
||||
import FileContextMenuWrapper from '@/Components/FileContextMenu/FileContextMenu'
|
||||
import PermissionsModalWrapper from '@/Components/PermissionsModal/PermissionsModalWrapper'
|
||||
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import TagContextMenuWrapper from '@/Components/Tags/TagContextMenuWrapper'
|
||||
import FileDragNDropProvider from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||
import ResponsivePaneProvider from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import FileDragNDropProvider from '../FileDragNDropProvider'
|
||||
import ResponsivePaneProvider from '../Panes/ResponsivePaneProvider'
|
||||
import AndroidBackHandlerProvider from '@/NativeMobileWeb/useAndroidBackHandler'
|
||||
import ConfirmDeleteAccountContainer from '@/Components/ConfirmDeleteAccountModal/ConfirmDeleteAccountModal'
|
||||
import DarkModeHandler from '../DarkModeHandler/DarkModeHandler'
|
||||
import ApplicationProvider from './ApplicationProvider'
|
||||
import { ErrorBoundary } from '@/Utils/ErrorBoundary'
|
||||
import CommandProvider from './CommandProvider'
|
||||
import ApplicationProvider from '../ApplicationProvider'
|
||||
import CommandProvider from '../CommandProvider'
|
||||
import PanesSystemComponent from '../Panes/PanesSystemComponent'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -45,8 +40,6 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
|
||||
const viewControllerManager = application.getViewControllerManager()
|
||||
|
||||
const appColumnContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const desktopService = application.getDesktopService()
|
||||
|
||||
@@ -137,30 +130,8 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
}, [application, onAppLaunch, onAppStart])
|
||||
|
||||
useEffect(() => {
|
||||
const removeObserver = application.addWebEventObserver(async (eventName, data) => {
|
||||
if (eventName === WebAppEvent.PanelResized) {
|
||||
if (!appColumnContainerRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
const { panel, collapsed } = data as PanelResizedData
|
||||
|
||||
if (panel === PANEL_NAME_NOTES) {
|
||||
if (collapsed) {
|
||||
appColumnContainerRef.current.classList.add('collapsed-notes')
|
||||
} else {
|
||||
appColumnContainerRef.current.classList.remove('collapsed-notes')
|
||||
}
|
||||
}
|
||||
|
||||
if (panel === PANEL_NAME_NAVIGATION) {
|
||||
if (collapsed) {
|
||||
appColumnContainerRef.current.classList.add('collapsed-navigation')
|
||||
} else {
|
||||
appColumnContainerRef.current.classList.remove('collapsed-navigation')
|
||||
}
|
||||
}
|
||||
} else if (eventName === WebAppEvent.WindowDidFocus) {
|
||||
const removeObserver = application.addWebEventObserver(async (eventName) => {
|
||||
if (eventName === WebAppEvent.WindowDidFocus) {
|
||||
if (!(await application.isLocked())) {
|
||||
application.sync.sync().catch(console.error)
|
||||
}
|
||||
@@ -206,30 +177,13 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
>
|
||||
<div className={platformString + ' main-ui-view sn-component h-full'}>
|
||||
<div id="app" className="app app-column-container" ref={appColumnContainerRef}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
>
|
||||
<Navigation application={application} />
|
||||
<ContentListView
|
||||
application={application}
|
||||
accountMenuController={viewControllerManager.accountMenuController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
itemListController={viewControllerManager.itemListController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
noAccountWarningController={viewControllerManager.noAccountWarningController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
searchOptionsController={viewControllerManager.searchOptionsController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
/>
|
||||
<ErrorBoundary>
|
||||
<NoteGroupView application={application} />
|
||||
</ErrorBoundary>
|
||||
</FileDragNDropProvider>
|
||||
</div>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
>
|
||||
<PanesSystemComponent />
|
||||
</FileDragNDropProvider>
|
||||
|
||||
<>
|
||||
<Footer application={application} applicationGroup={mainApplicationGroup} />
|
||||
|
||||
@@ -9,12 +9,11 @@ import {
|
||||
} from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||
import { FileItem, PrefKey } from '@standardnotes/snjs'
|
||||
import { FileItem, PrefKey, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import ContentList from '@/Components/ContentListView/ContentList'
|
||||
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
||||
import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
@@ -24,19 +23,19 @@ import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import ContentListHeader from './Header/ContentListHeader'
|
||||
import ResponsivePaneContent from '../ResponsivePane/ResponsivePaneContent'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { StreamingFileReader } from '@standardnotes/filepicker'
|
||||
import SearchBar from '../SearchBar/SearchBar'
|
||||
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import DailyContentList from './Daily/DailyContentList'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
import { FeatureName } from '@/Controllers/FeatureName'
|
||||
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import { useForwardedRef } from '@/Hooks/useForwardedRef'
|
||||
|
||||
type Props = {
|
||||
accountMenuController: AccountMenuController
|
||||
@@ -49,235 +48,252 @@ type Props = {
|
||||
selectionController: SelectedItemsController
|
||||
searchOptionsController: SearchOptionsController
|
||||
linkingController: LinkingController
|
||||
className?: string
|
||||
id: string
|
||||
children?: React.ReactNode
|
||||
onPanelWidthLoad: (width: number) => void
|
||||
}
|
||||
|
||||
const ContentListView: FunctionComponent<Props> = ({
|
||||
accountMenuController,
|
||||
application,
|
||||
filesController,
|
||||
itemListController,
|
||||
navigationController,
|
||||
noAccountWarningController,
|
||||
notesController,
|
||||
selectionController,
|
||||
searchOptionsController,
|
||||
linkingController,
|
||||
}) => {
|
||||
const { isNotesListVisibleOnTablets, toggleAppPane } = useResponsiveAppPane()
|
||||
const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
(
|
||||
{
|
||||
accountMenuController,
|
||||
application,
|
||||
filesController,
|
||||
itemListController,
|
||||
navigationController,
|
||||
noAccountWarningController,
|
||||
notesController,
|
||||
selectionController,
|
||||
searchOptionsController,
|
||||
linkingController,
|
||||
className,
|
||||
id,
|
||||
children,
|
||||
onPanelWidthLoad,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const { toggleAppPane, panes } = useResponsiveAppPane()
|
||||
const { selectedUuids, selectNextItem, selectPreviousItem } = selectionController
|
||||
const { selected: selectedTag, selectedAsTag } = navigationController
|
||||
const {
|
||||
completedFullSync,
|
||||
createNewNote,
|
||||
optionsSubtitle,
|
||||
paginate,
|
||||
panelTitle,
|
||||
renderedItems,
|
||||
items,
|
||||
isCurrentNoteTemplate,
|
||||
} = itemListController
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const itemsViewPanelRef = useRef<HTMLDivElement>(null)
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const innerRef = useForwardedRef(ref)
|
||||
|
||||
const { addDragTarget, removeDragTarget } = useFileDragNDrop()
|
||||
const { addDragTarget, removeDragTarget } = useFileDragNDrop()
|
||||
|
||||
const fileDropCallback = useCallback(
|
||||
async (files: FileItem[]) => {
|
||||
useEffect(() => {
|
||||
return application.addWebEventObserver((event, data) => {
|
||||
if (event === WebAppEvent.PanelResized) {
|
||||
const { panel, width } = data as PanelResizedData
|
||||
if (panel === PANEL_NAME_NOTES) {
|
||||
if (selectedAsTag) {
|
||||
void navigationController.setPanelWidthForTag(selectedAsTag, width)
|
||||
} else {
|
||||
void application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [application, navigationController, selectedAsTag])
|
||||
|
||||
useEffect(() => {
|
||||
const panelWidth = selectedTag?.preferences?.panelWidth || application.getPreference(PrefKey.NotesPanelWidth)
|
||||
if (panelWidth) {
|
||||
onPanelWidthLoad(panelWidth)
|
||||
}
|
||||
}, [selectedTag, application, onPanelWidthLoad])
|
||||
|
||||
const fileDropCallback = useCallback(
|
||||
async (files: FileItem[]) => {
|
||||
const currentTag = navigationController.selected
|
||||
|
||||
if (!currentTag) {
|
||||
return
|
||||
}
|
||||
|
||||
if (navigationController.isInAnySystemView() || navigationController.isInSmartView()) {
|
||||
console.error('Trying to link uploaded files to smart view')
|
||||
return
|
||||
}
|
||||
|
||||
files.forEach(async (file) => {
|
||||
await linkingController.linkItems(file, currentTag)
|
||||
})
|
||||
},
|
||||
[navigationController, linkingController],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const target = innerRef.current
|
||||
const currentTag = navigationController.selected
|
||||
const shouldAddDropTarget = !navigationController.isInAnySystemView() && !navigationController.isInSmartView()
|
||||
|
||||
if (!currentTag) {
|
||||
return
|
||||
if (target && shouldAddDropTarget && currentTag) {
|
||||
addDragTarget(target, {
|
||||
tooltipText: `Drop your files to upload and link them to tag "${currentTag.title}"`,
|
||||
callback: fileDropCallback,
|
||||
})
|
||||
}
|
||||
|
||||
if (navigationController.isInAnySystemView() || navigationController.isInSmartView()) {
|
||||
console.error('Trying to link uploaded files to smart view')
|
||||
return
|
||||
return () => {
|
||||
if (target) {
|
||||
removeDragTarget(target)
|
||||
}
|
||||
}
|
||||
|
||||
files.forEach(async (file) => {
|
||||
await linkingController.linkItems(file, currentTag)
|
||||
})
|
||||
},
|
||||
[navigationController, linkingController],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const target = itemsViewPanelRef.current
|
||||
const currentTag = navigationController.selected
|
||||
const shouldAddDropTarget = !navigationController.isInAnySystemView() && !navigationController.isInSmartView()
|
||||
|
||||
if (target && shouldAddDropTarget && currentTag) {
|
||||
addDragTarget(target, {
|
||||
tooltipText: `Drop your files to upload and link them to tag "${currentTag.title}"`,
|
||||
callback: fileDropCallback,
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (target) {
|
||||
removeDragTarget(target)
|
||||
}
|
||||
}
|
||||
}, [addDragTarget, fileDropCallback, navigationController, navigationController.selected, removeDragTarget])
|
||||
|
||||
const {
|
||||
completedFullSync,
|
||||
createNewNote,
|
||||
optionsSubtitle,
|
||||
paginate,
|
||||
panelTitle,
|
||||
panelWidth,
|
||||
renderedItems,
|
||||
items,
|
||||
isCurrentNoteTemplate,
|
||||
} = itemListController
|
||||
|
||||
const { selectedUuids, selectNextItem, selectPreviousItem } = selectionController
|
||||
|
||||
const { selected: selectedTag, selectedAsTag } = navigationController
|
||||
|
||||
const icon = selectedTag?.iconString
|
||||
|
||||
const isFilesSmartView = useMemo(() => navigationController.isInFilesView, [navigationController.isInFilesView])
|
||||
|
||||
const addNewItem = useCallback(async () => {
|
||||
if (isFilesSmartView) {
|
||||
if (!application.entitledToFiles) {
|
||||
application.showPremiumModal(FeatureName.Files)
|
||||
return
|
||||
}
|
||||
|
||||
if (StreamingFileReader.available()) {
|
||||
void filesController.uploadNewFile()
|
||||
return
|
||||
}
|
||||
|
||||
fileInputRef.current?.click()
|
||||
} else {
|
||||
await createNewNote()
|
||||
toggleAppPane(AppPaneId.Editor)
|
||||
}
|
||||
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
||||
|
||||
useEffect(() => {
|
||||
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
||||
/**
|
||||
* In the browser we're not allowed to override cmd/ctrl + n, so we have to
|
||||
* use Control modifier as well. These rules don't apply to desktop, but
|
||||
* probably better to be consistent.
|
||||
*/
|
||||
return application.keyboardService.addCommandHandlers([
|
||||
{
|
||||
command: CREATE_NEW_NOTE_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
void addNewItem()
|
||||
},
|
||||
},
|
||||
{
|
||||
command: NEXT_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])],
|
||||
onKeyDown: () => {
|
||||
if (searchBarElement === document.activeElement) {
|
||||
searchBarElement?.blur()
|
||||
}
|
||||
selectNextItem()
|
||||
},
|
||||
},
|
||||
{
|
||||
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
element: document.body,
|
||||
onKeyDown: () => {
|
||||
selectPreviousItem()
|
||||
},
|
||||
},
|
||||
{
|
||||
command: SEARCH_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
if (searchBarElement) {
|
||||
event.preventDefault()
|
||||
searchBarElement.focus()
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
command: CANCEL_SEARCH_COMMAND,
|
||||
onKeyDown: () => {
|
||||
if (searchBarElement) {
|
||||
searchBarElement.blur()
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
const isTargetInsideContentList = (event.target as HTMLElement).closest(`#${ElementIds.ContentList}`)
|
||||
|
||||
if (!isTargetInsideContentList) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
selectionController.selectAll()
|
||||
},
|
||||
},
|
||||
}, [
|
||||
addDragTarget,
|
||||
fileDropCallback,
|
||||
navigationController,
|
||||
navigationController.selected,
|
||||
removeDragTarget,
|
||||
innerRef,
|
||||
])
|
||||
}, [addNewItem, application.keyboardService, createNewNote, selectNextItem, selectPreviousItem, selectionController])
|
||||
|
||||
const panelResizeFinishCallback: ResizeFinishCallback = useCallback(
|
||||
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
||||
if (selectedAsTag) {
|
||||
void navigationController.setPanelWidthForTag(selectedAsTag, width)
|
||||
const icon = selectedTag?.iconString
|
||||
|
||||
const isFilesSmartView = useMemo(() => navigationController.isInFilesView, [navigationController.isInFilesView])
|
||||
|
||||
const addNewItem = useCallback(async () => {
|
||||
if (isFilesSmartView) {
|
||||
if (!application.entitledToFiles) {
|
||||
application.showPremiumModal(FeatureName.Files)
|
||||
return
|
||||
}
|
||||
|
||||
if (StreamingFileReader.available()) {
|
||||
void filesController.uploadNewFile()
|
||||
return
|
||||
}
|
||||
|
||||
fileInputRef.current?.click()
|
||||
} else {
|
||||
void application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
||||
await createNewNote()
|
||||
toggleAppPane(AppPaneId.Editor)
|
||||
}
|
||||
application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, isCollapsed)
|
||||
},
|
||||
[application, selectedAsTag, navigationController],
|
||||
)
|
||||
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
||||
|
||||
const shortcutForCreate = useMemo(
|
||||
() => application.keyboardService.keyboardShortcutForCommand(CREATE_NEW_NOTE_KEYBOARD_COMMAND),
|
||||
[application],
|
||||
)
|
||||
useEffect(() => {
|
||||
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
||||
/**
|
||||
* In the browser we're not allowed to override cmd/ctrl + n, so we have to
|
||||
* use Control modifier as well. These rules don't apply to desktop, but
|
||||
* probably better to be consistent.
|
||||
*/
|
||||
return application.keyboardService.addCommandHandlers([
|
||||
{
|
||||
command: CREATE_NEW_NOTE_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
void addNewItem()
|
||||
},
|
||||
},
|
||||
{
|
||||
command: NEXT_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])],
|
||||
onKeyDown: () => {
|
||||
if (searchBarElement === document.activeElement) {
|
||||
searchBarElement?.blur()
|
||||
}
|
||||
selectNextItem()
|
||||
},
|
||||
},
|
||||
{
|
||||
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
element: document.body,
|
||||
onKeyDown: () => {
|
||||
selectPreviousItem()
|
||||
},
|
||||
},
|
||||
{
|
||||
command: SEARCH_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
if (searchBarElement) {
|
||||
event.preventDefault()
|
||||
searchBarElement.focus()
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
command: CANCEL_SEARCH_COMMAND,
|
||||
onKeyDown: () => {
|
||||
if (searchBarElement) {
|
||||
searchBarElement.blur()
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
const isTargetInsideContentList = (event.target as HTMLElement).closest(`#${ElementIds.ContentList}`)
|
||||
|
||||
const addButtonLabel = useMemo(() => {
|
||||
return isFilesSmartView
|
||||
? 'Upload file'
|
||||
: `Create a new note in the selected tag (${shortcutForCreate && keyboardStringForShortcut(shortcutForCreate)})`
|
||||
}, [isFilesSmartView, shortcutForCreate])
|
||||
if (!isTargetInsideContentList) {
|
||||
return
|
||||
}
|
||||
|
||||
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
|
||||
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
|
||||
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
|
||||
event.preventDefault()
|
||||
selectionController.selectAll()
|
||||
},
|
||||
},
|
||||
])
|
||||
}, [
|
||||
addNewItem,
|
||||
application.keyboardService,
|
||||
createNewNote,
|
||||
selectNextItem,
|
||||
selectPreviousItem,
|
||||
selectionController,
|
||||
])
|
||||
|
||||
const dailyMode = selectedAsTag?.isDailyEntry
|
||||
const shortcutForCreate = useMemo(
|
||||
() => application.keyboardService.keyboardShortcutForCommand(CREATE_NEW_NOTE_KEYBOARD_COMMAND),
|
||||
[application],
|
||||
)
|
||||
|
||||
const handleDailyListSelection = useCallback(
|
||||
async (item: ListableContentItem, userTriggered: boolean) => {
|
||||
await selectionController.selectItemWithScrollHandling(item, {
|
||||
userTriggered: true,
|
||||
scrollIntoView: userTriggered === false,
|
||||
animated: false,
|
||||
})
|
||||
},
|
||||
[selectionController],
|
||||
)
|
||||
const addButtonLabel = useMemo(() => {
|
||||
return isFilesSmartView
|
||||
? 'Upload file'
|
||||
: `Create a new note in the selected tag (${shortcutForCreate && keyboardStringForShortcut(shortcutForCreate)})`
|
||||
}, [isFilesSmartView, shortcutForCreate])
|
||||
|
||||
useEffect(() => {
|
||||
const hasEditorPane = selectedUuids.size > 0 || renderedItems.length === 0 || isCurrentNoteTemplate
|
||||
if (!hasEditorPane) {
|
||||
itemsViewPanelRef.current?.style.removeProperty('width')
|
||||
}
|
||||
}, [selectedUuids, itemsViewPanelRef, isCurrentNoteTemplate, renderedItems])
|
||||
const dailyMode = selectedAsTag?.isDailyEntry
|
||||
|
||||
const hasEditorPane = selectedUuids.size > 0 || renderedItems.length === 0 || isCurrentNoteTemplate
|
||||
const handleDailyListSelection = useCallback(
|
||||
async (item: ListableContentItem, userTriggered: boolean) => {
|
||||
await selectionController.selectItemWithScrollHandling(item, {
|
||||
userTriggered: true,
|
||||
scrollIntoView: userTriggered === false,
|
||||
animated: false,
|
||||
})
|
||||
},
|
||||
[selectionController],
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
id="items-column"
|
||||
className={classNames(
|
||||
'sn-component section app-column flex h-full flex-col overflow-hidden pt-safe-top',
|
||||
hasEditorPane ? 'xl:w-[24rem] xsm-only:!w-full sm-only:!w-full' : 'w-full md:min-w-[400px]',
|
||||
hasEditorPane
|
||||
? isTabletScreenSize && !isNotesListVisibleOnTablets
|
||||
? 'pointer-coarse:md-only:!w-0 pointer-coarse:lg-only:!w-0'
|
||||
: 'pointer-coarse:md-only:!w-60 pointer-coarse:lg-only:!w-60'
|
||||
: '',
|
||||
)}
|
||||
aria-label={'Notes & Files'}
|
||||
ref={itemsViewPanelRef}
|
||||
>
|
||||
<ResponsivePaneContent className="overflow-hidden" paneId={AppPaneId.Items}>
|
||||
useEffect(() => {
|
||||
const hasEditorPane = panes.includes(AppPaneId.Editor)
|
||||
if (!hasEditorPane) {
|
||||
innerRef.current?.style.removeProperty('width')
|
||||
}
|
||||
}, [selectedUuids, innerRef, isCurrentNoteTemplate, renderedItems, panes])
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className={classNames(className, 'sn-component section h-full overflow-hidden pt-safe-top')}
|
||||
aria-label={'Notes & Files'}
|
||||
ref={innerRef}
|
||||
>
|
||||
<div id="items-title-bar" className="section-title-bar border-b border-solid border-border">
|
||||
<div id="items-title-bar-container">
|
||||
<input
|
||||
@@ -325,14 +341,16 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
onSelect={handleDailyListSelection}
|
||||
/>
|
||||
)}
|
||||
{!dailyMode && completedFullSync && !renderedItems.length ? (
|
||||
<p className="empty-items-list opacity-50">No items.</p>
|
||||
) : null}
|
||||
|
||||
{!dailyMode && !completedFullSync && !renderedItems.length ? (
|
||||
<p className="empty-items-list opacity-50">Loading...</p>
|
||||
) : null}
|
||||
|
||||
{!dailyMode && renderedItems.length ? (
|
||||
<>
|
||||
{completedFullSync && !renderedItems.length ? (
|
||||
<p className="empty-items-list opacity-50">No items.</p>
|
||||
) : null}
|
||||
{!completedFullSync && !renderedItems.length ? (
|
||||
<p className="empty-items-list opacity-50">Loading...</p>
|
||||
) : null}
|
||||
<ContentList
|
||||
items={renderedItems}
|
||||
selectedUuids={selectedUuids}
|
||||
@@ -347,22 +365,10 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
</>
|
||||
) : null}
|
||||
<div className="absolute bottom-0 h-safe-bottom w-full" />
|
||||
</ResponsivePaneContent>
|
||||
{hasEditorPane && itemsViewPanelRef.current && (
|
||||
<PanelResizer
|
||||
collapsable={true}
|
||||
hoverable={true}
|
||||
defaultWidth={300}
|
||||
panel={itemsViewPanelRef.current}
|
||||
side={PanelSide.Right}
|
||||
type={PanelResizeType.WidthOnly}
|
||||
resizeFinishCallback={panelResizeFinishCallback}
|
||||
width={panelWidth}
|
||||
left={0}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export default observer(ContentListView)
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
import { addDaysToDate } from '@standardnotes/utils'
|
||||
import { ListableContentItem } from '../Types/ListableContentItem'
|
||||
import { addDays, getWeekdayName } from '@/Utils/DateUtils'
|
||||
import { getWeekdayName } from '@/Utils/DateUtils'
|
||||
import { DailyItemsDay } from './DailyItemsDaySection'
|
||||
import { dateToDailyDayIdentifier } from './Utils'
|
||||
|
||||
@@ -53,11 +54,11 @@ export function insertBlanks(entries: DailyItemsDay[], location: 'front' | 'end'
|
||||
|
||||
for (let i = 1; i <= number; i++) {
|
||||
if (location === 'front') {
|
||||
const plusNFromFirstDay = addDays(laterDay, i)
|
||||
const plusNFromFirstDay = addDaysToDate(laterDay, i)
|
||||
const futureEntry = templateEntryForDate(plusNFromFirstDay)
|
||||
entries.unshift(futureEntry)
|
||||
} else {
|
||||
const minusNFromLastDay = addDays(earlierDay, -i)
|
||||
const minusNFromLastDay = addDaysToDate(earlierDay, -i)
|
||||
const pastEntry = templateEntryForDate(minusNFromLastDay)
|
||||
entries.push(pastEntry)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import { FunctionComponent, useCallback, useEffect, useLayoutEffect, useMemo, us
|
||||
import { ListableContentItem } from '../Types/ListableContentItem'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { useResponsiveAppPane } from '../../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../../Panes/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../../Panes/AppPaneMetadata'
|
||||
import { createDailyItemsWithToday, createItemsByDateMapping, insertBlanks } from './CreateDailySections'
|
||||
import { DailyItemsDay } from './DailyItemsDaySection'
|
||||
import { DailyItemCell } from './DailyItemCell'
|
||||
|
||||
@@ -6,13 +6,13 @@ import ListItemConflictIndicator from './ListItemConflictIndicator'
|
||||
import ListItemTags from './ListItemTags'
|
||||
import ListItemMetadata from './ListItemMetadata'
|
||||
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import Icon from '../Icon/Icon'
|
||||
|
||||
const FileListItem: FunctionComponent<DisplayableListItemProps<FileItem>> = ({
|
||||
|
||||
@@ -7,8 +7,6 @@ import ListItemFlagIcons from './ListItemFlagIcons'
|
||||
import ListItemTags from './ListItemTags'
|
||||
import ListItemMetadata from './ListItemMetadata'
|
||||
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import ListItemNotePreviewText from './ListItemNotePreviewText'
|
||||
import { ListItemTitle } from './ListItemTitle'
|
||||
@@ -31,8 +29,6 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
|
||||
isPreviousItemTiled,
|
||||
isNextItemTiled,
|
||||
}) => {
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
const listItemRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const noteType = item.noteType || application.componentManager.editorForNote(item)?.package_info.note_type
|
||||
@@ -65,11 +61,8 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
|
||||
}
|
||||
|
||||
const onClick = useCallback(async () => {
|
||||
const { didSelect } = await onSelect(item, true)
|
||||
if (didSelect) {
|
||||
toggleAppPane(AppPaneId.Editor)
|
||||
}
|
||||
}, [item, onSelect, toggleAppPane])
|
||||
await onSelect(item, true)
|
||||
}, [item, onSelect])
|
||||
|
||||
useContextMenuEvent(listItemRef, openContextMenu)
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||
import MenuItem from '../Menu/MenuItem'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { FileBackupRecord, FileItem } from '@standardnotes/snjs'
|
||||
import { dateToStringStyle1 } from '@/Utils/DateUtils'
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import { FilesController } from '@/Controllers/FilesController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import MenuItem from '../Menu/MenuItem'
|
||||
import { FileContextMenuBackupOption } from './FileContextMenuBackupOption'
|
||||
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { isHandlingFileDrag } from '@/Utils/DragTypeCheck'
|
||||
import { StreamingFileReader } from '@standardnotes/filepicker'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { useMemo, useState, createContext, ReactNode, useRef, useCallback, useEffect, useContext, memo } from 'react'
|
||||
import Portal from '../Portal/Portal'
|
||||
import Portal from './Portal/Portal'
|
||||
|
||||
type FileDragTargetData = {
|
||||
tooltipText: string
|
||||
@@ -20,6 +20,7 @@ const ImagePreview: FunctionComponent<Props> = ({
|
||||
imageZoomLevel,
|
||||
setImageZoomLevel,
|
||||
}) => {
|
||||
const [imageWidth, setImageWidth] = useState(0)
|
||||
const [imageHeight, setImageHeight] = useState<number>(0)
|
||||
const [imageZoomPercent, setImageZoomPercent] = useState(imageZoomLevel ? imageZoomLevel : DefaultZoomPercent)
|
||||
const [isZoomInputVisible, setIsZoomInputVisible] = useState(false)
|
||||
@@ -40,24 +41,26 @@ const ImagePreview: FunctionComponent<Props> = ({
|
||||
const image = new Image()
|
||||
image.src = objectUrl
|
||||
image.onload = () => {
|
||||
setImageWidth(image.width)
|
||||
setImageHeight(image.height)
|
||||
}
|
||||
}, [objectUrl])
|
||||
|
||||
const heightIfEmbedded = imageHeight * (imageZoomPercent / PercentageDivisor)
|
||||
const widthIfEmbedded = imageWidth * (imageZoomPercent / PercentageDivisor)
|
||||
|
||||
return (
|
||||
<div className="group flex h-full min-h-0 w-full items-center justify-center">
|
||||
<div
|
||||
className="relative flex h-full w-full items-center justify-center overflow-auto"
|
||||
style={{
|
||||
height: isEmbeddedInSuper ? `${heightIfEmbedded}px` : '',
|
||||
width: isEmbeddedInSuper ? `${widthIfEmbedded}px` : '',
|
||||
aspectRatio: isEmbeddedInSuper ? `${imageWidth} / ${imageHeight}` : '',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={objectUrl}
|
||||
style={{
|
||||
height: isEmbeddedInSuper ? `${heightIfEmbedded}px` : `${imageZoomPercent}%`,
|
||||
height: isEmbeddedInSuper ? '100%' : `${imageZoomPercent}%`,
|
||||
...(isEmbeddedInSuper
|
||||
? {}
|
||||
: imageZoomPercent <= DefaultZoomPercent
|
||||
|
||||
@@ -3,8 +3,8 @@ import { getBase64FromBlob } from '@/Utils'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import Button from '../Button/Button'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { createObjectURLWithRef } from './CreateObjectURLWithRef'
|
||||
import ImagePreview from './ImagePreview'
|
||||
import { ImageZoomLevelProps } from './ImageZoomLevelProps'
|
||||
|
||||
@@ -9,7 +9,7 @@ import LinkedItemsButton from '../LinkedItems/LinkedItemsButton'
|
||||
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
|
||||
import Popover from '../Popover/Popover'
|
||||
import FilePreviewInfoPanel from '../FilePreview/FilePreviewInfoPanel'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
|
||||
const SyncTimeoutNoDebounceMs = 100
|
||||
|
||||
@@ -2,6 +2,7 @@ import { WebApplication } from '@/Application/Application'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -13,13 +14,13 @@ const UpgradeNow = ({ application, featuresController, subscriptionContoller }:
|
||||
const shouldShowCTA = !featuresController.hasFolders
|
||||
const hasAccount = subscriptionContoller.hasAccount
|
||||
|
||||
const onClick = () => {
|
||||
if (application.isNativeIOS()) {
|
||||
const onClick = useCallback(() => {
|
||||
if (hasAccount && application.isNativeIOS()) {
|
||||
application.showPremiumModal()
|
||||
} else {
|
||||
application.openPurchaseFlow()
|
||||
}
|
||||
}
|
||||
}, [application, hasAccount])
|
||||
|
||||
return shouldShowCTA ? (
|
||||
<div className="flex h-full items-center px-2">
|
||||
|
||||
+26
-11
@@ -1,4 +1,10 @@
|
||||
import { PlatformedKeyboardShortcut, keyboardCharacterForModifier, isMacPlatform } from '@standardnotes/ui-services'
|
||||
import {
|
||||
PlatformedKeyboardShortcut,
|
||||
keyboardCharacterForModifier,
|
||||
isMacPlatform,
|
||||
isMobilePlatform,
|
||||
} from '@standardnotes/ui-services'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
type Props = {
|
||||
shortcut: PlatformedKeyboardShortcut
|
||||
@@ -6,22 +12,31 @@ type Props = {
|
||||
}
|
||||
|
||||
export const KeyboardShortcutIndicator = ({ shortcut, className }: Props) => {
|
||||
const modifiers = shortcut.modifiers || []
|
||||
const primaryKey = (shortcut.key || '').toUpperCase()
|
||||
const addPluses = !isMacPlatform(shortcut.platform)
|
||||
const spacingClass = addPluses ? '' : 'ml-0.5'
|
||||
|
||||
const keys: string[] = []
|
||||
modifiers.forEach((modifier, index) => {
|
||||
keys.push(keyboardCharacterForModifier(modifier, shortcut.platform))
|
||||
const keys = useMemo(() => {
|
||||
const modifiers = shortcut.modifiers || []
|
||||
const primaryKey = (shortcut.key || '').toUpperCase()
|
||||
|
||||
if (addPluses && (primaryKey || index !== modifiers.length - 1)) {
|
||||
keys.push('+')
|
||||
const results: string[] = []
|
||||
modifiers.forEach((modifier, index) => {
|
||||
results.push(keyboardCharacterForModifier(modifier, shortcut.platform))
|
||||
|
||||
if (addPluses && (primaryKey || index !== modifiers.length - 1)) {
|
||||
results.push('+')
|
||||
}
|
||||
})
|
||||
|
||||
if (primaryKey) {
|
||||
results.push(primaryKey)
|
||||
}
|
||||
})
|
||||
|
||||
if (primaryKey) {
|
||||
keys.push(primaryKey)
|
||||
return results
|
||||
}, [shortcut, addPluses])
|
||||
|
||||
if (isMobilePlatform(shortcut.platform)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -19,7 +19,7 @@ import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import Menu from '../Menu/Menu'
|
||||
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
linkingController: LinkingController
|
||||
|
||||
@@ -8,7 +8,7 @@ import Icon from '../Icon/Icon'
|
||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||
import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { getIconForItem } from '@/Utils/Items/Icons/getIconForItem'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag'
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -3,14 +3,14 @@ import ItemLinkAutocompleteInput from './ItemLinkAutocompleteInput'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import LinkedItemBubble from './LinkedItemBubble'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { ContentType } from '@standardnotes/snjs'
|
||||
import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||
import { FOCUS_TAGS_INPUT_COMMAND, keyboardStringForShortcut } from '@standardnotes/ui-services'
|
||||
import { useCommandService } from '../ApplicationView/CommandProvider'
|
||||
import { useCommandService } from '../CommandProvider'
|
||||
|
||||
type Props = {
|
||||
linkingController: LinkingController
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag
|
||||
import { getIconForItem } from '@/Utils/Items/Icons/getIconForItem'
|
||||
import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import Icon from '../Icon/Icon'
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { classNames } from '@standardnotes/utils'
|
||||
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ChangeEventHandler, useEffect, useRef, useState } from 'react'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import ClearInputButton from '../ClearInputButton/ClearInputButton'
|
||||
import Icon from '../Icon/Icon'
|
||||
import DecoratedInput from '../Input/DecoratedInput'
|
||||
|
||||
@@ -8,7 +8,7 @@ import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import Icon from '../Icon/Icon'
|
||||
import MenuItem from '../Menu/MenuItem'
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||
import useIsTabletOrMobileScreen from '@/Hooks/useIsTabletOrMobileScreen'
|
||||
import { classNames } from '@standardnotes/snjs'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
|
||||
/** This button is displayed in the items list header */
|
||||
export const NavigationMenuButton = () => {
|
||||
const { selectedPane, toggleAppPane } = useResponsiveAppPane()
|
||||
const { setPaneLayout } = useResponsiveAppPane()
|
||||
const { isTabletOrMobile } = useIsTabletOrMobileScreen()
|
||||
|
||||
return (
|
||||
<RoundIconButton
|
||||
className="mr-3 md:hidden pointer-coarse:md-only:flex pointer-coarse:lg-only:flex"
|
||||
className={classNames(isTabletOrMobile ? 'flex' : 'hidden', 'mr-3')}
|
||||
onClick={() => {
|
||||
if (selectedPane === AppPaneId.Items || selectedPane === AppPaneId.Editor) {
|
||||
toggleAppPane(AppPaneId.Navigation)
|
||||
} else {
|
||||
toggleAppPane(AppPaneId.Items)
|
||||
}
|
||||
setPaneLayout(PaneLayout.TagSelection)
|
||||
}}
|
||||
label="Open navigation menu"
|
||||
icon="menu-variant"
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { useMediaQuery, MediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
|
||||
import { IconType } from '@standardnotes/snjs'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { classNames, IconType } from '@standardnotes/snjs'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
import useIsTabletOrMobileScreen from '@/Hooks/useIsTabletOrMobileScreen'
|
||||
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||
|
||||
const MobileItemsListButton = () => {
|
||||
const { toggleAppPane, isNotesListVisibleOnTablets, toggleNotesListOnTablets } = useResponsiveAppPane()
|
||||
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
|
||||
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
|
||||
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
|
||||
const { panes, replacePanes, setPaneLayout } = useResponsiveAppPane()
|
||||
|
||||
const iconType: IconType = isTabletScreenSize && !isNotesListVisibleOnTablets ? 'chevron-right' : 'chevron-left'
|
||||
const label = isTabletScreenSize
|
||||
? isNotesListVisibleOnTablets
|
||||
? 'Hide items list'
|
||||
: 'Show items list'
|
||||
: 'Go to items list'
|
||||
const { isTablet, isTabletOrMobile, isMobile } = useIsTabletOrMobileScreen()
|
||||
|
||||
const itemsShown = panes.includes(AppPaneId.Items)
|
||||
|
||||
const iconType: IconType = isTablet && !itemsShown ? 'chevron-right' : 'chevron-left'
|
||||
|
||||
const label = isTablet ? (itemsShown ? 'Hide items list' : 'Show items list') : 'Go to items list'
|
||||
|
||||
return (
|
||||
<RoundIconButton
|
||||
className="mr-3 md:hidden pointer-coarse:md-only:flex pointer-coarse:lg-only:flex"
|
||||
className={classNames(isTabletOrMobile ? 'flex' : 'hidden', 'mr-3')}
|
||||
onClick={() => {
|
||||
if (isTabletScreenSize) {
|
||||
toggleNotesListOnTablets()
|
||||
if (isMobile) {
|
||||
void setPaneLayout(PaneLayout.ItemSelection)
|
||||
} else {
|
||||
toggleAppPane(AppPaneId.Items)
|
||||
if (itemsShown) {
|
||||
void replacePanes([AppPaneId.Editor])
|
||||
} else {
|
||||
void setPaneLayout(PaneLayout.ItemSelection)
|
||||
}
|
||||
}
|
||||
}}
|
||||
label={label}
|
||||
|
||||
@@ -3,9 +3,7 @@ import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import MultipleSelectedNotes from '@/Components/MultipleSelectedNotes/MultipleSelectedNotes'
|
||||
import MultipleSelectedFiles from '../MultipleSelectedFiles/MultipleSelectedFiles'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import ResponsivePaneContent from '../ResponsivePane/ResponsivePaneContent'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import FileView from '../FileView/FileView'
|
||||
import NoteView from '../NoteView/NoteView'
|
||||
import { NoteViewController } from '../NoteView/Controller/NoteViewController'
|
||||
@@ -22,6 +20,9 @@ type State = {
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
className?: string
|
||||
innerRef: (ref: HTMLDivElement) => void
|
||||
id: string
|
||||
}
|
||||
|
||||
class NoteGroupView extends AbstractComponent<Props, State> {
|
||||
@@ -97,44 +98,44 @@ class NoteGroupView extends AbstractComponent<Props, State> {
|
||||
|
||||
const hasControllers = this.state.controllers.length > 0
|
||||
|
||||
const canRenderEditorView = this.state.selectedPane === AppPaneId.Editor || !this.state.isInMobileView
|
||||
|
||||
return (
|
||||
<div id={ElementIds.EditorColumn} className="app-column app-column-third flex h-full flex-col pt-safe-top">
|
||||
<ResponsivePaneContent paneId={AppPaneId.Editor} className="flex-grow">
|
||||
{this.state.showMultipleSelectedNotes && (
|
||||
<MultipleSelectedNotes
|
||||
application={this.application}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
navigationController={this.viewControllerManager.navigationController}
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
historyModalController={this.viewControllerManager.historyModalController}
|
||||
/>
|
||||
)}
|
||||
{this.state.showMultipleSelectedFiles && (
|
||||
<MultipleSelectedFiles
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
/>
|
||||
)}
|
||||
{shouldNotShowMultipleSelectedItems && hasControllers && canRenderEditorView && (
|
||||
<>
|
||||
{this.state.controllers.map((controller) => {
|
||||
return controller instanceof NoteViewController ? (
|
||||
<NoteView key={controller.runtimeId} application={this.application} controller={controller} />
|
||||
) : (
|
||||
<FileView
|
||||
key={controller.runtimeId}
|
||||
application={this.application}
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
file={controller.item}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</ResponsivePaneContent>
|
||||
<div
|
||||
id={this.props.id}
|
||||
className={`flex h-full flex-grow flex-col pt-safe-top ${this.props.className}`}
|
||||
ref={this.props.innerRef}
|
||||
>
|
||||
{this.state.showMultipleSelectedNotes && (
|
||||
<MultipleSelectedNotes
|
||||
application={this.application}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
navigationController={this.viewControllerManager.navigationController}
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
historyModalController={this.viewControllerManager.historyModalController}
|
||||
/>
|
||||
)}
|
||||
{this.state.showMultipleSelectedFiles && (
|
||||
<MultipleSelectedFiles
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
/>
|
||||
)}
|
||||
{shouldNotShowMultipleSelectedItems && hasControllers && (
|
||||
<>
|
||||
{this.state.controllers.map((controller) => {
|
||||
return controller instanceof NoteViewController ? (
|
||||
<NoteView key={controller.runtimeId} application={this.application} controller={controller} />
|
||||
) : (
|
||||
<FileView
|
||||
key={controller.runtimeId}
|
||||
application={this.application}
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
file={controller.item}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { classNames } from '@standardnotes/utils'
|
||||
import { ReactNode, useCallback, useState } from 'react'
|
||||
import { IconType, PrefKey } from '@standardnotes/snjs'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
export type NoteStatus = {
|
||||
type: 'saving' | 'saved' | 'error'
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { StringDeleteNote, STRING_DELETE_LOCKED_ATTEMPT, STRING_DELETE_PLACEHOLDER_ATTEMPT } from '@/Constants/Strings'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { debounce, isDesktopApplication, isMobileScreen } from '@/Utils'
|
||||
import { debounce, isDesktopApplication, isMobileScreen, isTabletOrMobileScreen } from '@/Utils'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
@@ -672,10 +672,9 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
PrefDefaults[PrefKey.EditorMonospaceEnabled],
|
||||
)
|
||||
|
||||
const marginResizersEnabled = this.application.getPreference(
|
||||
PrefKey.EditorResizersEnabled,
|
||||
PrefDefaults[PrefKey.EditorResizersEnabled],
|
||||
)
|
||||
const marginResizersEnabled =
|
||||
!isTabletOrMobileScreen() &&
|
||||
this.application.getPreference(PrefKey.EditorResizersEnabled, PrefDefaults[PrefKey.EditorResizersEnabled])
|
||||
|
||||
const updateSavingIndicator = this.application.getPreference(
|
||||
PrefKey.UpdateSavingStatusIndicator,
|
||||
@@ -687,7 +686,6 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
this.setState({
|
||||
monospaceFont,
|
||||
marginResizersEnabled,
|
||||
|
||||
updateSavingIndicator,
|
||||
})
|
||||
|
||||
@@ -929,6 +927,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
left={this.state.leftResizerOffset}
|
||||
width={this.state.leftResizerWidth}
|
||||
resizeFinishCallback={this.onPanelResizeFinish}
|
||||
modifyElementWidth={true}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -980,6 +979,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
left={this.state.rightResizerOffset}
|
||||
width={this.state.rightResizerWidth}
|
||||
resizeFinishCallback={this.onPanelResizeFinish}
|
||||
modifyElementWidth={true}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { SNNote } from '@standardnotes/snjs'
|
||||
import { useEffect } from 'react'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider'
|
||||
|
||||
type Props = {
|
||||
note: SNNote
|
||||
|
||||
+51
-13
@@ -3,9 +3,16 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { FileNode } from './Nodes/FileNode'
|
||||
import { $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_EDITOR } from 'lexical'
|
||||
import {
|
||||
$createParagraphNode,
|
||||
$insertNodes,
|
||||
$isRootOrShadowRoot,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
COMMAND_PRIORITY_NORMAL,
|
||||
PASTE_COMMAND,
|
||||
} from 'lexical'
|
||||
import { $createFileNode } from './Nodes/FileUtils'
|
||||
import { $wrapNodeInElement } from '@lexical/utils'
|
||||
import { $wrapNodeInElement, mergeRegister } from '@lexical/utils'
|
||||
import { useFilesController } from '@/Controllers/FilesControllerProvider'
|
||||
import { FilesControllerEvent } from '@/Controllers/FilesController'
|
||||
|
||||
@@ -18,20 +25,51 @@ export default function FilePlugin(): JSX.Element | null {
|
||||
throw new Error('FilePlugin: FileNode not registered on editor')
|
||||
}
|
||||
|
||||
return editor.registerCommand<string>(
|
||||
INSERT_FILE_COMMAND,
|
||||
(payload) => {
|
||||
const fileNode = $createFileNode(payload)
|
||||
$insertNodes([fileNode])
|
||||
if ($isRootOrShadowRoot(fileNode.getParentOrThrow())) {
|
||||
$wrapNodeInElement(fileNode, $createParagraphNode).selectEnd()
|
||||
const uploadFilesList = (files: FileList) => {
|
||||
Array.from(files).forEach(async (file) => {
|
||||
try {
|
||||
const uploadedFiles = await filesController.uploadNewFile(file)
|
||||
if (uploadedFiles) {
|
||||
uploadedFiles.forEach((uploadedFile) => {
|
||||
editor.dispatchCommand(INSERT_FILE_COMMAND, uploadedFile.uuid)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
return mergeRegister(
|
||||
editor.registerCommand<string>(
|
||||
INSERT_FILE_COMMAND,
|
||||
(payload) => {
|
||||
const fileNode = $createFileNode(payload)
|
||||
$insertNodes([fileNode])
|
||||
if ($isRootOrShadowRoot(fileNode.getParentOrThrow())) {
|
||||
$wrapNodeInElement(fileNode, $createParagraphNode).selectEnd()
|
||||
}
|
||||
const newLineNode = $createParagraphNode()
|
||||
$insertNodes([newLineNode])
|
||||
|
||||
return true
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
editor.registerCommand(
|
||||
PASTE_COMMAND,
|
||||
(payload) => {
|
||||
const files = payload instanceof ClipboardEvent ? payload.clipboardData?.files : null
|
||||
if (files?.length) {
|
||||
uploadFilesList(files)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
COMMAND_PRIORITY_NORMAL,
|
||||
),
|
||||
)
|
||||
}, [editor])
|
||||
}, [editor, filesController])
|
||||
|
||||
useEffect(() => {
|
||||
const disposer = filesController.addEventObserver((event, data) => {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { ElementFormatType, NodeKey } from 'lexical'
|
||||
import { useApplication } from '@/Components/ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import FilePreview from '@/Components/FilePreview/FilePreview'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { useApplication } from '@/Components/ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import { downloadBlobOnAndroid } from '@/NativeMobileWeb/DownloadBlobOnAndroid'
|
||||
import { shareBlobOnMobile } from '@/NativeMobileWeb/ShareBlobOnMobile'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
@@ -13,7 +13,7 @@ import { useCallback, useEffect } from 'react'
|
||||
import { $convertToMarkdownString } from '@lexical/markdown'
|
||||
import { MarkdownTransformers } from '@standardnotes/blocks-editor'
|
||||
import { $generateHtmlFromNodes } from '@lexical/html'
|
||||
import { useCommandService } from '@/Components/ApplicationView/CommandProvider'
|
||||
import { useCommandService } from '@/Components/CommandProvider'
|
||||
|
||||
export const ExportPlugin = () => {
|
||||
const application = useApplication()
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { forwardRef, useCallback, useImperativeHandle } from 'react'
|
||||
import { $convertToMarkdownString } from '@lexical/markdown'
|
||||
import { MarkdownTransformers } from '@standardnotes/blocks-editor'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
|
||||
export type GetMarkdownPluginInterface = {
|
||||
getMarkdown: () => string
|
||||
}
|
||||
|
||||
const GetMarkdownPlugin = forwardRef<GetMarkdownPluginInterface>((_, ref) => {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getMarkdown() {
|
||||
return getMarkdown()
|
||||
},
|
||||
}))
|
||||
|
||||
const getMarkdown = useCallback(() => {
|
||||
return editor.getEditorState().read(() => {
|
||||
return $convertToMarkdownString(MarkdownTransformers)
|
||||
})
|
||||
}, [editor])
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export default GetMarkdownPlugin
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useApplication } from '@/Components/ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import LinkedItemBubble from '@/Components/LinkedItems/LinkedItemBubble'
|
||||
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
|
||||
import { useLinkingController } from '@/Controllers/LinkingControllerProvider'
|
||||
import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { useResponsiveAppPane } from '@/Components/ResponsivePane/ResponsivePaneProvider'
|
||||
import { useResponsiveAppPane } from '@/Components/Panes/ResponsivePaneProvider'
|
||||
import { LexicalNode } from 'lexical'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { TextNode } from 'lexical'
|
||||
import { FunctionComponent, useCallback, useMemo, useState } from 'react'
|
||||
import { ItemSelectionItemComponent } from './ItemSelectionItemComponent'
|
||||
import { ItemOption } from './ItemOption'
|
||||
import { useApplication } from '@/Components/ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import { ContentType, SNNote } from '@standardnotes/snjs'
|
||||
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
||||
import Popover from '@/Components/Popover/Popover'
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { SNNote, ContentType } from '@standardnotes/snjs'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
const ReadonlyPlugin = ({ note }: { note: SNNote }) => {
|
||||
const application = useApplication()
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const [readOnly, setReadOnly] = useState(note.locked)
|
||||
|
||||
useEffect(() => {
|
||||
return application.streamItems<SNNote>(ContentType.Note, ({ changed }) => {
|
||||
const changedNoteItem = changed.find((changedItem) => changedItem.uuid === note.uuid)
|
||||
|
||||
if (changedNoteItem) {
|
||||
setReadOnly(changedNoteItem.locked)
|
||||
}
|
||||
})
|
||||
}, [application, note.uuid])
|
||||
|
||||
useEffect(() => {
|
||||
editor.update(() => {
|
||||
editor.setEditable(!readOnly)
|
||||
})
|
||||
}, [editor, readOnly])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default ReadonlyPlugin
|
||||
@@ -1,5 +1,12 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ApplicationEvent, EditorLineHeight, isPayloadSourceRetrieved, PrefKey } from '@standardnotes/snjs'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
classNames,
|
||||
EditorFontSize,
|
||||
EditorLineHeight,
|
||||
isPayloadSourceRetrieved,
|
||||
PrefKey,
|
||||
} from '@standardnotes/snjs'
|
||||
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { BlocksEditor, BlocksEditorComposer } from '@standardnotes/blocks-editor'
|
||||
import { ItemSelectionPlugin } from './Plugins/ItemSelectionPlugin/ItemSelectionPlugin'
|
||||
@@ -23,10 +30,14 @@ import {
|
||||
} from './Plugins/ChangeContentCallback/ChangeContentCallback'
|
||||
import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { useCommandService } from '@/Components/ApplicationView/CommandProvider'
|
||||
import { useCommandService } from '@/Components/CommandProvider'
|
||||
import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services'
|
||||
import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview'
|
||||
import { ExportPlugin } from './Plugins/ExportPlugin/ExportPlugin'
|
||||
import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin'
|
||||
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin'
|
||||
import { getPlaintextFontSize } from '@/Utils/getPlaintextFontSize'
|
||||
import ReadonlyPlugin from './Plugins/ReadonlyPlugin/ReadonlyPlugin'
|
||||
|
||||
const NotePreviewCharLimit = 160
|
||||
|
||||
@@ -49,6 +60,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
const changeEditorFunction = useRef<ChangeEditorFunction>()
|
||||
const ignoreNextChange = useRef(false)
|
||||
const [showMarkdownPreview, setShowMarkdownPreview] = useState(false)
|
||||
const getMarkdownPlugin = useRef<GetMarkdownPluginInterface | null>(null)
|
||||
|
||||
const commandService = useCommandService()
|
||||
|
||||
@@ -63,7 +75,20 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
setShowMarkdownPreview(false)
|
||||
}, [])
|
||||
|
||||
const [lineHeight, setLineHeight] = useState<EditorLineHeight>(PrefDefaults[PrefKey.EditorLineHeight])
|
||||
useEffect(() => {
|
||||
return application.actions.addPayloadRequestHandler((uuid) => {
|
||||
if (uuid === note.current.uuid) {
|
||||
const basePayload = note.current.payload.ejected()
|
||||
return {
|
||||
...basePayload,
|
||||
content: {
|
||||
...basePayload.content,
|
||||
text: getMarkdownPlugin.current?.getMarkdown() ?? basePayload.content.text,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [application])
|
||||
|
||||
const handleChange = useCallback(
|
||||
async (value: string, preview: string) => {
|
||||
@@ -111,10 +136,15 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
return disposer
|
||||
}, [controller, controller.item.uuid])
|
||||
|
||||
const [lineHeight, setLineHeight] = useState<EditorLineHeight>(PrefDefaults[PrefKey.EditorLineHeight])
|
||||
const [fontSize, setFontSize] = useState<EditorFontSize | undefined>()
|
||||
|
||||
const reloadPreferences = useCallback(() => {
|
||||
const lineHeight = application.getPreference(PrefKey.EditorLineHeight, PrefDefaults[PrefKey.EditorLineHeight])
|
||||
const fontSize = application.getPreference(PrefKey.EditorFontSize, PrefDefaults[PrefKey.EditorFontSize])
|
||||
|
||||
setLineHeight(lineHeight)
|
||||
setFontSize(fontSize)
|
||||
}, [application])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -126,7 +156,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
}, [reloadPreferences, application])
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full">
|
||||
<div className="font-editor relative h-full w-full">
|
||||
<ErrorBoundary>
|
||||
<>
|
||||
<LinkingControllerProvider controller={linkingController}>
|
||||
@@ -139,15 +169,19 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
<BlocksEditor
|
||||
onChange={handleChange}
|
||||
ignoreFirstChange={true}
|
||||
className="relative h-full resize-none px-6 py-4 text-base focus:shadow-none focus:outline-none"
|
||||
className={classNames(
|
||||
'relative h-full resize-none px-4 py-4 focus:shadow-none focus:outline-none',
|
||||
lineHeight && `leading-${lineHeight.toLowerCase()}`,
|
||||
fontSize ? getPlaintextFontSize(fontSize) : 'text-base',
|
||||
)}
|
||||
previewLength={NotePreviewCharLimit}
|
||||
spellcheck={spellcheck}
|
||||
lineHeight={lineHeight}
|
||||
>
|
||||
<ItemSelectionPlugin currentNote={note.current} />
|
||||
<FilePlugin />
|
||||
<ItemBubblePlugin />
|
||||
<BlockPickerMenuPlugin />
|
||||
<GetMarkdownPlugin ref={getMarkdownPlugin} />
|
||||
<DatetimePlugin />
|
||||
<PasswordPlugin />
|
||||
<AutoLinkPlugin />
|
||||
@@ -157,6 +191,8 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
<NodeObserverPlugin nodeType={BubbleNode} onRemove={handleBubbleRemove} />
|
||||
<NodeObserverPlugin nodeType={FileNode} onRemove={handleBubbleRemove} />
|
||||
<ExportPlugin />
|
||||
<ReadonlyPlugin note={note.current} />
|
||||
{controller.isTemplateNote ? <AutoFocusPlugin /> : null}
|
||||
</BlocksEditor>
|
||||
</BlocksEditorComposer>
|
||||
</FilesControllerProvider>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import Popover from '../Popover/Popover'
|
||||
import { IconType } from '@standardnotes/snjs'
|
||||
import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag'
|
||||
import { useApplication } from '../ApplicationView/ApplicationProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import MenuItem from '../Menu/MenuItem'
|
||||
import Menu from '../Menu/Menu'
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import AddTagOption from './AddTagOption'
|
||||
import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
|
||||
import { NotesOptionsProps } from './NotesOptionsProps'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
|
||||
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
|
||||
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
|
||||
@@ -27,7 +27,7 @@ import { NoteAttributes } from './NoteAttributes'
|
||||
import { SpellcheckOptions } from './SpellcheckOptions'
|
||||
import { NoteSizeWarning } from './NoteSizeWarning'
|
||||
import { DeletePermanentlyButton } from './DeletePermanentlyButton'
|
||||
import { useCommandService } from '../ApplicationView/CommandProvider'
|
||||
import { useCommandService } from '../CommandProvider'
|
||||
import { iconClass } from './ClassNames'
|
||||
import SuperNoteOptions from './SuperNoteOptions'
|
||||
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
|
||||
@@ -160,6 +160,10 @@ const NotesOptions = ({
|
||||
return <ProtectedUnauthorizedLabel />
|
||||
}
|
||||
|
||||
if (notes.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{notes.length === 1 && (
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
SUPER_EXPORT_MARKDOWN,
|
||||
} from '@standardnotes/ui-services'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useCommandService } from '../ApplicationView/CommandProvider'
|
||||
import { useCommandService } from '../CommandProvider'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
|
||||
import Menu from '../Menu/Menu'
|
||||
|
||||
@@ -31,7 +31,8 @@ type Props = {
|
||||
side: PanelSide
|
||||
type: PanelResizeType
|
||||
resizeFinishCallback?: ResizeFinishCallback
|
||||
widthEventCallback?: () => void
|
||||
widthEventCallback?: (width: number) => void
|
||||
modifyElementWidth: boolean
|
||||
}
|
||||
|
||||
type State = {
|
||||
@@ -82,9 +83,12 @@ class PanelResizer extends Component<Props, State> {
|
||||
}
|
||||
|
||||
override componentDidUpdate(prevProps: Props) {
|
||||
this.lastWidth = this.props.panel.scrollWidth
|
||||
|
||||
if (this.props.width != prevProps.width) {
|
||||
this.setWidth(this.props.width)
|
||||
}
|
||||
|
||||
if (this.props.left !== prevProps.left) {
|
||||
this.setLeft(this.props.left)
|
||||
this.setWidth(this.props.width)
|
||||
@@ -108,6 +112,10 @@ class PanelResizer extends Component<Props, State> {
|
||||
}
|
||||
|
||||
getParentRect() {
|
||||
if (!this.props.panel.parentNode) {
|
||||
return new DOMRect()
|
||||
}
|
||||
|
||||
return (this.props.panel.parentNode as HTMLElement).getBoundingClientRect()
|
||||
}
|
||||
|
||||
@@ -131,7 +139,7 @@ class PanelResizer extends Component<Props, State> {
|
||||
})
|
||||
}
|
||||
|
||||
setWidth = (width: number, finish = false): void => {
|
||||
setWidth = (width: number, finish = false): number => {
|
||||
if (width === 0) {
|
||||
width = this.computeMaxWidth()
|
||||
}
|
||||
@@ -150,22 +158,29 @@ class PanelResizer extends Component<Props, State> {
|
||||
}
|
||||
|
||||
const isFullWidth = Math.round(width + this.lastLeft) === Math.round(parentRect.width)
|
||||
if (isFullWidth) {
|
||||
if (this.props.type === PanelResizeType.WidthOnly) {
|
||||
this.props.panel.style.removeProperty('width')
|
||||
if (this.props.modifyElementWidth) {
|
||||
if (isFullWidth) {
|
||||
if (this.props.type === PanelResizeType.WidthOnly) {
|
||||
this.props.panel.style.removeProperty('width')
|
||||
} else {
|
||||
this.props.panel.style.width = `calc(100% - ${this.lastLeft}px)`
|
||||
}
|
||||
} else {
|
||||
this.props.panel.style.width = `calc(100% - ${this.lastLeft}px)`
|
||||
this.props.panel.style.width = width + 'px'
|
||||
}
|
||||
} else {
|
||||
this.props.panel.style.width = width + 'px'
|
||||
}
|
||||
|
||||
this.lastWidth = width
|
||||
|
||||
if (finish) {
|
||||
this.finishSettingWidth()
|
||||
|
||||
if (this.props.resizeFinishCallback) {
|
||||
this.props.resizeFinishCallback(this.lastWidth, this.lastLeft, this.isAtMaxWidth(), this.isCollapsed())
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
setLeft = (left: number) => {
|
||||
@@ -187,10 +202,6 @@ class PanelResizer extends Component<Props, State> {
|
||||
}
|
||||
|
||||
handleWidthEvent(event?: MouseEvent) {
|
||||
if (this.props.widthEventCallback) {
|
||||
this.props.widthEventCallback()
|
||||
}
|
||||
|
||||
let x
|
||||
if (event) {
|
||||
x = event.clientX
|
||||
@@ -201,7 +212,11 @@ class PanelResizer extends Component<Props, State> {
|
||||
}
|
||||
const deltaX = x - this.lastDownX
|
||||
const newWidth = this.startWidth + deltaX
|
||||
this.setWidth(newWidth, false)
|
||||
const adjustedWidth = this.setWidth(newWidth, false)
|
||||
|
||||
if (this.props.widthEventCallback) {
|
||||
this.props.widthEventCallback(adjustedWidth)
|
||||
}
|
||||
}
|
||||
|
||||
handleLeftEvent(event: MouseEvent) {
|
||||
@@ -309,6 +324,7 @@ class PanelResizer extends Component<Props, State> {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'panel-resizer',
|
||||
'absolute right-0 top-0 z-panel-resizer',
|
||||
'hidden h-full w-[4px] cursor-col-resize border-y-0 bg-[color:var(--panel-resizer-background-color)] md:block',
|
||||
this.props.alwaysVisible || this.state.collapsed || this.state.pressed ? ' opacity-100' : 'opacity-0',
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
|
||||
export enum AppPaneId {
|
||||
Navigation = 'NavigationColumn',
|
||||
Items = 'ItemsColumn',
|
||||
Editor = 'EditorColumn',
|
||||
}
|
||||
|
||||
export const AppPaneIdToDivId = {
|
||||
[AppPaneId.Navigation]: ElementIds.NavigationColumn,
|
||||
[AppPaneId.Items]: ElementIds.ItemsColumn,
|
||||
[AppPaneId.Editor]: ElementIds.EditorColumn,
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
|
||||
const ENTRANCE_DURATION = 200
|
||||
const EXIT_DURATION = 200
|
||||
|
||||
export async function animatePaneEntranceTransitionFromOffscreenToTheRight(elementId: string): Promise<void> {
|
||||
log(LoggingDomain.Panes, 'Animating pane entrance transition from offscreen to the right', elementId)
|
||||
const element = document.getElementById(elementId)
|
||||
if (!element) {
|
||||
return
|
||||
}
|
||||
|
||||
const animation = element.animate(
|
||||
[
|
||||
{
|
||||
transform: 'translateX(100%)',
|
||||
},
|
||||
{
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
],
|
||||
{
|
||||
duration: ENTRANCE_DURATION,
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
},
|
||||
)
|
||||
|
||||
await animation.finished
|
||||
|
||||
animation.finish()
|
||||
}
|
||||
|
||||
export async function animatePaneExitTransitionOffscreenToTheRight(elementId: string): Promise<void> {
|
||||
log(LoggingDomain.Panes, 'Animating pane exit transition offscreen to the right', elementId)
|
||||
const element = document.getElementById(elementId)
|
||||
if (!element) {
|
||||
return
|
||||
}
|
||||
|
||||
const animation = element.animate(
|
||||
[
|
||||
{
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
{
|
||||
transform: 'translateX(100%)',
|
||||
},
|
||||
],
|
||||
{
|
||||
duration: EXIT_DURATION,
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
},
|
||||
)
|
||||
|
||||
await animation.finished
|
||||
|
||||
animation.finish()
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
import { PANEL_NAME_NAVIGATION, PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import useIsTabletOrMobileScreen from '@/Hooks/useIsTabletOrMobileScreen'
|
||||
import { ErrorBoundary } from '@/Utils/ErrorBoundary'
|
||||
import { ApplicationEvent, classNames, PrefKey } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { usePrevious } from '../ContentListView/Calendar/usePrevious'
|
||||
import ContentListView from '../ContentListView/ContentListView'
|
||||
import NoteGroupView from '../NoteGroupView/NoteGroupView'
|
||||
import PanelResizer, { PanelResizeType, PanelSide, ResizeFinishCallback } from '../PanelResizer/PanelResizer'
|
||||
import { AppPaneId, AppPaneIdToDivId } from './AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from './ResponsivePaneProvider'
|
||||
import Navigation from '../Tags/Navigation'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import {
|
||||
animatePaneEntranceTransitionFromOffscreenToTheRight,
|
||||
animatePaneExitTransitionOffscreenToTheRight,
|
||||
} from '@/Components/Panes/PaneAnimator'
|
||||
import { isPanesChangeLeafDismiss, isPanesChangePush } from '@/Controllers/PaneController/panesForLayout'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
|
||||
const NAVIGATION_PANEL_MIN_WIDTH = 48
|
||||
const ITEMS_PANEL_MIN_WIDTH = 200
|
||||
const PLACEHOLDER_NAVIGATION_PANEL_WIDTH = 220
|
||||
const PLACEHOLDER_NOTES_PANEL_WIDTH = 400
|
||||
|
||||
const PanesSystemComponent = () => {
|
||||
const application = useApplication()
|
||||
const isTabletOrMobileScreenWrapped = useIsTabletOrMobileScreen()
|
||||
const { isTabletOrMobile, isTablet, isMobile } = isTabletOrMobileScreenWrapped
|
||||
const previousIsTabletOrMobileWrapped = usePrevious(isTabletOrMobileScreenWrapped)
|
||||
|
||||
const paneController = useResponsiveAppPane()
|
||||
const previousPaneController = usePrevious(paneController)
|
||||
const [renderPanes, setRenderPanes] = useState<AppPaneId[]>([])
|
||||
const [panesPendingEntrance, setPanesPendingEntrance] = useState<AppPaneId[]>([])
|
||||
const [panesPendingExit, setPanesPendingExit] = useState<AppPaneId[]>([])
|
||||
|
||||
const viewControllerManager = application.getViewControllerManager()
|
||||
|
||||
const [navigationPanelWidth, setNavigationPanelWidth] = useState<number>(
|
||||
application.getPreference(PrefKey.TagsPanelWidth, PLACEHOLDER_NAVIGATION_PANEL_WIDTH),
|
||||
)
|
||||
const [navigationRef, setNavigationRef] = useState<HTMLDivElement | null>(null)
|
||||
|
||||
const [itemsPanelWidth, setItemsPanelWidth] = useState<number>(
|
||||
application.getPreference(PrefKey.NotesPanelWidth, PLACEHOLDER_NOTES_PANEL_WIDTH),
|
||||
)
|
||||
const [listRef, setListRef] = useState<HTMLDivElement | null>(null)
|
||||
|
||||
const showPanelResizers = !isTabletOrMobile
|
||||
|
||||
const [_editorRef, setEditorRef] = useState<HTMLDivElement | null>(null)
|
||||
|
||||
const animationsSupported = isMobile
|
||||
|
||||
useEffect(() => {
|
||||
if (!animationsSupported) {
|
||||
return
|
||||
}
|
||||
|
||||
const panes = paneController.panes
|
||||
const previousPanes = previousPaneController?.panes
|
||||
if (!previousPanes) {
|
||||
setPanesPendingEntrance([])
|
||||
return
|
||||
}
|
||||
|
||||
const isPush = isPanesChangePush(previousPanes, panes)
|
||||
if (isPush) {
|
||||
setPanesPendingEntrance([panes[panes.length - 1]])
|
||||
}
|
||||
}, [paneController.panes, previousPaneController?.panes, animationsSupported])
|
||||
|
||||
useEffect(() => {
|
||||
if (!animationsSupported) {
|
||||
return
|
||||
}
|
||||
|
||||
const panes = paneController.panes
|
||||
const previousPanes = previousPaneController?.panes
|
||||
if (!previousPanes) {
|
||||
setPanesPendingExit([])
|
||||
return
|
||||
}
|
||||
|
||||
const isExit = isPanesChangeLeafDismiss(previousPanes, panes)
|
||||
if (isExit) {
|
||||
setPanesPendingExit([previousPanes[previousPanes.length - 1]])
|
||||
}
|
||||
}, [paneController.panes, previousPaneController?.panes, animationsSupported])
|
||||
|
||||
useEffect(() => {
|
||||
setRenderPanes(paneController.panes)
|
||||
}, [paneController.panes])
|
||||
|
||||
useEffect(() => {
|
||||
if (!panesPendingEntrance || panesPendingEntrance?.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (panesPendingEntrance.length > 1) {
|
||||
console.warn('More than one pane pending entrance. This is not supported.')
|
||||
return
|
||||
}
|
||||
|
||||
void animatePaneEntranceTransitionFromOffscreenToTheRight(AppPaneIdToDivId[panesPendingEntrance[0]]).then(() => {
|
||||
setPanesPendingEntrance([])
|
||||
})
|
||||
}, [panesPendingEntrance])
|
||||
|
||||
useEffect(() => {
|
||||
if (!panesPendingExit || panesPendingExit?.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (panesPendingExit.length > 1) {
|
||||
console.warn('More than one pane pending exit. This is not supported.')
|
||||
return
|
||||
}
|
||||
|
||||
void animatePaneExitTransitionOffscreenToTheRight(AppPaneIdToDivId[panesPendingExit[0]]).then(() => {
|
||||
setPanesPendingExit([])
|
||||
})
|
||||
}, [panesPendingExit])
|
||||
|
||||
useEffect(() => {
|
||||
const removeObserver = application.addEventObserver(async () => {
|
||||
const width = application.getPreference(PrefKey.TagsPanelWidth, PLACEHOLDER_NAVIGATION_PANEL_WIDTH)
|
||||
setNavigationPanelWidth(width)
|
||||
}, ApplicationEvent.PreferencesChanged)
|
||||
|
||||
return () => {
|
||||
removeObserver()
|
||||
}
|
||||
}, [application])
|
||||
|
||||
const navigationPanelResizeWidthChangeCallback = useCallback((width: number) => {
|
||||
setNavigationPanelWidth(width)
|
||||
}, [])
|
||||
|
||||
const itemsPanelResizeWidthChangeCallback = useCallback((width: number) => {
|
||||
setItemsPanelWidth(width)
|
||||
}, [])
|
||||
|
||||
const handleInitialItemsListPanelWidthLoad = useCallback((width: number) => {
|
||||
setItemsPanelWidth(width)
|
||||
}, [])
|
||||
|
||||
const navigationPanelResizeFinishCallback: ResizeFinishCallback = useCallback(
|
||||
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
||||
application.publishPanelDidResizeEvent(PANEL_NAME_NAVIGATION, width, isCollapsed)
|
||||
},
|
||||
[application],
|
||||
)
|
||||
|
||||
const itemsPanelResizeFinishCallback: ResizeFinishCallback = useCallback(
|
||||
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
||||
application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, width, isCollapsed)
|
||||
},
|
||||
[application],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (isTablet && !previousIsTabletOrMobileWrapped?.isTablet) {
|
||||
if (paneController.selectedPane !== AppPaneId.Navigation) {
|
||||
paneController.removePane(AppPaneId.Navigation)
|
||||
}
|
||||
} else if (
|
||||
!isTablet &&
|
||||
previousIsTabletOrMobileWrapped?.isTablet &&
|
||||
!paneController.panes.includes(AppPaneId.Navigation)
|
||||
) {
|
||||
paneController.insertPaneAtIndex(AppPaneId.Navigation, 0)
|
||||
}
|
||||
}, [isTablet, paneController, previousIsTabletOrMobileWrapped])
|
||||
|
||||
const computeStylesForContainer = (): React.CSSProperties => {
|
||||
const panes = paneController.panes
|
||||
const numPanes = panes.length
|
||||
|
||||
if (isMobile) {
|
||||
return {}
|
||||
}
|
||||
|
||||
switch (numPanes) {
|
||||
case 1: {
|
||||
return {
|
||||
gridTemplateColumns: 'auto',
|
||||
}
|
||||
}
|
||||
case 2: {
|
||||
if (paneController.focusModeEnabled) {
|
||||
return {
|
||||
gridTemplateColumns: '0 1fr',
|
||||
}
|
||||
}
|
||||
if (isTablet) {
|
||||
return {
|
||||
gridTemplateColumns: '1fr 2fr',
|
||||
}
|
||||
} else {
|
||||
if (panes[0] === AppPaneId.Navigation) {
|
||||
return {
|
||||
gridTemplateColumns: `${navigationPanelWidth}px auto`,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
gridTemplateColumns: `${itemsPanelWidth}px auto`,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case 3: {
|
||||
if (paneController.focusModeEnabled) {
|
||||
return {
|
||||
gridTemplateColumns: '0 0 1fr',
|
||||
}
|
||||
}
|
||||
return {
|
||||
gridTemplateColumns: `${navigationPanelWidth}px ${itemsPanelWidth}px 2fr`,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const computeClassesForPane = (_paneId: AppPaneId, isPendingEntrance: boolean, index: number): string => {
|
||||
const common = `app-pane app-pane-${index + 1} h-full content`
|
||||
|
||||
if (isMobile) {
|
||||
return `absolute top-0 left-0 w-full flex flex-col ${common} ${
|
||||
isPendingEntrance ? 'translate-x-[100%]' : 'translate-x-0 '
|
||||
}`
|
||||
} else {
|
||||
return `flex flex-col relative overflow-hidden ${common}`
|
||||
}
|
||||
}
|
||||
|
||||
const computeClassesForContainer = (): string => {
|
||||
if (isMobile) {
|
||||
return 'w-full'
|
||||
}
|
||||
|
||||
return 'grid'
|
||||
}
|
||||
|
||||
const renderPanesWithPendingExit = [...renderPanes, ...panesPendingExit]
|
||||
|
||||
log(LoggingDomain.Panes, 'Rendering panes', renderPanesWithPendingExit)
|
||||
|
||||
return (
|
||||
<div id="app" className={`app ${computeClassesForContainer()}`} style={{ ...computeStylesForContainer() }}>
|
||||
{renderPanesWithPendingExit.map((pane, index) => {
|
||||
const isPendingEntrance = panesPendingEntrance?.includes(pane)
|
||||
|
||||
const className = computeClassesForPane(pane, isPendingEntrance ?? false, index)
|
||||
|
||||
if (pane === AppPaneId.Navigation) {
|
||||
return (
|
||||
<Navigation
|
||||
id={ElementIds.NavigationColumn}
|
||||
ref={setNavigationRef}
|
||||
className={classNames(className, isTabletOrMobile ? 'w-full' : '')}
|
||||
key="navigation-pane"
|
||||
application={application}
|
||||
>
|
||||
{showPanelResizers && navigationRef && (
|
||||
<PanelResizer
|
||||
collapsable={true}
|
||||
defaultWidth={navigationPanelWidth}
|
||||
hoverable={true}
|
||||
left={0}
|
||||
minWidth={NAVIGATION_PANEL_MIN_WIDTH}
|
||||
modifyElementWidth={false}
|
||||
panel={navigationRef}
|
||||
resizeFinishCallback={navigationPanelResizeFinishCallback}
|
||||
side={PanelSide.Right}
|
||||
type={PanelResizeType.WidthOnly}
|
||||
width={navigationPanelWidth}
|
||||
widthEventCallback={navigationPanelResizeWidthChangeCallback}
|
||||
/>
|
||||
)}
|
||||
</Navigation>
|
||||
)
|
||||
} else if (pane === AppPaneId.Items) {
|
||||
return (
|
||||
<ContentListView
|
||||
id={ElementIds.ItemsColumn}
|
||||
className={className}
|
||||
ref={setListRef}
|
||||
key={'content-list-view'}
|
||||
application={application}
|
||||
onPanelWidthLoad={handleInitialItemsListPanelWidthLoad}
|
||||
accountMenuController={viewControllerManager.accountMenuController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
itemListController={viewControllerManager.itemListController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
noAccountWarningController={viewControllerManager.noAccountWarningController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
searchOptionsController={viewControllerManager.searchOptionsController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
>
|
||||
{showPanelResizers && listRef && (
|
||||
<PanelResizer
|
||||
collapsable={true}
|
||||
defaultWidth={itemsPanelWidth}
|
||||
hoverable={true}
|
||||
left={0}
|
||||
minWidth={ITEMS_PANEL_MIN_WIDTH}
|
||||
modifyElementWidth={false}
|
||||
panel={listRef}
|
||||
resizeFinishCallback={itemsPanelResizeFinishCallback}
|
||||
side={PanelSide.Right}
|
||||
type={PanelResizeType.WidthOnly}
|
||||
width={itemsPanelWidth}
|
||||
widthEventCallback={itemsPanelResizeWidthChangeCallback}
|
||||
/>
|
||||
)}
|
||||
</ContentListView>
|
||||
)
|
||||
} else if (pane === AppPaneId.Editor) {
|
||||
return (
|
||||
<ErrorBoundary key="editor-pane">
|
||||
<NoteGroupView
|
||||
id={ElementIds.EditorColumn}
|
||||
innerRef={(ref) => setEditorRef(ref)}
|
||||
className={className}
|
||||
application={application}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(PanesSystemComponent)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user