Compare commits

...
Author SHA1 Message Date
StandardNotes CI 8755001f7e chore(release): publish
- @standardnotes/[email protected].9
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-11-23 02:16:46 +00:00
Mo 7c2e832065 feat: display file backup status in file context menu (#2044) (skip e2e)
* feat: show file backup status in context menu

* feat: show backup status in list cell

* feat: mapping cache

* feat: add to linking menu + date format

* fix: types
2022-11-22 19:58:48 -06:00
Aman Harwara 096d82f7af fix: lazy load embedded files in super editor (#2043) 2022-11-23 00:55:20 +05:30
Aman Harwara 8c8f045b9a fix: super editor popover menus (#2041) 2022-11-22 19:16:59 +05:30
StandardNotes CI b3140f1623 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected].1
2022-11-22 10:15:26 +00:00
Karol Sójko c5e104f90b fix(snjs): add handling errors on creating websocket connection 2022-11-22 10:47:54 +01:00
StandardNotes CI 09b8f00eed 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].0
2022-11-19 15:17:36 +00:00
Mo c39c72da7a feat: ability to drag super list items; secure password generation blocks (#2039)
* feat: ability to drag list item nodes

* fix: issue where editor focus would scroll to bottom

* fix: improve drag icon and prevent from interfering with selection

* fix(super): add 'current' as keyword for bringing up date block options

* fix(super): issue with autocomplete menu width on large screens

* feat(super): ability to generate secure random passwords
2022-11-19 08:53:30 -06:00
StandardNotes CI 7f28876047 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-11-19 03:21:08 +00:00
Mo 24ff87c306 fix: improved error reporting in super; improved draggable block icon padding 2022-11-18 20:54:03 -06:00
StandardNotes CI e8b758214d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-18 21:15:30 +00:00
Mo e58c563795 fix: issue where converting empty plain note to super would result in error 2022-11-18 14:53:18 -06:00
StandardNotes CI 328bbb797d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].0
2022-11-18 16:10:59 +00:00
Aman Harwara dab4f678f0 feat(web): enable block drag'n'drop in super editor (#2029) 2022-11-18 21:15:18 +05:30
StandardNotes CI c847cc1d16 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-11-18 15:30:47 +00:00
Mo f49ba6bd4d feat: keyboard shortcuts for primary actions (#2030) 2022-11-18 09:01:48 -06:00
StandardNotes CI 0309912f98 chore(release): publish
- @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-11-18 12:15:29 +00:00
Mo 0c253dc46a chore: make models package public for consumption by server 2022-11-18 05:51:08 -06:00
StandardNotes CI 4b081890fd chore(release): publish
- @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-18 04:18:24 +00:00
Mo 87f86693a6 fix(snjs): keep apply payload timestamp when using keep base conflict strategy (#2031) 2022-11-17 21:51:06 -06:00
StandardNotes CI bc12663c7f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-11-17 17:25:29 +00:00
Mo f793673875 feat: click saving indicator to toggle visibility (#2028) 2022-11-17 10:57:23 -06:00
StandardNotes CI dcc4353865 chore(release): publish
- @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
2022-11-17 15:59:11 +00:00
Mo b9e6dc4c2b fix: select first item when switching tags (#2026) 2022-11-17 09:35:45 -06:00
StandardNotes CI e6baf74d14 chore(release): publish
- @standardnotes/[email protected].6
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
2022-11-17 15:06:39 +00:00
Mo a6e57e30cf fix: distinguish client controlled features so that server expiration timestamps are ignored (#2022) 2022-11-17 08:42:37 -06:00
StandardNotes CI 33b25d4b7a chore(release): publish
- @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].6
2022-11-17 14:25:03 +00:00
Mo 55b6714794 fix: handle cleared data on firefox (#2021) 2022-11-17 07:59:33 -06:00
StandardNotes CI 54b1a14bcd chore(release): publish
- @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].5
2022-11-17 12:38:10 +00:00
Mo fd170cfd0e chore: add retry to e2e test 2022-11-17 06:13:50 -06:00
187 changed files with 2854 additions and 1058 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+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.20.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/api
## [1.20.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/api
## [1.20.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/api
## [1.20.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/api
## [1.20.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/api
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.20.5",
"version": "1.20.9",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,4 +1,4 @@
import { Invitation } from '@standardnotes/models'
import { type Invitation } from '@standardnotes/models'
import { SubscriptionInviteAcceptResponse } from '../../Response/Subscription/SubscriptionInviteAcceptResponse'
import { SubscriptionInviteCancelResponse } from '../../Response/Subscription/SubscriptionInviteCancelResponse'
@@ -1,5 +1,5 @@
import { ProtocolVersion, UserRequestType } from '@standardnotes/common'
import { RootKeyParamsInterface } from '@standardnotes/models'
import { type RootKeyParamsInterface } from '@standardnotes/models'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
@@ -1,4 +1,4 @@
import { RootKeyParamsInterface } from '@standardnotes/models'
import { type RootKeyParamsInterface } from '@standardnotes/models'
import { UserRequestType } from '@standardnotes/common'
import { ErrorMessage } from '../../Error/ErrorMessage'
@@ -1,5 +1,5 @@
import { UserRequestType, Uuid } from '@standardnotes/common'
import { RootKeyParamsInterface } from '@standardnotes/models'
import { type RootKeyParamsInterface } from '@standardnotes/models'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
@@ -1,4 +1,4 @@
import { Invitation } from '@standardnotes/models'
import { type Invitation } from '@standardnotes/models'
export type SubscriptionInviteListResponseBody = {
invitations: Array<Invitation>
@@ -1,4 +1,4 @@
import { Invitation } from '@standardnotes/models'
import { type Invitation } from '@standardnotes/models'
import { ApiVersion } from '../../Api'
import { HttpServiceInterface } from '../../Http'
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.7.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
### Features
* ability to drag super list items; secure password generation blocks ([#2039](https://github.com/standardnotes/app/issues/2039)) ([c39c72d](https://github.com/standardnotes/app/commit/c39c72da7a4fb85f4da9aa4e6f8e9f7ba4486a94))
## [1.6.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
### Bug Fixes
* improved error reporting in super; improved draggable block icon padding ([24ff87c](https://github.com/standardnotes/app/commit/24ff87c30625432393ffedeada14478ebb625292))
## [1.6.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Bug Fixes
* issue where converting empty plain note to super would result in error ([e58c563](https://github.com/standardnotes/app/commit/e58c5637951f928efcd8c8427f06d0b6c69b5e55))
# [1.6.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Features
* **web:** enable block drag'n'drop in super editor ([#2029](https://github.com/standardnotes/app/issues/2029)) ([dab4f67](https://github.com/standardnotes/app/commit/dab4f678f04aee2c7dfbe7f416e7fc34e8d870cf))
## [1.5.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/blocks-editor
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/blocks-editor",
"version": "1.5.3",
"version": "1.7.0",
"private": true,
"main": "./src/index.ts",
"scripts": {
@@ -31,8 +31,6 @@ import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugi
import {truncateString} from './Utils';
import {SuperEditorContentId} from './Constants';
const BlockDragEnabled = false;
type BlocksEditorProps = {
onChange: (value: string, preview: string) => void;
className?: string;
@@ -69,8 +67,14 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
});
previewText = truncateString(previewText, previewLength);
const stringifiedEditorState = JSON.stringify(editorState.toJSON());
onChange(stringifiedEditorState, previewText);
try {
const stringifiedEditorState = JSON.stringify(editorState.toJSON());
onChange(stringifiedEditorState, previewText);
} catch (error) {
window.alert(
`An invalid change was made inside the Super editor. Your change was not saved. Please report this error to the team: ${error}`,
);
}
});
},
[onChange, didIgnoreFirstChange],
@@ -94,7 +98,7 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
<div className="editor" ref={onRef}>
<ContentEditable
id={SuperEditorContentId}
className={`ContentEditable__root ${className}`}
className={`ContentEditable__root overflow-y-auto ${className}`}
spellCheck={spellcheck}
/>
</div>
@@ -130,11 +134,9 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
<>
<FloatingTextFormatToolbarPlugin anchorElem={floatingAnchorElem} />
<FloatingLinkEditorPlugin />
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
</>
)}
{floatingAnchorElem && BlockDragEnabled && (
<>{<DraggableBlockPlugin anchorElem={floatingAnchorElem} />}</>
)}
</>
);
};
@@ -5,7 +5,7 @@ import {BlockEditorNodes} from '../Lexical/Nodes/AllNodes';
import {Klass, LexicalNode} from 'lexical';
type BlocksEditorComposerProps = {
initialValue: string;
initialValue: string | undefined;
children: React.ReactNode;
nodes?: Array<Klass<LexicalNode>>;
readonly?: boolean;
@@ -1,19 +1,21 @@
.draggable-block-menu {
border-radius: 4px;
padding: 2px 1px;
padding: 3px 1px;
cursor: grab;
opacity: 0;
position: absolute;
left: 0;
top: 0;
will-change: transform;
transition: opacity 0.3s;
}
.draggable-block-menu .icon {
width: 16px;
height: 16px;
opacity: 0.3;
background-image: url(#{$blocks-editor-icons-path}/draggable-block-menu.svg);
width: 0.8rem;
height: 1.1rem;
opacity: 0.2;
padding-left: 4.75px;
padding-top: 2px;
}
.draggable-block-menu:active {
@@ -21,16 +23,17 @@
}
.draggable-block-menu:hover {
background-color: #efefef;
background-color: var(--sn-stylekit-contrast-background-color);
}
.draggable-block-target-line {
pointer-events: none;
background: deepskyblue;
height: 4px;
background: var(--sn-stylekit-info-color);
height: 0.25rem;
position: absolute;
left: 0;
top: 0;
opacity: 0;
will-change: transform;
will-change: transform, opacity;
transition: opacity 0.15s;
}
@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
*/
import {$createListNode, $isListNode} from '@lexical/list';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {eventFiles} from '@lexical/rich-text';
import {mergeRegister} from '@lexical/utils';
@@ -19,16 +20,17 @@ import {
} from 'lexical';
import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
import {createPortal} from 'react-dom';
import {BlockIcon} from '@standardnotes/icons';
import {isHTMLElement} from '../../Utils/guard';
import {Point} from '../../Utils/point';
import {Rect} from '../../Utils/rect';
import {ContainsPointReturn, Rect} from '../../Utils/rect';
const SPACE = 4;
const TARGET_LINE_HALF_HEIGHT = 2;
const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu';
const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
const TEXT_BOX_HORIZONTAL_PADDING = 28;
const TEXT_BOX_HORIZONTAL_PADDING = 24;
const Downward = 1;
const Upward = -1;
@@ -52,12 +54,56 @@ function getTopLevelNodeKeys(editor: LexicalEditor): string[] {
return root ? root.__children : [];
}
function elementContainingEventLocation(
anchorElem: HTMLElement,
element: HTMLElement,
event: MouseEvent,
): {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);
const rect = elementDomRect.generateNewRect({
bottom: elementDomRect.bottom + parseFloat(marginBottom),
left: anchorElementRect.left,
right: anchorElementRect.right,
top: elementDomRect.top - parseFloat(marginTop),
});
const children = Array.from(element.children);
const shouldRecurseIntoChildren = ['UL', 'OL', 'LI'].includes(
element.tagName,
);
if (shouldRecurseIntoChildren) {
for (const child of children) {
const isLeaf = child.children.length === 0;
if (isLeaf) {
continue;
}
const childResult = elementContainingEventLocation(
anchorElem,
child as HTMLElement,
event,
);
if (childResult.contains.result) {
return childResult;
}
}
}
return {contains: rect.contains(eventLocation), element: element};
}
function getBlockElement(
anchorElem: HTMLElement,
editor: LexicalEditor,
event: MouseEvent,
): HTMLElement | null {
const anchorElementRect = anchorElem.getBoundingClientRect();
const topLevelNodeKeys = getTopLevelNodeKeys(editor);
let blockElem: HTMLElement | null = null;
@@ -72,32 +118,22 @@ function getBlockElement(
if (elem === null) {
break;
}
const point = new Point(event.x, event.y);
const domRect = Rect.fromDOM(elem);
const {marginTop, marginBottom} = window.getComputedStyle(elem);
const {contains, element} = elementContainingEventLocation(
anchorElem,
elem,
event,
);
const rect = domRect.generateNewRect({
bottom: domRect.bottom + parseFloat(marginBottom),
left: anchorElementRect.left,
right: anchorElementRect.right,
top: domRect.top - parseFloat(marginTop),
});
const {
result,
reason: {isOnTopSide, isOnBottomSide},
} = rect.contains(point);
if (result) {
blockElem = elem;
if (contains.result) {
blockElem = element;
prevIndex = index;
break;
}
if (direction === Indeterminate) {
if (isOnTopSide) {
if (contains.reason.isOnTopSide) {
direction = Upward;
} else if (isOnBottomSide) {
} else if (contains.reason.isOnBottomSide) {
direction = Downward;
} else {
// stop search block element
@@ -123,7 +159,6 @@ function setMenuPosition(
) {
if (!targetElem) {
floatingElem.style.opacity = '0';
floatingElem.style.transform = 'translate(-10000px, -10000px)';
return;
}
@@ -185,13 +220,12 @@ function setTargetLine(
targetLineElem.style.width = `${
anchorWidth - (TEXT_BOX_HORIZONTAL_PADDING - SPACE) * 2
}px`;
targetLineElem.style.opacity = '.4';
targetLineElem.style.opacity = '.6';
}
function hideTargetLine(targetLineElem: HTMLElement | null) {
if (targetLineElem) {
targetLineElem.style.opacity = '0';
targetLineElem.style.transform = 'translate(-10000px, -10000px)';
}
}
@@ -283,18 +317,30 @@ function useDraggableBlockMenu(
return false;
}
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
if (!targetNode) {
return false;
}
if (targetNode === draggedNode) {
return true;
}
let nodeToInsert = draggedNode;
const targetParent = targetNode.getParent();
const sourceParent = draggedNode.getParent();
if ($isListNode(sourceParent) && !$isListNode(targetParent)) {
const newList = $createListNode(sourceParent.getListType());
newList.append(draggedNode);
nodeToInsert = newList;
}
const {top, height} = targetBlockElem.getBoundingClientRect();
const shouldInsertAfter = pageY - top > height / 2;
if (shouldInsertAfter) {
targetNode.insertAfter(draggedNode);
targetNode.insertAfter(nodeToInsert);
} else {
targetNode.insertBefore(draggedNode);
targetNode.insertBefore(nodeToInsert);
}
setDraggableBlockElem(null);
@@ -347,7 +393,9 @@ function useDraggableBlockMenu(
draggable={true}
onDragStart={onDragStart}
onDragEnd={onDragEnd}>
<div className={isEditable ? 'icon' : ''} />
<div className={isEditable ? 'icon' : ''}>
<BlockIcon className="text-text pointer-events-none" />
</div>
</div>
<div className="draggable-block-target-line" ref={targetLineRef} />
</>,
@@ -110,8 +110,8 @@ export function InsertTableDialog({
return (
<>
<TextInput label="No of rows" onChange={setRows} value={rows} />
<TextInput label="No of columns" onChange={setColumns} value={columns} />
<TextInput label="Number of rows" onChange={setRows} value={rows} />
<TextInput label="Number of columns" onChange={setColumns} value={columns} />
<DialogActions data-test-id="table-model-confirm-insert">
<Button onClick={onClick}>Confirm</Button>
</DialogActions>
@@ -13,13 +13,13 @@
padding-left: 15px;
padding-right: 15px;
border: 0px;
background-color: #eee;
border-radius: 5px;
background-color: var(--sn-stylekit-contrast-background-color);
cursor: pointer;
font-size: 14px;
}
.Button__root:hover {
background-color: #ddd;
background-color: var(--sn-stylekit-info-color);
color: var(--sn-stylekit-info-contrast-color);
}
.Button__small {
padding-top: 5px;
@@ -32,5 +32,5 @@
cursor: not-allowed;
}
.Button__disabled:hover {
background-color: #eee;
background-color: var(--sn-stylekit-secondary-background-color);
}
@@ -17,16 +17,17 @@
display: flex;
flex: 1;
color: #666;
margin-right: 20px;
}
.Input__input {
display: flex;
flex: 2;
border: 1px solid #999;
border: 1px solid var(--sn-stylekit-contrast-border-color);
background-color: var(--sn-stylekit-contrast-background-color);
padding-top: 7px;
padding-bottom: 7px;
padding-left: 10px;
padding-right: 10px;
font-size: 16px;
border-radius: 5px;
min-width: 0;
}
@@ -17,7 +17,7 @@
bottom: 0px;
left: 0px;
right: 0px;
background-color: rgba(40, 40, 40, 0.6);
background-color: rgba(0, 0, 0, 0.7);
flex-grow: 0px;
flex-shrink: 1px;
z-index: 100;
@@ -28,22 +28,23 @@
min-width: 300px;
display: flex;
flex-grow: 0px;
background-color: #fff;
background-color: var(--sn-stylekit-background-color);
flex-direction: column;
position: relative;
box-shadow: 0 0 20px 0 #444;
border-radius: 10px;
box-shadow: 0 0px 0 var(--sn-stylekit-shadow-color);
border-radius: 0px;
}
.Modal__title {
color: #444;
color:var(--sn-stylekit-foreground-color);
margin: 0px;
padding-bottom: 10px;
border-bottom: 1px solid #ccc;
padding-bottom: 15px;
border-bottom: 1px solid var(--sn-stylekit-border-color);
}
.Modal__closeButton {
border: 0px;
position: absolute;
right: 20px;
top: 15px;
border-radius: 20px;
justify-content: center;
align-items: center;
@@ -52,10 +53,11 @@
height: 30px;
text-align: center;
cursor: pointer;
background-color: #eee;
background-color: var(--sn-stylekit-contrast-background-color);
}
.Modal__closeButton:hover {
background-color: #ddd;
background-color: var(--sn-stylekit-info-color);
color: var(--sn-stylekit-info-contrast-color);
}
.Modal__content {
padding-top: 20px;
@@ -73,7 +73,7 @@ function PortalImpl({
aria-label="Close modal"
type="button"
onClick={onClose}>
X
</button>
<div className="Modal__content">{children}</div>
</div>
@@ -7,7 +7,7 @@
*/
import {isPoint, Point} from './point';
type ContainsPointReturn = {
export type ContainsPointReturn = {
result: boolean;
reason: {
isOnTopSide: boolean;
+58
View File
@@ -3,6 +3,64 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.101.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Features
* display file backup status in file context menu ([#2044](https://github.com/standardnotes/app/issues/2044)) (skip e2e) ([7c2e832](https://github.com/standardnotes/app/commit/7c2e832065d45676f1b69b7c386e789e2f76775e))
## [3.100.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.100.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/desktop
@@ -1,4 +1,4 @@
import { FileBackupsDevice, FileBackupsMapping } from '@web/Application/Device/DesktopSnjsExports'
import { FileBackupRecord, FileBackupsDevice, FileBackupsMapping } from '@web/Application/Device/DesktopSnjsExports'
import { AppState } from 'app/AppState'
import { shell } from 'electron'
import { StoreKeys } from '../Store/StoreKeys'
@@ -120,6 +120,10 @@ export class FilesBackupManager implements FileBackupsDevice {
return this.defaultMappingFileValue()
}
for (const entry of Object.values(data.files)) {
entry.backedUpOn = new Date(entry.backedUpOn)
}
return data
}
@@ -129,6 +133,10 @@ export class FilesBackupManager implements FileBackupsDevice {
void shell.openPath(location)
}
async openFileBackup(record: FileBackupRecord): Promise<void> {
void shell.openPath(record.absolutePath)
}
async saveFilesBackupsMappingFile(file: FileBackupsMapping): Promise<'success' | 'failed'> {
await writeJSONFile(this.getMappingFileLocation(), file)
@@ -5,7 +5,7 @@ import { StoreKeys } from '../Store/StoreKeys'
const path = require('path')
const rendererPath = path.join('file://', __dirname, '/renderer.js')
import { FileBackupsDevice, FileBackupsMapping } from '@web/Application/Device/DesktopSnjsExports'
import { FileBackupsDevice, FileBackupsMapping, FileBackupRecord } from '@web/Application/Device/DesktopSnjsExports'
import { app, BrowserWindow } from 'electron'
import { BackupsManagerInterface } from '../Backups/BackupsManagerInterface'
import { KeychainInterface } from '../Keychain/KeychainInterface'
@@ -64,6 +64,7 @@ export class RemoteBridge implements CrossProcessBridge {
changeFilesBackupsLocation: this.changeFilesBackupsLocation.bind(this),
getFilesBackupsLocation: this.getFilesBackupsLocation.bind(this),
openFilesBackupsLocation: this.openFilesBackupsLocation.bind(this),
openFileBackup: this.openFileBackup.bind(this),
}
}
@@ -202,4 +203,8 @@ export class RemoteBridge implements CrossProcessBridge {
public openFilesBackupsLocation(): Promise<void> {
return this.fileBackups.openFilesBackupsLocation()
}
public openFileBackup(record: FileBackupRecord): Promise<void> {
return this.fileBackups.openFileBackup(record)
}
}
@@ -3,6 +3,7 @@ import {
Environment,
FileBackupsMapping,
RawKeychainValue,
FileBackupRecord,
} from '@web/Application/Device/DesktopSnjsExports'
import { WebOrDesktopDevice } from '@web/Application/Device/WebOrDesktopDevice'
import { Component } from '../Main/Packages/PackageManagerInterface'
@@ -132,6 +133,10 @@ export class DesktopDevice extends WebOrDesktopDevice implements DesktopDeviceIn
return this.remoteBridge.openFilesBackupsLocation()
}
openFileBackup(record: FileBackupRecord): Promise<void> {
return this.remoteBridge.openFileBackup(record)
}
async saveFilesBackupsFile(
uuid: string,
metaFile: string,
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.100.5",
"version": "3.101.0",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+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.19.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.19.13",
"version": "1.19.17",
"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.55.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Bug Fixes
* distinguish client controlled features so that server expiration timestamps are ignored ([#2022](https://github.com/standardnotes/app/issues/2022)) ([a6e57e3](https://github.com/standardnotes/app/commit/a6e57e30cff64de83eaedf3d36117a497a7a70ae))
## [1.55.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.55.2",
"version": "1.55.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -23,6 +23,9 @@ export type BaseFeatureDescription = RoleFields & {
description?: string
expires_at?: number
/** Whether the client controls availability of this feature (such as the dark theme) */
clientControlled?: boolean
flags?: ComponentFlag[]
identifier: FeatureIdentifier
marketing_url?: string
+1 -13
View File
@@ -10,8 +10,6 @@ export function themes(): ThemeFeatureDescription[] {
name: 'Midnight',
identifier: FeatureIdentifier.MidnightTheme,
permission_name: PermissionName.MidnightTheme,
description: 'Elegant utilitarianism.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/midnight-with-mobile.jpg',
isDark: true,
dock_icon: {
type: 'circle',
@@ -26,8 +24,6 @@ export function themes(): ThemeFeatureDescription[] {
name: 'Futura',
identifier: FeatureIdentifier.FuturaTheme,
permission_name: PermissionName.FuturaTheme,
description: 'Calm and relaxed. Take some time off.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/futura-with-mobile.jpg',
isDark: true,
dock_icon: {
type: 'circle',
@@ -42,8 +38,6 @@ export function themes(): ThemeFeatureDescription[] {
name: 'Solarized Dark',
identifier: FeatureIdentifier.SolarizedDarkTheme,
permission_name: PermissionName.SolarizedDarkTheme,
description: 'The perfect theme for any time.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/solarized-dark.jpg',
isDark: true,
dock_icon: {
type: 'circle',
@@ -58,8 +52,6 @@ export function themes(): ThemeFeatureDescription[] {
name: 'Autobiography',
identifier: FeatureIdentifier.AutobiographyTheme,
permission_name: PermissionName.AutobiographyTheme,
description: 'A theme for writers and readers.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/autobiography.jpg',
dock_icon: {
type: 'circle',
background_color: '#9D7441',
@@ -73,8 +65,7 @@ export function themes(): ThemeFeatureDescription[] {
name: 'Dark',
identifier: FeatureIdentifier.DarkTheme,
permission_name: PermissionName.FocusedTheme,
description: 'For when you need to go in.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg',
clientControlled: true,
isDark: true,
dock_icon: {
type: 'circle',
@@ -89,8 +80,6 @@ export function themes(): ThemeFeatureDescription[] {
name: 'Titanium',
identifier: FeatureIdentifier.TitaniumTheme,
permission_name: PermissionName.TitaniumTheme,
description: 'Light on the eyes, heavy on the spirit.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/titanium-with-mobile.jpg',
dock_icon: {
type: 'circle',
background_color: '#6e2b9e',
@@ -106,7 +95,6 @@ export function themes(): ThemeFeatureDescription[] {
permission_name: PermissionName.ThemeDynamic,
layerable: true,
no_mobile: true,
description: 'A smart theme that minimizes the tags and notes panels when they are not in use.',
})
return [midnight, futura, solarizedDark, autobiography, dark, titanium, dynamic]
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.26.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Features
* display file backup status in file context menu ([#2044](https://github.com/standardnotes/app/issues/2044)) (skip e2e) ([7c2e832](https://github.com/standardnotes/app/commit/7c2e832065d45676f1b69b7c386e789e2f76775e))
## [1.25.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.25.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.25.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.25.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/filepicker
+4 -11
View File
@@ -1,24 +1,17 @@
{
"name": "@standardnotes/filepicker",
"version": "1.25.13",
"version": "1.26.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"description": "Web filepicker for Standard Notes projects",
"main": "dist/index.js",
"main": "./src/index.ts",
"author": "Standard Notes",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"types": "./src/index.ts",
"private": true,
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"prestart": "yarn clean",
"start": "tsc -p tsconfig.json --watch",
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"build": "echo 'Empty build script required for yarn topological install'",
"lint": "eslint src --ext .ts",
"test": "jest"
},
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "../../node_modules/@standardnotes/config/src/tsconfig.json",
"extends": "../../UILib.tsconfig.json",
"compilerOptions": {
"skipLibCheck": true,
"rootDir": "./src",
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Features
* display file backup status in file context menu ([#2044](https://github.com/standardnotes/app/issues/2044)) (skip e2e) ([7c2e832](https://github.com/standardnotes/app/commit/7c2e832065d45676f1b69b7c386e789e2f76775e))
## [1.12.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/files
## [1.12.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/files
## [1.12.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/files
## [1.12.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/files
+5 -9
View File
@@ -1,26 +1,22 @@
{
"name": "@standardnotes/files",
"version": "1.12.13",
"version": "1.13.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"description": "Client-side files library",
"main": "dist/index.js",
"main": "./src/index.ts",
"author": "Standard Notes",
"types": "dist/index.d.ts",
"types": "./src/index.ts",
"files": [
"dist"
],
"private": true,
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"prestart": "yarn clean",
"start": "tsc -p tsconfig.json --watch",
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint src --ext .ts",
"test": "jest"
"test": "jest",
"build": "echo 'Empty build script required for yarn topological install'"
},
"devDependencies": {
"@types/jest": "^29.2.3",
@@ -1,5 +1,5 @@
import { Uuid } from '@standardnotes/common'
import { FileBackupsMapping } from './FileBackupsMapping'
import { FileBackupRecord, FileBackupsMapping } from './FileBackupsMapping'
export interface FileBackupsDevice {
getFilesBackupsMappingFile(): Promise<FileBackupsMapping>
@@ -18,4 +18,5 @@ export interface FileBackupsDevice {
changeFilesBackupsLocation(): Promise<string | undefined>
getFilesBackupsLocation(): Promise<string>
openFilesBackupsLocation(): Promise<void>
openFileBackup(record: FileBackupRecord): Promise<void>
}
@@ -1,17 +1,16 @@
import { Uuid } from '@standardnotes/common'
import { FileBackupsConstantsV1 } from './FileBackupsConstantsV1'
export type FileBackupRecord = {
backedUpOn: Date
absolutePath: string
relativePath: string
metadataFileName: typeof FileBackupsConstantsV1.MetadataFileName
binaryFileName: typeof FileBackupsConstantsV1.BinaryFileName
version: typeof FileBackupsConstantsV1.Version
}
export interface FileBackupsMapping {
version: typeof FileBackupsConstantsV1.Version
files: Record<
Uuid,
{
backedUpOn: Date
absolutePath: string
relativePath: string
metadataFileName: typeof FileBackupsConstantsV1.MetadataFileName
binaryFileName: typeof FileBackupsConstantsV1.BinaryFileName
version: typeof FileBackupsConstantsV1.Version
}
>
files: Record<Uuid, FileBackupRecord>
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "../../node_modules/@standardnotes/config/src/tsconfig.json",
"extends": "../../UILib.tsconfig.json",
"compilerOptions": {
"skipLibCheck": true,
"rootDir": "./src",
+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.6.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
### Features
* ability to drag super list items; secure password generation blocks ([#2039](https://github.com/standardnotes/app/issues/2039)) ([c39c72d](https://github.com/standardnotes/app/commit/c39c72da7a4fb85f4da9aa4e6f8e9f7ba4486a94))
# [1.5.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Features
* **web:** enable block drag'n'drop in super editor ([#2029](https://github.com/standardnotes/app/issues/2029)) ([dab4f67](https://github.com/standardnotes/app/commit/dab4f678f04aee2c7dfbe7f416e7fc34e8d870cf))
## [1.4.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-16)
**Note:** Version bump only for package @standardnotes/icons
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/icons",
"version": "1.4.10",
"version": "1.6.0",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
+7
View File
@@ -0,0 +1,7 @@
<svg width="9px" height="14px" viewBox="0 0 9 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-2" fill="currentColor" fill-rule="nonzero">
<path d="M1.6666,3.33333 C1.2246,3.33333 0.8007,3.15774 0.4881,2.84518 C0.1755,2.53262 0,2.1087 0,1.66667 C0,1.22464 0.1755,0.80072 0.4881,0.48816 C0.8007,0.1756 1.2246,0 1.6666,0 C2.1086,0 2.5326,0.1756 2.8451,0.48816 C3.1577,0.80072 3.3333,1.22464 3.3333,1.66667 C3.3333,2.1087 3.1577,2.53262 2.8451,2.84518 C2.5326,3.15774 2.1086,3.33333 1.6666,3.33333 Z M1.6666,8.33333 C1.2246,8.33333 0.8007,8.15773 0.4881,7.84513 C0.1755,7.53263 0,7.10873 0,6.66663 C0,6.22463 0.1755,5.80073 0.4881,5.48813 C0.8007,5.17563 1.2246,5.00003 1.6666,5.00003 C2.1086,5.00003 2.5326,5.17563 2.8451,5.48813 C3.1577,5.80073 3.3333,6.22463 3.3333,6.66663 C3.3333,7.10873 3.1577,7.53263 2.8451,7.84513 C2.5326,8.15773 2.1086,8.33333 1.6666,8.33333 Z M1.6666,13.33333 C1.2246,13.33333 0.8007,13.15773 0.4881,12.84513 C0.1755,12.53263 0,12.10873 0,11.66663 C0,11.22463 0.1755,10.80073 0.4881,10.48813 C0.8007,10.17563 1.2246,10.00003 1.6666,10.00003 C2.1086,10.00003 2.5326,10.17563 2.8451,10.48813 C3.1577,10.80073 3.3333,11.22463 3.3333,11.66663 C3.3333,12.10873 3.1577,12.53263 2.8451,12.84513 C2.5326,13.15773 2.1086,13.33333 1.6666,13.33333 L1.6666,13.33333 Z M6.6666,3.33333 C6.2246,3.33333 5.8007,3.15774 5.4881,2.84518 C5.1755,2.53262 5,2.1087 5,1.66667 C5,1.22464 5.1755,0.80072 5.4881,0.48816 C5.8007,0.1756 6.2246,0 6.6666,0 C7.1086,0 7.5326,0.1756 7.8451,0.48816 C8.1577,0.80072 8.3333,1.22464 8.3333,1.66667 C8.3333,2.1087 8.1577,2.53262 7.8451,2.84518 C7.5326,3.15774 7.1086,3.33333 6.6666,3.33333 Z M6.6666,8.33333 C6.2246,8.33333 5.8007,8.15773 5.4881,7.84513 C5.1755,7.53263 5,7.10873 5,6.66663 C5,6.22463 5.1755,5.80073 5.4881,5.48813 C5.8007,5.17563 6.2246,5.00003 6.6666,5.00003 C7.1086,5.00003 7.5326,5.17563 7.8451,5.48813 C8.1577,5.80073 8.3333,6.22463 8.3333,6.66663 C8.3333,7.10873 8.1577,7.53263 7.8451,7.84513 C7.5326,8.15773 7.1086,8.33333 6.6666,8.33333 Z M6.6666,13.33333 C6.2246,13.33333 5.8007,13.15773 5.4881,12.84513 C5.1755,12.53263 5,12.10873 5,11.66663 C5,11.22463 5.1755,10.80073 5.4881,10.48813 C5.8007,10.17563 6.2246,10.00003 6.6666,10.00003 C7.1086,10.00003 7.5326,10.17563 7.8451,10.48813 C8.1577,10.80073 8.3333,11.22463 8.3333,11.66663 C8.3333,12.10873 8.1577,12.53263 7.8451,12.84513 C7.5326,13.15773 7.1086,13.33333 6.6666,13.33333 L6.6666,13.33333 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+2
View File
@@ -21,6 +21,7 @@ import AttachmentFileIcon from './ic-attachment-file.svg'
import AuthenticatorIcon from './ic-authenticator.svg'
import AuthenticatorVariantIcon from './ic-authenticator-variant.svg'
import BackIosIcon from './ic-back-ios.svg'
import BlockIcon from './ic-block.svg'
import BlueDotIcon from './blue-dot.svg'
import BoldIcon from './ic-bold.svg'
import BoxFilledIcon from './ic-box-filled.svg'
@@ -224,6 +225,7 @@ export {
AuthenticatorIcon,
AuthenticatorVariantIcon,
BackIosIcon,
BlockIcon,
BlueDotIcon,
BoldIcon,
BoxFilledIcon,
+2
View File
@@ -27,6 +27,7 @@ import TypeSubscript from './type-subscript.svg'
import TypeSuperscript from './type-superscript.svg'
import TypeUnderline from './type-underline.svg'
import LexicalPencilFill from './pencil-fill.svg'
import LexicalDraggableBlockMenu from './draggable-block-menu.svg'
export {
LexicalCaretRightFill,
@@ -58,4 +59,5 @@ export {
TypeSuperscript,
TypeUnderline,
LexicalPencilFill,
LexicalDraggableBlockMenu,
}
+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.46.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.46.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.46.15",
"version": "3.46.29",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/models
## [1.37.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/models
## [1.37.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Bug Fixes
* **snjs:** keep apply payload timestamp when using keep base conflict strategy ([#2031](https://github.com/standardnotes/app/issues/2031)) ([87f8669](https://github.com/standardnotes/app/commit/87f86693a67a36972f0e44a883b18a7129c36893))
## [1.37.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/models
## [1.37.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/models
+11 -4
View File
@@ -1,16 +1,23 @@
{
"name": "@standardnotes/models",
"version": "1.37.2",
"version": "1.37.6",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"private": true,
"publishConfig": {
"access": "public"
},
"description": "Models used in SNJS library",
"author": "Standard Notes",
"main": "./src/index.ts",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "AGPL-3.0-or-later",
"scripts": {
"tsc": "tsc -p tsconfig.json",
"clean": "rm -fr dist",
"prestart": "yarn clean",
"start": "tsc -p tsconfig.json --watch",
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint src --ext .ts",
"test": "jest"
},
@@ -99,4 +99,22 @@ describe('conflict delta', () => {
expect(delta.getConflictStrategy()).toBe(ConflictStrategy.KeepApply)
})
it('if keep base strategy, always use the apply payloads updated_at_timestamp', () => {
const basePayload = createDecryptedItemsKey('123', 'secret', 2)
const baseCollection = createBaseCollection(basePayload)
const applyPayload = createDecryptedItemsKey('123', 'other secret', 1)
const delta = new ConflictDelta(baseCollection, basePayload, applyPayload, historyMap)
expect(delta.getConflictStrategy()).toBe(ConflictStrategy.KeepBaseDuplicateApply)
const result = delta.result()
expect(result.emits).toHaveLength(1)
expect(result.emits[0].updated_at_timestamp).toEqual(applyPayload.updated_at_timestamp)
})
})
@@ -1,4 +1,4 @@
import { greaterOfTwoDates, uniqCombineObjArrays } from '@standardnotes/utils'
import { uniqCombineObjArrays } from '@standardnotes/utils'
import { ImmutablePayloadCollection } from '../Collection/Payload/ImmutablePayloadCollection'
import { CreateDecryptedItemFromPayload, CreateItemFromPayload } from '../../Utilities/Item/ItemGenerator'
import { HistoryMap, historyMapFunctions } from '../History/HistoryMap'
@@ -114,9 +114,9 @@ export class ConflictDelta {
}
private handleKeepBaseStrategy(): SyncResolvedPayload[] {
const updatedAt = greaterOfTwoDates(this.basePayload.serverUpdatedAt, this.applyPayload.serverUpdatedAt)
const updatedAt = this.applyPayload.serverUpdatedAt
const updatedAtTimestamp = Math.max(this.basePayload.updated_at_timestamp, this.applyPayload.updated_at_timestamp)
const updatedAtTimestamp = this.applyPayload.updated_at_timestamp
const leftPayload = this.basePayload.copyAsSyncResolved(
{
@@ -146,9 +146,9 @@ export class ConflictDelta {
}
private handleKeepBaseDuplicateApplyStrategy(): SyncResolvedPayload[] {
const updatedAt = greaterOfTwoDates(this.basePayload.serverUpdatedAt, this.applyPayload.serverUpdatedAt)
const updatedAt = this.applyPayload.serverUpdatedAt
const updatedAtTimestamp = Math.max(this.basePayload.updated_at_timestamp, this.applyPayload.updated_at_timestamp)
const updatedAtTimestamp = this.applyPayload.updated_at_timestamp
const leftPayload = this.basePayload.copyAsSyncResolved(
{
@@ -201,9 +201,9 @@ export class ConflictDelta {
'content_type',
])
const updatedAt = greaterOfTwoDates(this.basePayload.serverUpdatedAt, this.applyPayload.serverUpdatedAt)
const updatedAt = this.applyPayload.serverUpdatedAt
const updatedAtTimestamp = Math.max(this.basePayload.updated_at_timestamp, this.applyPayload.updated_at_timestamp)
const updatedAtTimestamp = this.applyPayload.updated_at_timestamp
const payload = this.basePayload.copyAsSyncResolved(
{
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "../../UILib.tsconfig.json",
"extends": "../../node_modules/@standardnotes/config/src/tsconfig.json",
"compilerOptions": {
"skipLibCheck": true,
"rootDir": "./src",
+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.
## [1.3.255](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.254](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.253](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.252](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.251](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.250](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.249](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.248](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.247](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.246](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.245](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.244](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.243](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.242](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.241](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.241",
"version": "1.3.255",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+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.12.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/responses
## [1.12.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/responses
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.12.8",
"version": "1.12.9",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.46.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Features
* display file backup status in file context menu ([#2044](https://github.com/standardnotes/app/issues/2044)) (skip e2e) ([7c2e832](https://github.com/standardnotes/app/commit/7c2e832065d45676f1b69b7c386e789e2f76775e))
## [1.45.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/services
## [1.45.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/services
## [1.45.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/services
## [1.45.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.45.3",
"version": "1.46.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -2,7 +2,13 @@ import { ContentType, Uuid } from '@standardnotes/common'
import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { PayloadEmitSource, FileItem, CreateEncryptedBackupFileContextPayload } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses'
import { FilesApiInterface, FileBackupMetadataFile, FileBackupsDevice, FileBackupsMapping } from '@standardnotes/files'
import {
FilesApiInterface,
FileBackupMetadataFile,
FileBackupsDevice,
FileBackupsMapping,
FileBackupRecord,
} from '@standardnotes/files'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { AbstractService } from '../Service/AbstractService'
@@ -11,6 +17,7 @@ import { StatusServiceInterface } from '../Status/StatusServiceInterface'
export class FilesBackupService extends AbstractService {
private itemsObserverDisposer: () => void
private pendingFiles = new Set<Uuid>()
private mappingCache?: FileBackupsMapping['files']
constructor(
private items: ItemManagerInterface,
@@ -75,8 +82,30 @@ export class FilesBackupService extends AbstractService {
return this.device.openFilesBackupsLocation()
}
private async getBackupsMapping(): Promise<FileBackupsMapping['files']> {
return (await this.device.getFilesBackupsMappingFile()).files
private async getBackupsMappingFromDisk(): Promise<FileBackupsMapping['files']> {
const result = (await this.device.getFilesBackupsMappingFile()).files
this.mappingCache = result
return result
}
private invalidateMappingCache(): void {
this.mappingCache = undefined
}
private async getBackupsMappingFromCache(): Promise<FileBackupsMapping['files']> {
return this.mappingCache ?? (await this.getBackupsMappingFromDisk())
}
public async getFileBackupInfo(file: FileItem): Promise<FileBackupRecord | undefined> {
const mapping = await this.getBackupsMappingFromCache()
const record = mapping[file.uuid]
return record
}
public async openFileBackup(record: FileBackupRecord): Promise<void> {
await this.device.openFileBackup(record)
}
private async handleChangedFiles(files: FileItem[]): Promise<void> {
@@ -88,7 +117,7 @@ export class FilesBackupService extends AbstractService {
return
}
const mapping = await this.getBackupsMapping()
const mapping = await this.getBackupsMappingFromDisk()
for (const file of files) {
if (this.pendingFiles.has(file.uuid)) {
@@ -105,6 +134,8 @@ export class FilesBackupService extends AbstractService {
this.pendingFiles.delete(file.uuid)
}
}
this.invalidateMappingCache()
}
private async performBackupOperation(file: FileItem): Promise<'success' | 'failed' | 'aborted'> {
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.151.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/snjs
## [2.151.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-22)
### Bug Fixes
* **snjs:** add handling errors on creating websocket connection ([c5e104f](https://github.com/standardnotes/app/commit/c5e104f90bcd0d494b9d4adc0b27f2f942dfe71c))
## [2.151.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/snjs
## [2.151.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Bug Fixes
* **snjs:** keep apply payload timestamp when using keep base conflict strategy ([#2031](https://github.com/standardnotes/app/issues/2031)) ([87f8669](https://github.com/standardnotes/app/commit/87f86693a67a36972f0e44a883b18a7129c36893))
## [2.151.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Bug Fixes
* distinguish client controlled features so that server expiration timestamps are ignored ([#2022](https://github.com/standardnotes/app/issues/2022)) ([a6e57e3](https://github.com/standardnotes/app/commit/a6e57e30cff64de83eaedf3d36117a497a7a70ae))
## [2.151.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Bug Fixes
* handle cleared data on firefox ([#2021](https://github.com/standardnotes/app/issues/2021)) ([55b6714](https://github.com/standardnotes/app/commit/55b671479451d8d7e291a9875e227abc917d30b5))
## [2.151.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/snjs
## [2.151.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/snjs
+29 -24
View File
@@ -1,5 +1,4 @@
import { AnyKeyParamsContent } from '@standardnotes/common'
import { SNLog } from '@Lib/Log'
import { EncryptedPayload, EncryptedTransferPayload, isErrorDecryptingPayload } from '@standardnotes/models'
import { PreviousSnjsVersion1_0_0, PreviousSnjsVersion2_0_0, SnjsVersion } from '../Version'
import { Migration } from '@Lib/Migrations/Migration'
@@ -14,7 +13,7 @@ import {
SessionStrings,
Challenge,
} from '@standardnotes/services'
import { isNullOrUndefined } from '@standardnotes/utils'
import { assert } from '@standardnotes/utils'
import { CreateReader } from './StorageReaders/Functions'
import { StorageReader } from './StorageReaders/Reader'
import { ContentTypeUsesRootKeyEncryption } from '@standardnotes/encryption'
@@ -77,7 +76,7 @@ export class BaseMigration extends Migration {
/** Coming from 2.0.0 (which did not store version) OR is brand new application */
const migrationKey = namespacedKey(this.services.identifier, LastMigrationTimeStampKey2_0_0)
const migrationValue = await this.services.deviceInterface.getRawStorageValue(migrationKey)
const is_2_0_0_application = !isNullOrUndefined(migrationValue)
const is_2_0_0_application = migrationValue != undefined
if (is_2_0_0_application) {
await this.services.deviceInterface.setRawStorageValue(storageKey, PreviousSnjsVersion2_0_0)
await this.services.deviceInterface.removeRawStorageValue(LastMigrationTimeStampKey2_0_0)
@@ -141,7 +140,7 @@ export class BaseMigration extends Migration {
}
const rawAccountParams = await this.reader.getAccountKeyParams()
const hasAccountKeyParams = !isNullOrUndefined(rawAccountParams)
const hasAccountKeyParams = rawAccountParams != undefined
if (!hasAccountKeyParams) {
/** Doesn't apply if account is not involved */
this.memoizedNeedsKeychainRepair = false
@@ -169,7 +168,30 @@ export class BaseMigration extends Migration {
const version = (await this.getStoredVersion()) as string
const rawAccountParams = await this.reader.getAccountKeyParams()
/** Challenge for account password */
/** Choose an item to decrypt against */
const allItems = (
await this.services.deviceInterface.getAllRawDatabasePayloads<EncryptedTransferPayload>(this.services.identifier)
).map((p) => new EncryptedPayload(p))
let itemToDecrypt = allItems.find((item) => {
return ContentTypeUsesRootKeyEncryption(item.content_type)
})
if (!itemToDecrypt) {
/** If no root key encrypted item, choose any item */
itemToDecrypt = allItems[0]
}
if (!itemToDecrypt) {
/**
* No items to decrypt, user probably cleared their browser data. Only choice is to clear storage
* as any remainign account data is useless without items
*/
await this.services.storageService.clearValues()
return
}
/** Prompt for account password */
const challenge = new Challenge(
[new ChallengePrompt(ChallengeValidation.None, undefined, SessionStrings.PasswordInputPlaceholder, true)],
ChallengeReason.Custom,
@@ -185,25 +207,8 @@ export class BaseMigration extends Migration {
const accountParams = this.services.protocolService.createKeyParams(rawAccountParams as AnyKeyParamsContent)
const rootKey = await this.services.protocolService.computeRootKey(password, accountParams)
/** Choose an item to decrypt */
const allItems = (
await this.services.deviceInterface.getAllRawDatabasePayloads<EncryptedTransferPayload>(
this.services.identifier,
)
).map((p) => new EncryptedPayload(p))
let itemToDecrypt = allItems.find((item) => {
return ContentTypeUsesRootKeyEncryption(item.content_type)
})
if (!itemToDecrypt) {
/** If no root key encrypted item, just choose any item */
itemToDecrypt = allItems[0]
}
if (!itemToDecrypt) {
throw SNLog.error(Error('Attempting keychain recovery validation but no items present.'))
}
/** TS can't detect we returned early above if itemToDecrypt is null */
assert(itemToDecrypt)
const decryptedPayload = await this.services.protocolService.decryptSplitSingle({
usesRootKey: {
@@ -70,6 +70,11 @@ export class SNWebSocketsService extends AbstractService<WebSocketsServiceEvent,
private async createWebSocketConnectionToken(): Promise<string | undefined> {
try {
const response = await this.webSocketApiService.createConnectionToken()
if (response.data.error) {
console.error(response.data.error)
return undefined
}
return response.data.token
} catch (error) {
@@ -479,16 +479,35 @@ describe('featuresService', () => {
const featuresService = createService()
const nativeFeature = featuresService['mapRemoteNativeFeatureToStaticFeature'](remoteFeature)
featuresService['mapNativeFeatureToItem'] = jest.fn()
featuresService['mapRemoteNativeFeatureToItem'] = jest.fn()
featuresService.initializeFromDisk()
await featuresService.updateRolesAndFetchFeatures('123', newRoles)
expect(featuresService['mapNativeFeatureToItem']).toHaveBeenCalledWith(
expect(featuresService['mapRemoteNativeFeatureToItem']).toHaveBeenCalledWith(
nativeFeature,
expect.anything(),
expect.anything(),
)
})
it('mapRemoteNativeFeatureToItem should throw if called with client controlled feature', async () => {
const clientFeature = {
identifier: FeatureIdentifier.DarkTheme,
content_type: ContentType.Theme,
clientControlled: true,
} as FeatureDescription
storageService.getValue = jest.fn().mockReturnValue(roles)
apiService.getUserFeatures = jest.fn().mockReturnValue({
data: {
features: [clientFeature],
},
})
const featuresService = createService()
featuresService.initializeFromDisk()
await expect(() => featuresService['mapRemoteNativeFeatureToItem'](clientFeature, [], [])).rejects.toThrow()
})
it('feature status', async () => {
const featuresService = createService()
@@ -649,6 +668,16 @@ describe('featuresService', () => {
expect(featuresService.getFeatureStatus(FeatureIdentifier.TokenVaultEditor)).toBe(FeatureStatus.NotInCurrentPlan)
})
it('didDownloadFeatures should filter out client controlled features', async () => {
const featuresService = createService()
featuresService['mapRemoteNativeFeaturesToItems'] = jest.fn()
await featuresService.didDownloadFeatures(GetFeatures().filter((f) => f.clientControlled))
expect(featuresService['mapRemoteNativeFeaturesToItems']).toHaveBeenCalledWith([])
})
it('feature status should be dynamic for subscriber if cached features and no successful features request made yet', async () => {
const featuresService = createService()
@@ -152,7 +152,7 @@ export class SNFeaturesService
await super.handleApplicationStage(stage)
if (stage === ApplicationStage.FullSyncCompleted_13) {
void this.addDarkTheme()
void this.mapClientControlledFeaturesToItems()
if (!this.rolesIncludePaidSubscription()) {
const offlineRepo = this.getOfflineRepo()
@@ -163,11 +163,32 @@ export class SNFeaturesService
}
}
private async addDarkTheme() {
const darkThemeFeature = FeaturesImports.FindNativeFeature(FeatureIdentifier.DarkTheme)
private async mapClientControlledFeaturesToItems() {
const clientFeatures = FeaturesImports.GetFeatures().filter((feature) => feature.clientControlled)
const currentItems = this.itemManager.getItems<Models.SNComponent>([ContentType.Component, ContentType.Theme])
if (darkThemeFeature) {
await this.mapRemoteNativeFeaturesToItems([darkThemeFeature])
for (const feature of clientFeatures) {
if (!feature.content_type) {
continue
}
const existingItem = currentItems.find((item) => item.identifier === feature.identifier)
if (existingItem) {
const hasChange = JSON.stringify(feature) !== JSON.stringify(existingItem.package_info)
if (hasChange) {
await this.itemManager.changeComponent(existingItem, (mutator) => {
mutator.package_info = feature
})
}
continue
}
await this.itemManager.createItem(
feature.content_type,
this.componentContentForNativeFeatureDescription(feature),
true,
)
}
}
@@ -396,7 +417,10 @@ export class SNFeaturesService
public async didDownloadFeatures(features: FeaturesImports.FeatureDescription[]): Promise<void> {
features = features
.filter((feature) => !!FeaturesImports.FindNativeFeature(feature.identifier))
.filter((feature) => {
const nativeFeature = FeaturesImports.FindNativeFeature(feature.identifier)
return nativeFeature != undefined && !nativeFeature.clientControlled
})
.map((feature) => this.mapRemoteNativeFeatureToStaticFeature(feature))
this.features = features
@@ -436,6 +460,7 @@ export class SNFeaturesService
if (nativeFeatureCopy.expires_at) {
nativeFeatureCopy.expires_at = convertTimestampToMilliseconds(nativeFeatureCopy.expires_at)
}
return nativeFeatureCopy
}
@@ -563,32 +588,41 @@ export class SNFeaturesService
let hasChanges = false
for (const feature of features) {
const didChange = await this.mapNativeFeatureToItem(feature, currentItems, itemsToDelete)
const didChange = await this.mapRemoteNativeFeatureToItem(feature, currentItems, itemsToDelete)
if (didChange) {
hasChanges = true
}
}
await this.itemManager.setItemsToBeDeleted(itemsToDelete)
if (hasChanges) {
void this.syncService.sync()
}
}
private async mapNativeFeatureToItem(
private async mapRemoteNativeFeatureToItem(
feature: FeaturesImports.FeatureDescription,
currentItems: Models.SNComponent[],
itemsToDelete: Models.SNComponent[],
): Promise<boolean> {
if (feature.clientControlled) {
throw new Error('Attempted to map client controlled feature as remote item')
}
if (!feature.content_type) {
return false
}
if (this.isExperimentalFeature(feature.identifier) && !this.isExperimentalFeatureEnabled(feature.identifier)) {
const isDisabledExperimentalFeature =
this.isExperimentalFeature(feature.identifier) && !this.isExperimentalFeatureEnabled(feature.identifier)
if (isDisabledExperimentalFeature) {
return false
}
let hasChanges = false
const now = new Date()
const expired = this.isFreeFeature(feature.identifier)
? false
@@ -599,6 +633,7 @@ export class SNFeaturesService
const itemIdentifier = item.content.package_info.identifier
return itemIdentifier === feature.identifier
}
return false
})
@@ -610,14 +645,17 @@ export class SNFeaturesService
if (existingItem) {
const featureExpiresAt = new Date(feature.expires_at || 0)
const hasChange =
JSON.stringify(feature) !== JSON.stringify(existingItem.package_info) ||
featureExpiresAt.getTime() !== existingItem.valid_until.getTime()
const hasChangeInPackageInfo = JSON.stringify(feature) !== JSON.stringify(existingItem.package_info)
const hasChangeInExpiration = featureExpiresAt.getTime() !== existingItem.valid_until.getTime()
const hasChange = hasChangeInPackageInfo || hasChangeInExpiration
if (hasChange) {
resultingItem = await this.itemManager.changeComponent(existingItem, (mutator) => {
mutator.package_info = feature
mutator.valid_until = featureExpiresAt
})
hasChanges = true
} else {
resultingItem = existingItem
+1
View File
@@ -77,6 +77,7 @@ describe('storage manager', function () {
})
it('ephemeral session should not persist data', async function () {
this.retries(2)
await Factory.registerUserToApplication({
application: this.application,
email: this.email,
@@ -885,6 +885,38 @@ describe('online conflict handling', function () {
await this.sharedFinalAssertions()
})
it('conflict where server updated_at_timestamp is less than base updated_at should not result in infinite loop', async function () {
/**
* While this shouldn't happen, I've seen this happen locally where a single UserPrefs object has a timestamp of A
* on the server, and A + 10 on the client side. Somehow the client had a newer timestamp than the server. The
* server rejects any change if the timestamp is not exactly equal. When we use the KeepBase strategy during conflict
* resolution, we keep the base item, but give it the timestamp of the server item, so that the server accepts it.
* However, RemoteDataConflict would only take the server's timestamp if it was greater than the base's timestamp.
* Because this was not the case, the client kept sending up its own base timestamp and the server kept rejecting it,
* and it never resolved. The fix made here was to take the server's timestamp no matter what, even if it is less than client's.
*/
const note = await Factory.createSyncedNote(this.application)
this.expectedItemCount++
/** First modify the item without saving so that our local contents digress from the server's */
await this.application.mutator.changeItem(note, (mutator) => {
mutator.title = `${Math.random()}`
})
const modified = note.payload.copy({
updated_at_timestamp: note.payload.updated_at_timestamp + 1,
content: {
...note.content,
title: Math.random(),
},
dirty: true,
})
this.expectedItemCount++
await this.application.itemManager.emitItemFromPayload(modified)
await this.application.sync.sync()
expect(this.application.itemManager.getDisplayableNotes().length).to.equal(2)
await this.sharedFinalAssertions()
})
it('conflicting should not over resolve', async function () {
/**
* Before refactoring to use dirtyIndex instead of dirtiedDate, sometimes an item could be dirtied
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.151.2",
"version": "2.151.9",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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.3.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/toast
## [1.3.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-16)
**Note:** Version bump only for package @standardnotes/toast
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/toast",
"version": "1.3.9",
"version": "1.3.11",
"private": true,
"main": "./src/index.ts",
"scripts": {
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.14.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.14.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.14.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/ui-services
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Features
* keyboard shortcuts for primary actions ([#2030](https://github.com/standardnotes/app/issues/2030)) ([f49ba6b](https://github.com/standardnotes/app/commit/f49ba6bd4d38e4026bd88630014126a6e0086351))
## [1.13.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.13.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.13.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Bug Fixes
* distinguish client controlled features so that server expiration timestamps are ignored ([#2022](https://github.com/standardnotes/app/issues/2022)) ([a6e57e3](https://github.com/standardnotes/app/commit/a6e57e30cff64de83eaedf3d36117a497a7a70ae))
## [1.13.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.13.15",
"version": "1.14.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
-212
View File
@@ -1,212 +0,0 @@
import { removeFromArray } from '@standardnotes/utils'
export enum KeyboardKey {
Tab = 'Tab',
Backspace = 'Backspace',
Up = 'ArrowUp',
Down = 'ArrowDown',
Left = 'ArrowLeft',
Right = 'ArrowRight',
Enter = 'Enter',
Escape = 'Escape',
Home = 'Home',
End = 'End',
}
export enum KeyboardModifier {
Shift = 'Shift',
Ctrl = 'Control',
/** ⌘ key on Mac, ⊞ key on Windows */
Meta = 'Meta',
Alt = 'Alt',
}
enum KeyboardKeyEvent {
Down = 'KeyEventDown',
Up = 'KeyEventUp',
}
type KeyboardObserver = {
key?: KeyboardKey | string
modifiers?: KeyboardModifier[]
onKeyDown?: (event: KeyboardEvent) => void
onKeyUp?: (event: KeyboardEvent) => void
element?: HTMLElement
elements?: HTMLElement[]
notElement?: HTMLElement
notElementIds?: string[]
notTags?: string[]
}
export class IOService {
readonly activeModifiers = new Set<KeyboardModifier>()
private observers: KeyboardObserver[] = []
constructor(private isMac: boolean) {
window.addEventListener('keydown', this.handleKeyDown)
window.addEventListener('keyup', this.handleKeyUp)
window.addEventListener('blur', this.handleWindowBlur)
}
public deinit() {
this.observers.length = 0
window.removeEventListener('keydown', this.handleKeyDown)
window.removeEventListener('keyup', this.handleKeyUp)
window.removeEventListener('blur', this.handleWindowBlur)
;(this.handleKeyDown as unknown) = undefined
;(this.handleKeyUp as unknown) = undefined
;(this.handleWindowBlur as unknown) = undefined
}
private addActiveModifier = (modifier: KeyboardModifier | undefined): void => {
if (!modifier) {
return
}
switch (modifier) {
case KeyboardModifier.Meta: {
if (this.isMac) {
this.activeModifiers.add(modifier)
}
break
}
case KeyboardModifier.Ctrl: {
if (!this.isMac) {
this.activeModifiers.add(modifier)
}
break
}
default: {
this.activeModifiers.add(modifier)
break
}
}
}
private removeActiveModifier = (modifier: KeyboardModifier | undefined): void => {
if (!modifier) {
return
}
this.activeModifiers.delete(modifier)
}
public cancelAllKeyboardModifiers = (): void => {
this.activeModifiers.clear()
}
public handleComponentKeyDown = (modifier: KeyboardModifier | undefined): void => {
this.addActiveModifier(modifier)
}
public handleComponentKeyUp = (modifier: KeyboardModifier | undefined): void => {
this.removeActiveModifier(modifier)
}
private handleKeyDown = (event: KeyboardEvent): void => {
this.updateAllModifiersFromEvent(event)
this.notifyObserver(event, KeyboardKeyEvent.Down)
}
private handleKeyUp = (event: KeyboardEvent): void => {
this.updateAllModifiersFromEvent(event)
this.notifyObserver(event, KeyboardKeyEvent.Up)
}
private updateAllModifiersFromEvent(event: KeyboardEvent): void {
for (const modifier of Object.values(KeyboardModifier)) {
if (event.getModifierState(modifier)) {
this.addActiveModifier(modifier)
} else {
this.removeActiveModifier(modifier)
}
}
}
handleWindowBlur = (): void => {
for (const modifier of this.activeModifiers) {
this.activeModifiers.delete(modifier)
}
}
modifiersForEvent(event: KeyboardEvent): KeyboardModifier[] {
const allModifiers = Object.values(KeyboardModifier)
const eventModifiers = allModifiers.filter((modifier) => {
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
const matches =
((event.ctrlKey || event.key === KeyboardModifier.Ctrl) && modifier === KeyboardModifier.Ctrl) ||
((event.metaKey || event.key === KeyboardModifier.Meta) && modifier === KeyboardModifier.Meta) ||
((event.altKey || event.key === KeyboardModifier.Alt) && modifier === KeyboardModifier.Alt) ||
((event.shiftKey || event.key === KeyboardModifier.Shift) && modifier === KeyboardModifier.Shift)
return matches
})
return eventModifiers
}
private eventMatchesKeyAndModifiers(
event: KeyboardEvent,
key: KeyboardKey | string | undefined,
modifiers: KeyboardModifier[] = [],
): boolean {
const eventModifiers = this.modifiersForEvent(event)
if (eventModifiers.length !== modifiers.length) {
return false
}
for (const modifier of modifiers) {
if (!eventModifiers.includes(modifier)) {
return false
}
}
// Modifers match, check key
if (!key) {
return true
}
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
// In our case we don't differentiate between the two.
return key.toLowerCase() === event.key.toLowerCase()
}
notifyObserver(event: KeyboardEvent, keyEvent: KeyboardKeyEvent): void {
const target = event.target as HTMLElement
for (const observer of this.observers) {
if (observer.element && event.target !== observer.element) {
continue
}
if (observer.elements && !observer.elements.includes(target)) {
continue
}
if (observer.notElement && observer.notElement === event.target) {
continue
}
if (observer.notElementIds && observer.notElementIds.includes(target.id)) {
continue
}
if (observer.notTags && observer.notTags.includes(target.tagName)) {
continue
}
if (this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
const callback = keyEvent === KeyboardKeyEvent.Down ? observer.onKeyDown : observer.onKeyUp
if (callback) {
callback(event)
}
}
}
}
addKeyObserver(observer: KeyboardObserver): () => void {
this.observers.push(observer)
const thislessObservers = this.observers
return () => {
removeFromArray(thislessObservers, observer)
}
}
}
@@ -0,0 +1,12 @@
import { KeyboardCommand } from './KeyboardCommands'
export type KeyboardCommandHandler = {
command: KeyboardCommand
onKeyDown?: (event: KeyboardEvent) => boolean | void
onKeyUp?: (event: KeyboardEvent) => boolean | void
element?: HTMLElement
elements?: HTMLElement[]
notElement?: HTMLElement
notElementIds?: string[]
notTags?: string[]
}
@@ -0,0 +1,26 @@
export type KeyboardCommand = symbol
function createKeyboardCommand(type: string): KeyboardCommand {
return Symbol(type)
}
export const TOGGLE_LIST_PANE_KEYBOARD_COMMAND = createKeyboardCommand('TOGGLE_LIST_PANE_KEYBOARD_COMMAND')
export const TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND = createKeyboardCommand('TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND')
export const CREATE_NEW_NOTE_KEYBOARD_COMMAND = createKeyboardCommand('CREATE_NEW_NOTE_KEYBOARD_COMMAND')
export const NEXT_LIST_ITEM_KEYBOARD_COMMAND = createKeyboardCommand('NEXT_LIST_ITEM_KEYBOARD_COMMAND')
export const PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND = createKeyboardCommand('PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND')
export const SEARCH_KEYBOARD_COMMAND = createKeyboardCommand('SEARCH_KEYBOARD_COMMAND')
export const CANCEL_SEARCH_COMMAND = createKeyboardCommand('CANCEL_SEARCH_COMMAND')
export const SELECT_ALL_ITEMS_KEYBOARD_COMMAND = createKeyboardCommand('SELECT_ALL_ITEMS_KEYBOARD_COMMAND')
export const SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND = createKeyboardCommand('SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND')
export const DELETE_NOTE_KEYBOARD_COMMAND = createKeyboardCommand('DELETE_NOTE_KEYBOARD_COMMAND')
export const TAB_COMMAND = createKeyboardCommand('PLAIN_EDITOR_INSERT_TAB_KEYBOARD_COMMAND')
export const ESCAPE_COMMAND = createKeyboardCommand('ESCAPE_COMMAND')
export const TOGGLE_FOCUS_MODE_COMMAND = createKeyboardCommand('TOGGLE_FOCUS_MODE_COMMAND')
export const CHANGE_EDITOR_COMMAND = createKeyboardCommand('CHANGE_EDITOR_COMMAND')
export const FOCUS_TAGS_INPUT_COMMAND = createKeyboardCommand('FOCUS_TAGS_INPUT_COMMAND')
export const CREATE_NEW_TAG_COMMAND = createKeyboardCommand('CREATE_NEW_TAG_COMMAND')
export const OPEN_NOTE_HISTORY_COMMAND = createKeyboardCommand('OPEN_NOTE_HISTORY_COMMAND')
export const CAPTURE_SAVE_COMMAND = createKeyboardCommand('CAPTURE_SAVE_COMMAND')
export const STAR_NOTE_COMMAND = createKeyboardCommand('STAR_NOTE_COMMAND')
export const PIN_NOTE_COMMAND = createKeyboardCommand('PIN_NOTE_COMMAND')
@@ -0,0 +1,12 @@
export enum KeyboardKey {
Tab = 'Tab',
Backspace = 'Backspace',
Up = 'ArrowUp',
Down = 'ArrowDown',
Left = 'ArrowLeft',
Right = 'ArrowRight',
Enter = 'Enter',
Escape = 'Escape',
Home = 'Home',
End = 'End',
}
@@ -0,0 +1,4 @@
export enum KeyboardKeyEvent {
Down = 'KeyEventDown',
Up = 'KeyEventUp',
}
@@ -0,0 +1,7 @@
export enum KeyboardModifier {
Shift = 'Shift',
Ctrl = 'Control',
/** ⌘ key on Mac, ⊞ key on Windows */
Meta = 'Meta',
Alt = 'Alt',
}
@@ -0,0 +1,203 @@
import { Environment, Platform } from '@standardnotes/snjs'
import { removeFromArray } from '@standardnotes/utils'
import { eventMatchesKeyAndModifiers } from './eventMatchesKeyAndModifiers'
import { KeyboardCommand } from './KeyboardCommands'
import { KeyboardKeyEvent } from './KeyboardKeyEvent'
import { KeyboardModifier } from './KeyboardModifier'
import { KeyboardCommandHandler } from './KeyboardCommandHandler'
import { KeyboardShortcut, PlatformedKeyboardShortcut } from './KeyboardShortcut'
import { getKeyboardShortcuts } from './getKeyboardShortcuts'
export class KeyboardService {
readonly activeModifiers = new Set<KeyboardModifier>()
private commandHandlers: KeyboardCommandHandler[] = []
private commandMap = new Map<KeyboardCommand, KeyboardShortcut>()
constructor(private platform: Platform, environment: Environment) {
window.addEventListener('keydown', this.handleKeyDown)
window.addEventListener('keyup', this.handleKeyUp)
window.addEventListener('blur', this.handleWindowBlur)
const shortcuts = getKeyboardShortcuts(platform, environment)
for (const shortcut of shortcuts) {
this.registerShortcut(shortcut)
}
}
get isMac() {
return this.platform === Platform.MacDesktop || this.platform === Platform.MacWeb
}
public deinit() {
this.commandHandlers.length = 0
window.removeEventListener('keydown', this.handleKeyDown)
window.removeEventListener('keyup', this.handleKeyUp)
window.removeEventListener('blur', this.handleWindowBlur)
;(this.handleKeyDown as unknown) = undefined
;(this.handleKeyUp as unknown) = undefined
;(this.handleWindowBlur as unknown) = undefined
}
private addActiveModifier = (modifier: KeyboardModifier | undefined): void => {
if (!modifier) {
return
}
switch (modifier) {
case KeyboardModifier.Meta: {
if (this.isMac) {
this.activeModifiers.add(modifier)
}
break
}
case KeyboardModifier.Ctrl: {
if (!this.isMac) {
this.activeModifiers.add(modifier)
}
break
}
default: {
this.activeModifiers.add(modifier)
break
}
}
}
private removeActiveModifier = (modifier: KeyboardModifier | undefined): void => {
if (!modifier) {
return
}
this.activeModifiers.delete(modifier)
}
public cancelAllKeyboardModifiers = (): void => {
this.activeModifiers.clear()
}
public handleComponentKeyDown = (modifier: KeyboardModifier | undefined): void => {
this.addActiveModifier(modifier)
}
public handleComponentKeyUp = (modifier: KeyboardModifier | undefined): void => {
this.removeActiveModifier(modifier)
}
private handleKeyDown = (event: KeyboardEvent): void => {
this.updateAllModifiersFromEvent(event)
this.handleKeyboardEvent(event, KeyboardKeyEvent.Down)
}
private handleKeyUp = (event: KeyboardEvent): void => {
this.updateAllModifiersFromEvent(event)
this.handleKeyboardEvent(event, KeyboardKeyEvent.Up)
}
private updateAllModifiersFromEvent(event: KeyboardEvent): void {
for (const modifier of Object.values(KeyboardModifier)) {
if (event.getModifierState(modifier)) {
this.addActiveModifier(modifier)
} else {
this.removeActiveModifier(modifier)
}
}
}
handleWindowBlur = (): void => {
for (const modifier of this.activeModifiers) {
this.activeModifiers.delete(modifier)
}
}
private handleKeyboardEvent(event: KeyboardEvent, keyEvent: KeyboardKeyEvent): void {
for (const command of this.commandMap.keys()) {
const shortcut = this.commandMap.get(command)
if (!shortcut) {
continue
}
if (eventMatchesKeyAndModifiers(event, shortcut)) {
if (shortcut.preventDefault) {
event.preventDefault()
}
this.handleCommand(command, event, keyEvent)
}
}
}
private handleCommand(command: KeyboardCommand, event: KeyboardEvent, keyEvent: KeyboardKeyEvent): void {
const target = event.target as HTMLElement
for (const observer of this.commandHandlers) {
if (observer.command !== command) {
continue
}
if (observer.element && event.target !== observer.element) {
continue
}
if (observer.elements && !observer.elements.includes(target)) {
continue
}
if (observer.notElement && observer.notElement === event.target) {
continue
}
if (observer.notElementIds && observer.notElementIds.includes(target.id)) {
continue
}
if (observer.notTags && observer.notTags.includes(target.tagName)) {
continue
}
const callback = keyEvent === KeyboardKeyEvent.Down ? observer.onKeyDown : observer.onKeyUp
if (callback) {
const exclusive = callback(event)
if (exclusive) {
return
}
}
}
}
registerShortcut(shortcut: KeyboardShortcut): void {
this.commandMap.set(shortcut.command, shortcut)
}
addCommandHandler(observer: KeyboardCommandHandler): () => void {
this.commandHandlers.push(observer)
const thislessObservers = this.commandHandlers
return () => {
observer.onKeyDown = undefined
observer.onKeyDown = undefined
removeFromArray(thislessObservers, observer)
}
}
addCommandHandlers(handlers: KeyboardCommandHandler[]): () => void {
const disposers = handlers.map((handler) => this.addCommandHandler(handler))
return () => {
for (const disposer of disposers) {
disposer()
}
}
}
keyboardShortcutForCommand(command: KeyboardCommand): PlatformedKeyboardShortcut | undefined {
const shortcut = this.commandMap.get(command)
if (!shortcut) {
return undefined
}
return {
platform: this.platform,
...shortcut,
}
}
}
@@ -0,0 +1,20 @@
import { Platform } from '@standardnotes/snjs'
import { KeyboardCommand } from './KeyboardCommands'
import { KeyboardKey } from './KeyboardKey'
import { KeyboardModifier } from './KeyboardModifier'
export type KeyboardShortcut = {
command: KeyboardCommand
modifiers?: KeyboardModifier[]
key?: KeyboardKey | string
/**
* Alternative to using key, if the key can be affected by alt + shift. For example, if you want alt + shift + n,
* use code 'KeyN' instead of key 'n', as the modifiers would turn n into '˜' on Mac.
*/
code?: string
preventDefault?: boolean
}
export type PlatformedKeyboardShortcut = KeyboardShortcut & {
platform: Platform
}
@@ -0,0 +1,27 @@
import { KeyboardShortcut } from './KeyboardShortcut'
import { modifiersForEvent } from './modifiersForEvent'
export function eventMatchesKeyAndModifiers(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {
const eventModifiers = modifiersForEvent(event)
const shortcutModifiers = shortcut.modifiers ?? []
if (eventModifiers.length !== shortcutModifiers.length) {
return false
}
for (const modifier of shortcutModifiers) {
if (!eventModifiers.includes(modifier)) {
return false
}
}
if (!shortcut.key && !shortcut.code) {
return true
}
if (shortcut.key) {
return shortcut.key.toLowerCase() === event.key.toLowerCase()
} else {
return shortcut.code === event.code
}
}
@@ -0,0 +1,136 @@
import { Environment, Platform } from '@standardnotes/snjs'
import { isMacPlatform } from './platformCheck'
import {
CREATE_NEW_NOTE_KEYBOARD_COMMAND,
TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
NEXT_LIST_ITEM_KEYBOARD_COMMAND,
PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
SEARCH_KEYBOARD_COMMAND,
SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND,
DELETE_NOTE_KEYBOARD_COMMAND,
TAB_COMMAND,
ESCAPE_COMMAND,
CANCEL_SEARCH_COMMAND,
TOGGLE_FOCUS_MODE_COMMAND,
CHANGE_EDITOR_COMMAND,
FOCUS_TAGS_INPUT_COMMAND,
CREATE_NEW_TAG_COMMAND,
OPEN_NOTE_HISTORY_COMMAND,
CAPTURE_SAVE_COMMAND,
STAR_NOTE_COMMAND,
PIN_NOTE_COMMAND,
} from './KeyboardCommands'
import { KeyboardKey } from './KeyboardKey'
import { KeyboardModifier } from './KeyboardModifier'
import { KeyboardShortcut } from './KeyboardShortcut'
export function getKeyboardShortcuts(platform: Platform, _environment: Environment): KeyboardShortcut[] {
const isMac = isMacPlatform(platform)
const primaryModifier = isMac ? KeyboardModifier.Meta : KeyboardModifier.Ctrl
return [
{
command: TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
key: 'l',
modifiers: [primaryModifier, KeyboardModifier.Shift],
},
{
command: TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
key: 'e',
modifiers: [primaryModifier, KeyboardModifier.Shift],
},
{
command: CREATE_NEW_NOTE_KEYBOARD_COMMAND,
code: 'KeyN',
modifiers: [KeyboardModifier.Alt, KeyboardModifier.Shift],
},
{
command: NEXT_LIST_ITEM_KEYBOARD_COMMAND,
key: KeyboardKey.Down,
},
{
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
key: KeyboardKey.Up,
},
{
command: SEARCH_KEYBOARD_COMMAND,
code: 'KeyF',
modifiers: [KeyboardModifier.Alt, KeyboardModifier.Shift],
},
{
command: CANCEL_SEARCH_COMMAND,
key: KeyboardKey.Escape,
},
{
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
key: 'a',
modifiers: [primaryModifier],
},
{
command: SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND,
modifiers: [KeyboardModifier.Alt],
},
{
command: DELETE_NOTE_KEYBOARD_COMMAND,
key: KeyboardKey.Backspace,
modifiers: [primaryModifier],
},
{
command: TAB_COMMAND,
key: KeyboardKey.Tab,
},
{
command: ESCAPE_COMMAND,
key: KeyboardKey.Escape,
},
{
command: TOGGLE_FOCUS_MODE_COMMAND,
key: 'f',
modifiers: [primaryModifier, KeyboardModifier.Shift],
},
{
command: CHANGE_EDITOR_COMMAND,
key: '/',
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
{
command: FOCUS_TAGS_INPUT_COMMAND,
code: 'KeyT',
modifiers: [primaryModifier, KeyboardModifier.Alt],
preventDefault: true,
},
{
command: CREATE_NEW_TAG_COMMAND,
code: 'KeyN',
modifiers: [primaryModifier, KeyboardModifier.Alt],
},
{
command: OPEN_NOTE_HISTORY_COMMAND,
key: 'h',
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
{
command: CAPTURE_SAVE_COMMAND,
key: 's',
modifiers: [primaryModifier],
preventDefault: true,
},
{
command: STAR_NOTE_COMMAND,
key: 's',
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
{
command: PIN_NOTE_COMMAND,
key: 'p',
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
]
}
@@ -0,0 +1,22 @@
import { Platform } from '@standardnotes/snjs'
import { KeyboardModifier } from './KeyboardModifier'
function isMacPlatform(platform: Platform) {
return platform === Platform.MacDesktop || platform === Platform.MacWeb
}
export function keyboardCharacterForModifier(modifier: KeyboardModifier, platform: Platform) {
const isMac = isMacPlatform(platform)
if (modifier === KeyboardModifier.Meta) {
return isMac ? '⌘' : '⊞'
} else if (modifier === KeyboardModifier.Ctrl) {
return isMac ? '⌃' : 'Ctrl'
} else if (modifier === KeyboardModifier.Alt) {
return isMac ? '⌥' : 'Alt'
} else if (modifier === KeyboardModifier.Shift) {
return isMac ? '⇧' : 'Shift'
} else {
return KeyboardModifier[modifier]
}
}
@@ -0,0 +1,29 @@
import { isMacPlatform } from '@standardnotes/ui-services'
import { keyboardCharacterForModifier } from './keyboardCharacterForModifier'
import { PlatformedKeyboardShortcut } from './KeyboardShortcut'
function stringForCode(code = ''): string {
return code.replace('Key', '').replace('Digit', '')
}
export function keyboardStringForShortcut(shortcut: PlatformedKeyboardShortcut | undefined) {
if (!shortcut) {
return ''
}
const key = shortcut.key?.toUpperCase() || stringForCode(shortcut.code)
if (!shortcut.modifiers || shortcut.modifiers.length === 0) {
return key
}
const modifierCharacters = shortcut.modifiers.map((modifier) =>
keyboardCharacterForModifier(modifier, shortcut.platform),
)
if (isMacPlatform(shortcut.platform)) {
return `${modifierCharacters.join('')}${key}`
} else {
return `${modifierCharacters.join('+')}+${key}`
}
}
@@ -0,0 +1,18 @@
import { KeyboardModifier } from './KeyboardModifier'
export function modifiersForEvent(event: KeyboardEvent): KeyboardModifier[] {
const allModifiers = Object.values(KeyboardModifier)
const eventModifiers = allModifiers.filter((modifier) => {
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
const matches =
((event.ctrlKey || event.key === KeyboardModifier.Ctrl) && modifier === KeyboardModifier.Ctrl) ||
((event.metaKey || event.key === KeyboardModifier.Meta) && modifier === KeyboardModifier.Meta) ||
((event.altKey || event.key === KeyboardModifier.Alt) && modifier === KeyboardModifier.Alt) ||
((event.shiftKey || event.key === KeyboardModifier.Shift) && modifier === KeyboardModifier.Shift)
return matches
})
return eventModifiers
}
@@ -0,0 +1,9 @@
import { Platform } from '@standardnotes/snjs'
export function isMacPlatform(platform: Platform) {
return platform === Platform.MacDesktop || platform === Platform.MacWeb
}
export function isWindowsPlatform(platform: Platform) {
return platform === Platform.WindowsDesktop || platform === Platform.WindowsWeb
}
+12 -9
View File
@@ -153,21 +153,24 @@ export class ThemeManager extends AbstractService {
private handleFeaturesUpdated(): void {
let hasChange = false
for (const themeUuid of this.activeThemes) {
const theme = this.application.items.findItem(themeUuid) as SNTheme
if (!theme) {
this.deactivateTheme(themeUuid)
hasChange = true
} else {
const status = this.application.features.getFeatureStatus(theme.identifier)
if (status !== FeatureStatus.Entitled) {
if (theme.active) {
this.application.mutator.toggleTheme(theme).catch(console.error)
} else {
this.deactivateTheme(theme.uuid)
}
hasChange = true
continue
}
const status = this.application.features.getFeatureStatus(theme.identifier)
if (status !== FeatureStatus.Entitled) {
if (theme.active) {
this.application.mutator.toggleTheme(theme).catch(console.error)
} else {
this.deactivateTheme(theme.uuid)
}
hasChange = true
}
}
+8 -1
View File
@@ -1,7 +1,14 @@
export * from './Alert/Functions'
export * from './Alert/WebAlertService'
export * from './Archive/ArchiveManager'
export * from './IO/IOService'
export * from './Keyboard/KeyboardService'
export * from './Keyboard/KeyboardShortcut'
export * from './Keyboard/KeyboardCommands'
export * from './Keyboard/platformCheck'
export * from './Keyboard/KeyboardKey'
export * from './Keyboard/KeyboardModifier'
export * from './Keyboard/keyboardCharacterForModifier'
export * from './Keyboard/keyboardStringForShortcut'
export * from './Preferences/PreferenceId'
export * from './Route/Params/DemoParams'
export * from './Route/Params/OnboardingParams'
+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.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Features
* display file backup status in file context menu ([#2044](https://github.com/standardnotes/app/issues/2044)) (skip e2e) ([7c2e832](https://github.com/standardnotes/app/commit/7c2e832065d45676f1b69b7c386e789e2f76775e))
## [1.11.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/utils
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/utils",
"version": "1.11.3",
"version": "1.12.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -41,6 +41,7 @@
"eslint-plugin-prettier": "*",
"jest": "^29.3.1",
"jsdom": "^20.0.2",
"ts-jest": "^29.0.3"
"ts-jest": "^29.0.3",
"typescript": "*"
}
}
+77
View File
@@ -3,6 +3,83 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.105.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Bug Fixes
* lazy load embedded files in super editor ([#2043](https://github.com/standardnotes/app/issues/2043)) ([096d82f](https://github.com/standardnotes/app/commit/096d82f7af73613a3d8bc994bec2ec10a92a527c))
* super editor popover menus ([#2041](https://github.com/standardnotes/app/issues/2041)) ([8c8f045](https://github.com/standardnotes/app/commit/8c8f045b9a53d910bcbf1dc5ee19cf92addf252e))
### Features
* display file backup status in file context menu ([#2044](https://github.com/standardnotes/app/issues/2044)) (skip e2e) ([7c2e832](https://github.com/standardnotes/app/commit/7c2e832065d45676f1b69b7c386e789e2f76775e))
## [3.104.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-22)
**Note:** Version bump only for package @standardnotes/web
# [3.104.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
### Features
* ability to drag super list items; secure password generation blocks ([#2039](https://github.com/standardnotes/app/issues/2039)) ([c39c72d](https://github.com/standardnotes/app/commit/c39c72da7a4fb85f4da9aa4e6f8e9f7ba4486a94))
## [3.103.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
### Bug Fixes
* improved error reporting in super; improved draggable block icon padding ([24ff87c](https://github.com/standardnotes/app/commit/24ff87c30625432393ffedeada14478ebb625292))
## [3.103.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Bug Fixes
* issue where converting empty plain note to super would result in error ([e58c563](https://github.com/standardnotes/app/commit/e58c5637951f928efcd8c8427f06d0b6c69b5e55))
# [3.103.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Features
* **web:** enable block drag'n'drop in super editor ([#2029](https://github.com/standardnotes/app/issues/2029)) ([dab4f67](https://github.com/standardnotes/app/commit/dab4f678f04aee2c7dfbe7f416e7fc34e8d870cf))
# [3.102.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
### Features
* keyboard shortcuts for primary actions ([#2030](https://github.com/standardnotes/app/issues/2030)) ([f49ba6b](https://github.com/standardnotes/app/commit/f49ba6bd4d38e4026bd88630014126a6e0086351))
## [3.101.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/web
## [3.101.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-18)
**Note:** Version bump only for package @standardnotes/web
# [3.101.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Features
* click saving indicator to toggle visibility ([#2028](https://github.com/standardnotes/app/issues/2028)) ([f793673](https://github.com/standardnotes/app/commit/f7936738759d4c2aa9d5a4a370649a20d758d3f9))
## [3.100.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Bug Fixes
* select first item when switching tags ([#2026](https://github.com/standardnotes/app/issues/2026)) ([b9e6dc4](https://github.com/standardnotes/app/commit/b9e6dc4c2beafed0562fa41f6cb05c987fdc2e08))
## [3.100.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/web
## [3.100.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/web
## [3.100.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/web
## [3.100.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/web
+1 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.100.4",
"version": "3.105.0",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -99,9 +99,6 @@
"prettier-plugin-tailwindcss": "^0.1.13",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-refresh": "^0.14.0",
"sass-loader": "*",
@@ -24,7 +24,7 @@
--sn-stylekit-accessory-tint-color-1: #941648;
--sn-stylekit-shadow-color: var(--background-2);
--sn-stylekit-shadow-color: #c4b89f;
--sn-stylekit-background-color: var(--background-1);
--sn-stylekit-foreground-color: var(--foreground-color);
--sn-stylekit-border-color: var(--border-color);
@@ -29,7 +29,8 @@ import { DesktopManager } from './Device/DesktopManager'
import {
ArchiveManager,
AutolockService,
IOService,
KeyboardService,
PreferenceId,
RouteService,
RouteServiceInterface,
ThemeManager,
@@ -85,19 +86,17 @@ export class WebApplication extends SNApplication implements WebApplicationInter
this.itemControllerGroup = new ItemGroupController(this)
this.routeService = new RouteService(this, internalEventBus)
const viewControllerManager = new ViewControllerManager(this, deviceInterface)
const archiveService = new ArchiveManager(this)
const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop)
const themeService = new ThemeManager(this, internalEventBus)
this.setWebServices({
viewControllerManager,
archiveService,
desktopService: isDesktopDevice(deviceInterface) ? new DesktopManager(this, deviceInterface) : undefined,
io,
autolockService: this.isNativeMobileWeb() ? undefined : new AutolockService(this, internalEventBus),
themeService,
})
this.webServices = {} as WebServices
this.webServices.keyboardService = new KeyboardService(platform, this.environment)
this.webServices.archiveService = new ArchiveManager(this)
this.webServices.themeService = new ThemeManager(this, internalEventBus)
this.webServices.autolockService = this.isNativeMobileWeb()
? undefined
: new AutolockService(this, internalEventBus)
this.webServices.desktopService = isDesktopDevice(deviceInterface)
? new DesktopManager(this, deviceInterface)
: undefined
this.webServices.viewControllerManager = new ViewControllerManager(this, deviceInterface)
if (this.isNativeMobileWeb()) {
this.mobileWebReceiver = new MobileWebReceiver(this)
@@ -152,10 +151,6 @@ export class WebApplication extends SNApplication implements WebApplicationInter
}
}
setWebServices(services: WebServices): void {
this.webServices = services
}
public addWebEventObserver(observer: WebEventObserver): () => void {
this.webEventObservers.push(observer)
@@ -194,6 +189,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return this.webServices.archiveService
}
public get paneController() {
return this.webServices.viewControllerManager.paneController
}
public get desktopDevice(): DesktopDeviceInterface | undefined {
if (isDesktopDevice(this.deviceInterface)) {
return this.deviceInterface
@@ -221,8 +220,8 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return this.webServices.themeService
}
public get io() {
return this.webServices.io
public get keyboardService() {
return this.webServices.keyboardService
}
async checkForSecurityUpdate() {
@@ -389,4 +388,11 @@ export class WebApplication extends SNApplication implements WebApplicationInter
FeatureIdentifier.PlainEditor
)
}
openPreferences(pane?: PreferenceId): void {
this.getViewControllerManager().preferencesController.openPreferences()
if (pane) {
this.getViewControllerManager().preferencesController.setCurrentPane(pane)
}
}
}

Some files were not shown because too many files have changed in this diff Show More