Compare commits

...
Author SHA1 Message Date
StandardNotes CI e07fed267f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
2022-12-01 13:53:01 +00:00
Mo 1726302615 fix: fixes issue where Dynamic Theme did not properly work with new grid system 2022-12-01 07:21:30 -06:00
Mo 73ca12275f chore: fix date tests 2022-12-01 07:05:02 -06:00
Karol Sójko 1ee6be4e5c Revert "chore(temp): e2e tests run flag"
This reverts commit 0e95b451d6.
2022-12-01 10:47:35 +01:00
StandardNotes CI 42fc1d073d chore(release): publish
- @standardnotes/[email protected].1
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-30 21:26:13 +00:00
Aman Harwara c32541c6e1 fix: Embedded image aspect ratio in Super notes (#2075) 2022-12-01 02:41:35 +05:30
StandardNotes CI 34d9100af6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-11-30 20:53:42 +00:00
Mo 7f2074a6ec feat: screen presentation and dismiss animations for mobile (#2073) 2022-11-30 14:37:36 -06:00
Mo 0e95b451d6 chore(temp): e2e tests run flag 2022-11-30 14:32:05 -06:00
Aman Harwara 0e886bf0f1 feat: Move editor apperance preferences to "Appearance" tab 2022-12-01 00:09:55 +05:30
Aman Harwara 9b3f11d85b fix: Disable all-caps for H2/H3 headings in Super notes 2022-11-30 23:30:57 +05:30
Aman Harwara 349fd7204e fix: Super note block drag-n-drop on mobile (#2072) 2022-11-30 22:20:46 +05:30
StandardNotes CI afa9aafa3e chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-11-30 13:17:55 +00:00
Aman Harwara b57786eeeb Merge branch 'main' of github.com:standardnotes/app 2022-11-30 18:22:51 +05:30
Aman Harwara f30d0898ec fix: "Prevent editing" option not working correctly for Super notes 2022-11-30 18:22:41 +05:30
StandardNotes CI 1729bc4292 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-11-30 11:39:49 +00:00
Aman Harwara abe2b17cb4 Merge branch 'main' of github.com:standardnotes/app 2022-11-30 16:44:08 +05:30
Aman Harwara d51de34daa fix: Render super note content in revision history modal 2022-11-30 16:43:57 +05:30
StandardNotes CI ec0f0a2b18 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-29 16:54:01 +00:00
Aman Harwara a417e5ba8d fix: Make super note styling consistent with plaintext notes (#2068) 2022-11-29 21:57:23 +05:30
StandardNotes CI 02eb969e07 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].0
2022-11-28 22:08:20 +00:00
Mo 767d354780 feat: display number of files for 'Files' view (#2065)
* feat: display number of files for 'Files' view

* feat: include files count in Preferences > Security
2022-11-28 15:38:50 -06:00
StandardNotes CI 052e7d2f90 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-11-28 19:07:10 +00:00
Aman Harwara e3ec8503a5 refactor: use ClipboardEvent interface for file paste command 2022-11-29 00:12:10 +05:30
Mo cc2762a29d feat: convert Super notes to Markdown behind the scenes while publishing to Listed (#2064) 2022-11-28 12:19:53 -06:00
Aman Harwara 96e8dfdd31 fix: Only autofocus super notes when creating new note (#2063) 2022-11-28 23:38:45 +05:30
StandardNotes CI 60cbd7aed2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].2
2022-11-28 17:43:50 +00:00
Mo 2b0c9b188c fix: exclude files from being counted in 'Notes' view (#2062) 2022-11-28 11:11:55 -06:00
StandardNotes CI 4181a4cca0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].1
2022-11-28 16:29:28 +00:00
Mo d51ab36bbf refactor: hide keyboard shortcuts if on mobile 2022-11-28 10:00:35 -06:00
StandardNotes CI 81774d74c6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-11-28 15:40:46 +00:00
Aman Harwara 148aee441b fix: Super editor popover menus on iOS (#2061) 2022-11-28 20:36:10 +05:30
Aman Harwara db3069eab3 feat: Paste files in Super notes to upload and embed them (#2060) 2022-11-28 20:17:21 +05:30
StandardNotes CI c045eb1459 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
2022-11-28 10:17:02 +00:00
Aman Harwara e19184c2da Merge branch 'main' of github.com:standardnotes/app 2022-11-28 15:15:18 +05:30
Aman Harwara 7f6c53dfcb fix: add new line after embedding files
fix: sync icon direction
2022-11-28 15:15:06 +05:30
StandardNotes CI 1c9ad49aea chore(release): publish
- @standardnotes/[email protected].9
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-28 08:55:36 +00:00
Aman Harwara 6fc1bb5e84 fix: navigation footer padding 2022-11-28 13:59:41 +05:30
154 changed files with 2560 additions and 1192 deletions
+2 -1
View File
@@ -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
+12
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.20.11",
"version": "1.20.14",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+31
View File
@@ -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 -1
View File
@@ -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;
+54
View File
@@ -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 -1
View File
@@ -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,
+12
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.19.19",
"version": "1.19.22",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+12
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.26.2",
"version": "1.26.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+12
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.13.2",
"version": "1.13.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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

+56
View File
@@ -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 -1
View File
@@ -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",
+16
View File
@@ -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 -1
View File
@@ -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) {
+52
View File
@@ -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 -1
View File
@@ -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",
+14
View File
@@ -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 -1
View File
@@ -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
+22
View File
@@ -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
+5 -1
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.152.5",
"version": "2.154.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/toast",
"version": "1.3.11",
"version": "1.3.12",
"private": true,
"main": "./src/index.ts",
"scripts": {
+20
View File
@@ -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 -1
View File
@@ -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
}
+4
View File
@@ -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 -1
View File
@@ -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
}
+4 -3
View File
@@ -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'
+83
View File
@@ -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
+186
View File
@@ -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)",
+3 -4
View File
@@ -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;
+3 -1
View File
@@ -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)
@@ -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,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'
@@ -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">
@@ -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
@@ -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,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'
@@ -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()
@@ -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
@@ -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'
@@ -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'
@@ -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