mirror of
https://github.com/standardnotes/app
synced 2026-09-14 03:46:01 -04:00
Compare commits
39
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5a866a62a | ||
|
|
6090962b2c | ||
|
|
a9445dfcca | ||
|
|
b3fe769a60 | ||
|
|
fc6d28d166 | ||
|
|
03eac4353d | ||
|
|
58d84c47c1 | ||
|
|
577318e208 | ||
|
|
b8da877804 | ||
|
|
17f4d20fb5 | ||
|
|
4f07b915f0 | ||
|
|
f2f61b18e2 | ||
|
|
9fb506e532 | ||
|
|
3b64acc290 | ||
|
|
e6824a3414 | ||
|
|
08985bb5c8 | ||
|
|
77cb6e7891 | ||
|
|
7e7c5e14ab | ||
|
|
631be2e494 | ||
|
|
fe36009482 | ||
|
|
e77e852801 | ||
|
|
c59d824240 | ||
|
|
2f3bc9b85b | ||
|
|
1ec173dcd6 | ||
|
|
35b00737bf | ||
|
|
f3e147f4c9 | ||
|
|
63fdb328cb | ||
|
|
94c280256e | ||
|
|
cf0dc19f63 | ||
|
|
2696b07759 | ||
|
|
2cc5afb00a | ||
|
|
1828f80ba5 | ||
|
|
9c260c02f1 | ||
|
|
e4b3028d67 | ||
|
|
eceb46bed7 | ||
|
|
b9e0d0d154 | ||
|
|
a7add80633 | ||
|
|
0941183c8b | ||
|
|
c94035c1d6 |
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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.20.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** handling expired token responses ([#2113](https://github.com/standardnotes/app/issues/2113)) ([2696b07](https://github.com/standardnotes/app/commit/2696b07759104fb1d40781897f69e63133ad824a))
|
||||
|
||||
## [1.20.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.20.21",
|
||||
"version": "1.20.22",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -115,7 +115,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
return false
|
||||
}
|
||||
|
||||
const response = (await this.post(joinPaths(this.host, Paths.v1.refreshSession), {
|
||||
const response = (await this.post(Paths.v1.refreshSession, {
|
||||
access_token: this.session.accessToken.value,
|
||||
refresh_token: this.session.refreshToken.value,
|
||||
})) as SessionRefreshResponse
|
||||
@@ -265,13 +265,12 @@ export class HttpService implements HttpServiceInterface {
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
if (httpStatus >= HttpStatusCode.Success && httpStatus < HttpStatusCode.MultipleChoices) {
|
||||
resolve(response)
|
||||
} else {
|
||||
if (httpStatus >= HttpStatusCode.Success && httpStatus < HttpStatusCode.InternalServerError) {
|
||||
if (httpStatus === HttpStatusCode.Forbidden && response.data && response.data.error !== undefined) {
|
||||
;(response.data as HttpErrorResponseBody).error.message = ErrorMessage.RateLimited
|
||||
}
|
||||
|
||||
resolve(response)
|
||||
} else {
|
||||
reject(response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ export enum HttpStatusCode {
|
||||
Forbidden = 403,
|
||||
Gone = 410,
|
||||
ExpiredAccessToken = 498,
|
||||
InternalServerError = 500,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/blocks-editor
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
### Features
|
||||
|
||||
* Persist collapsible block state in Super notes ([#2119](https://github.com/standardnotes/app/issues/2119)) ([577318e](https://github.com/standardnotes/app/commit/577318e208367dc52caf2d76fd87419eaead7ed4))
|
||||
|
||||
## [1.10.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/blocks-editor
|
||||
|
||||
## [1.10.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/blocks-editor
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "@standardnotes/blocks-editor",
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.1",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"tsc": "tsc -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lexical/react": "^0.6.5",
|
||||
"@lexical/react": "0.7.5",
|
||||
"@standardnotes/icons": "workspace:*",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"lexical": "^0.6.5",
|
||||
"lexical": "0.7.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@ import DraggableBlockPlugin from '../Lexical/Plugins/DraggableBlockPlugin';
|
||||
import CodeHighlightPlugin from '../Lexical/Plugins/CodeHighlightPlugin';
|
||||
import FloatingTextFormatToolbarPlugin from '../Lexical/Plugins/FloatingTextFormatToolbarPlugin';
|
||||
import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugin';
|
||||
import {TabIndentationPlugin} from '../Lexical/Plugins/TabIndentationPlugin';
|
||||
import {truncateString} from './Utils';
|
||||
import {SuperEditorContentId} from './Constants';
|
||||
import {classNames} from '@standardnotes/utils';
|
||||
@@ -107,7 +108,7 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
placeholder=""
|
||||
placeholder={null}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
<ListPlugin />
|
||||
@@ -125,6 +126,7 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
<TwitterPlugin />
|
||||
<YouTubePlugin />
|
||||
<CollapsiblePlugin />
|
||||
<TabIndentationPlugin />
|
||||
{!readonly && floatingAnchorElem && (
|
||||
<>
|
||||
<FloatingTextFormatToolbarPlugin anchorElem={floatingAnchorElem} />
|
||||
|
||||
@@ -192,9 +192,10 @@ export function AutoEmbedDialog({
|
||||
const embedResult =
|
||||
text != null && urlMatch != null ? embedConfig.parseUrl(text) : null;
|
||||
|
||||
const onClick = () => {
|
||||
if (embedResult != null) {
|
||||
embedConfig.insertNode(editor, embedResult);
|
||||
const onClick = async () => {
|
||||
const result = await embedResult;
|
||||
if (result != null) {
|
||||
embedConfig.insertNode(editor, result);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
+16
-10
@@ -20,6 +20,7 @@ type SerializedCollapsibleContainerNode = Spread<
|
||||
{
|
||||
type: 'collapsible-container';
|
||||
version: 1;
|
||||
open: boolean;
|
||||
},
|
||||
SerializedElementNode
|
||||
>;
|
||||
@@ -29,25 +30,27 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
|
||||
constructor(open: boolean, key?: NodeKey) {
|
||||
super(key);
|
||||
this.__open = open;
|
||||
this.__open = open ?? false;
|
||||
}
|
||||
|
||||
static getType(): string {
|
||||
static override getType(): string {
|
||||
return 'collapsible-container';
|
||||
}
|
||||
|
||||
static clone(node: CollapsibleContainerNode): CollapsibleContainerNode {
|
||||
static override clone(
|
||||
node: CollapsibleContainerNode,
|
||||
): CollapsibleContainerNode {
|
||||
return new CollapsibleContainerNode(node.__open, node.__key);
|
||||
}
|
||||
|
||||
createDOM(config: EditorConfig): HTMLElement {
|
||||
override createDOM(config: EditorConfig): HTMLElement {
|
||||
const dom = document.createElement('details');
|
||||
dom.classList.add('Collapsible__container');
|
||||
dom.open = this.__open;
|
||||
return dom;
|
||||
}
|
||||
|
||||
updateDOM(
|
||||
override updateDOM(
|
||||
prevNode: CollapsibleContainerNode,
|
||||
dom: HTMLDetailsElement,
|
||||
): boolean {
|
||||
@@ -62,18 +65,19 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
return {};
|
||||
}
|
||||
|
||||
static importJSON(
|
||||
static override importJSON(
|
||||
serializedNode: SerializedCollapsibleContainerNode,
|
||||
): CollapsibleContainerNode {
|
||||
const node = $createCollapsibleContainerNode();
|
||||
const node = $createCollapsibleContainerNode(serializedNode.open);
|
||||
return node;
|
||||
}
|
||||
|
||||
exportJSON(): SerializedCollapsibleContainerNode {
|
||||
override exportJSON(): SerializedCollapsibleContainerNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
type: 'collapsible-container',
|
||||
version: 1,
|
||||
open: this.__open,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,8 +95,10 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
}
|
||||
}
|
||||
|
||||
export function $createCollapsibleContainerNode(): CollapsibleContainerNode {
|
||||
return new CollapsibleContainerNode(true);
|
||||
export function $createCollapsibleContainerNode(
|
||||
open: boolean,
|
||||
): CollapsibleContainerNode {
|
||||
return new CollapsibleContainerNode(open);
|
||||
}
|
||||
|
||||
export function $isCollapsibleContainerNode(
|
||||
|
||||
+10
-7
@@ -24,21 +24,24 @@ type SerializedCollapsibleContentNode = Spread<
|
||||
>;
|
||||
|
||||
export class CollapsibleContentNode extends ElementNode {
|
||||
static getType(): string {
|
||||
static override getType(): string {
|
||||
return 'collapsible-content';
|
||||
}
|
||||
|
||||
static clone(node: CollapsibleContentNode): CollapsibleContentNode {
|
||||
static override clone(node: CollapsibleContentNode): CollapsibleContentNode {
|
||||
return new CollapsibleContentNode(node.__key);
|
||||
}
|
||||
|
||||
createDOM(config: EditorConfig): HTMLElement {
|
||||
override createDOM(config: EditorConfig): HTMLElement {
|
||||
const dom = document.createElement('div');
|
||||
dom.classList.add('Collapsible__content');
|
||||
return dom;
|
||||
}
|
||||
|
||||
updateDOM(prevNode: CollapsibleContentNode, dom: HTMLElement): boolean {
|
||||
override updateDOM(
|
||||
prevNode: CollapsibleContentNode,
|
||||
dom: HTMLElement,
|
||||
): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -46,17 +49,17 @@ export class CollapsibleContentNode extends ElementNode {
|
||||
return {};
|
||||
}
|
||||
|
||||
static importJSON(
|
||||
static override importJSON(
|
||||
serializedNode: SerializedCollapsibleContentNode,
|
||||
): CollapsibleContentNode {
|
||||
return $createCollapsibleContentNode();
|
||||
}
|
||||
|
||||
isShadowRoot(): boolean {
|
||||
override isShadowRoot(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
exportJSON(): SerializedCollapsibleContentNode {
|
||||
override exportJSON(): SerializedCollapsibleContentNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
type: 'collapsible-content',
|
||||
|
||||
+21
-8
@@ -31,21 +31,34 @@ type SerializedCollapsibleTitleNode = Spread<
|
||||
>;
|
||||
|
||||
export class CollapsibleTitleNode extends ElementNode {
|
||||
static getType(): string {
|
||||
static override getType(): string {
|
||||
return 'collapsible-title';
|
||||
}
|
||||
|
||||
static clone(node: CollapsibleTitleNode): CollapsibleTitleNode {
|
||||
static override clone(node: CollapsibleTitleNode): CollapsibleTitleNode {
|
||||
return new CollapsibleTitleNode(node.__key);
|
||||
}
|
||||
|
||||
createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement {
|
||||
override createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement {
|
||||
const dom = document.createElement('summary');
|
||||
dom.classList.add('Collapsible__title');
|
||||
dom.onclick = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
editor.update(() => {
|
||||
const containerNode = this.getParentOrThrow();
|
||||
if ($isCollapsibleContainerNode(containerNode)) {
|
||||
containerNode.toggleOpen();
|
||||
}
|
||||
});
|
||||
};
|
||||
return dom;
|
||||
}
|
||||
|
||||
updateDOM(prevNode: CollapsibleTitleNode, dom: HTMLElement): boolean {
|
||||
override updateDOM(
|
||||
prevNode: CollapsibleTitleNode,
|
||||
dom: HTMLElement,
|
||||
): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -53,13 +66,13 @@ export class CollapsibleTitleNode extends ElementNode {
|
||||
return {};
|
||||
}
|
||||
|
||||
static importJSON(
|
||||
static override importJSON(
|
||||
serializedNode: SerializedCollapsibleTitleNode,
|
||||
): CollapsibleTitleNode {
|
||||
return $createCollapsibleTitleNode();
|
||||
}
|
||||
|
||||
exportJSON(): SerializedCollapsibleTitleNode {
|
||||
override exportJSON(): SerializedCollapsibleTitleNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
type: 'collapsible-title',
|
||||
@@ -67,12 +80,12 @@ export class CollapsibleTitleNode extends ElementNode {
|
||||
};
|
||||
}
|
||||
|
||||
collapseAtStart(_selection: RangeSelection): boolean {
|
||||
override collapseAtStart(_selection: RangeSelection): boolean {
|
||||
this.getParentOrThrow().insertBefore(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
insertNewAfter(): ElementNode {
|
||||
override insertNewAfter(): ElementNode {
|
||||
const containerNode = this.getParentOrThrow();
|
||||
|
||||
if (!$isCollapsibleContainerNode(containerNode)) {
|
||||
|
||||
@@ -204,7 +204,7 @@ export default function CollapsiblePlugin(): JSX.Element | null {
|
||||
const content = $createCollapsibleContentNode().append(
|
||||
$createParagraphNode(),
|
||||
);
|
||||
const container = $createCollapsibleContainerNode().append(
|
||||
const container = $createCollapsibleContainerNode(true).append(
|
||||
title,
|
||||
content,
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {mergeRegister} from '@lexical/utils';
|
||||
import {
|
||||
$getNearestNodeFromDOMNode,
|
||||
$getNodeByKey,
|
||||
$getRoot,
|
||||
COMMAND_PRIORITY_HIGH,
|
||||
COMMAND_PRIORITY_LOW,
|
||||
DRAGOVER_COMMAND,
|
||||
@@ -58,8 +59,7 @@ function getCurrentIndex(keysLength: number): number {
|
||||
}
|
||||
|
||||
function getTopLevelNodeKeys(editor: LexicalEditor): string[] {
|
||||
const root = editor.getEditorState()._nodeMap.get('root');
|
||||
return root ? root.__children : [];
|
||||
return editor.getEditorState().read(() => $getRoot().getChildrenKeys());
|
||||
}
|
||||
|
||||
function elementContainingEventLocation(
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
||||
import {
|
||||
$getSelection,
|
||||
$isRangeSelection,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
INDENT_CONTENT_COMMAND,
|
||||
KEY_TAB_COMMAND,
|
||||
OUTDENT_CONTENT_COMMAND,
|
||||
} from 'lexical';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
/**
|
||||
* This plugin adds the ability to indent content using the tab key. Generally, we don't
|
||||
* recommend using this plugin as it could negatively affect acessibility for keyboard
|
||||
* users, causing focus to become trapped within the editor.
|
||||
*/
|
||||
export function TabIndentationPlugin(): null {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
useEffect(() => {
|
||||
return editor.registerCommand<KeyboardEvent>(
|
||||
KEY_TAB_COMMAND,
|
||||
(event) => {
|
||||
const selection = $getSelection();
|
||||
|
||||
if (!$isRangeSelection(selection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
return editor.dispatchCommand(
|
||||
event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND,
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.104.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.104.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.104.31",
|
||||
"version": "3.104.43",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.49.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.49.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
@@ -583,7 +583,7 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4
|
||||
FBReactNativeSpec: dd1186fd05255e3457baa2f4ca65e94c2cd1e3ac
|
||||
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
|
||||
@@ -596,7 +596,7 @@ SPEC CHECKSUMS:
|
||||
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
|
||||
FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
|
||||
glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85
|
||||
hermes-engine: 2af7b7a59128f250adfd86f15aa1d5a2ecd39995
|
||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.49.28",
|
||||
"version": "3.49.40",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.66](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.65](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.64](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.63](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.59](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.58](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.55](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.54](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.4.54",
|
||||
"version": "1.4.66",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -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.50.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.50.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.50.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.50.7",
|
||||
"version": "1.50.9",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -4,8 +4,6 @@ export const InfoStrings = {
|
||||
'This backup file was created using a newer version of the application and cannot be imported here. Please update your application and try again.',
|
||||
BackupFileMoreRecentThanAccount:
|
||||
"This backup file was created using a newer encryption version than your account's. Please run the available encryption upgrade and try again.",
|
||||
SavingWhileDocumentHidden:
|
||||
'Attempting to save an item while the application is hidden. To protect data integrity, please refresh the application window and try again.',
|
||||
InvalidNote:
|
||||
"The note you are attempting to save can not be found or has been deleted. Changes you make will not be synced. Please copy this note's text and start a new note.",
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [2.156.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Disabled Moments photo capture when app is passcode or biometrics locked ([#2114](https://github.com/standardnotes/app/issues/2114)) ([94c2802](https://github.com/standardnotes/app/commit/94c280256e0a71fb8078f0d0e4d7ad2b6d3c1462))
|
||||
|
||||
## [2.156.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** handling expired token responses ([#2113](https://github.com/standardnotes/app/issues/2113)) ([2696b07](https://github.com/standardnotes/app/commit/2696b07759104fb1d40781897f69e63133ad824a))
|
||||
|
||||
## [2.156.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.156.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -178,6 +178,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
||||
private launched = false
|
||||
/** Whether the application has been destroyed via .deinit() */
|
||||
public dealloced = false
|
||||
private isBiometricsSoftLockEngaged = false
|
||||
private revokingSession = false
|
||||
private handledFullSyncStage = false
|
||||
|
||||
@@ -940,11 +941,12 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
||||
return this.protocolService.hasPasscode()
|
||||
}
|
||||
|
||||
isLocked(): Promise<boolean> {
|
||||
async isLocked(): Promise<boolean> {
|
||||
if (!this.started) {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
return this.challengeService.isPasscodeLocked()
|
||||
const isPasscodeLocked = await this.challengeService.isPasscodeLocked()
|
||||
return isPasscodeLocked || this.isBiometricsSoftLockEngaged
|
||||
}
|
||||
|
||||
public async lock(): Promise<void> {
|
||||
@@ -964,10 +966,12 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
||||
|
||||
void this.promptForCustomChallenge(challenge)
|
||||
|
||||
this.isBiometricsSoftLockEngaged = true
|
||||
void this.notifyEvent(ApplicationEvent.BiometricsSoftLockEngaged)
|
||||
|
||||
this.addChallengeObserver(challenge, {
|
||||
onComplete: () => {
|
||||
this.isBiometricsSoftLockEngaged = false
|
||||
void this.notifyEvent(ApplicationEvent.BiometricsSoftLockDisengaged)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as Factory from './lib/factory.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe.skip('subscriptions', function () {
|
||||
describe('subscriptions', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let application
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.156.6",
|
||||
"version": "2.156.9",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
### Features
|
||||
|
||||
* Added FileSend links to general account menu and preferences menu ([4f07b91](https://github.com/standardnotes/app/commit/4f07b915f0a60632a9e537b3b0394290d25fa071))
|
||||
|
||||
## [1.20.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.20.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.20.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.20.2",
|
||||
"version": "1.21.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ const PREFERENCE_IDS = [
|
||||
'get-free-month',
|
||||
'help-feedback',
|
||||
'whats-new',
|
||||
'filesend',
|
||||
] as const
|
||||
|
||||
export type PreferenceId = typeof PREFERENCE_IDS[number]
|
||||
|
||||
@@ -3,6 +3,69 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.128.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.128.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
# [3.128.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
### Features
|
||||
|
||||
* Added FileSend links to general account menu and preferences menu ([4f07b91](https://github.com/standardnotes/app/commit/4f07b915f0a60632a9e537b3b0394290d25fa071))
|
||||
|
||||
## [3.127.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.127.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed issue where navigating to a Daily Notebook on iOS would cause editor panel to be offscreen ([fe36009](https://github.com/standardnotes/app/commit/fe36009482ff1615612ca965abb956318d8bc626))
|
||||
|
||||
## [3.127.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Made Moments-related toast messages dismissable ([c59d824](https://github.com/standardnotes/app/commit/c59d82424012f98638625e057f88124982ad0734))
|
||||
|
||||
## [3.127.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed issue where Super note would convert whole line into tag even after closing menu ([35b0073](https://github.com/standardnotes/app/commit/35b00737bf13ceb8e66d64c67925f2fe9df2587c))
|
||||
|
||||
## [3.127.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Disabled Moments photo capture when app is passcode or biometrics locked ([#2114](https://github.com/standardnotes/app/issues/2114)) ([94c2802](https://github.com/standardnotes/app/commit/94c280256e0a71fb8078f0d0e4d7ad2b6d3c1462))
|
||||
|
||||
## [3.127.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.127.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed issue where Moments-related toasts would not get correctly dismissed ([eceb46b](https://github.com/standardnotes/app/commit/eceb46bed7117c8bf5ea34d87a800f8bf02881ac))
|
||||
* Increased interval between Moments capturing photos ([e4b3028](https://github.com/standardnotes/app/commit/e4b3028d673a037f2740939b294cf6301db0d529))
|
||||
|
||||
## [3.127.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Added potential fix for issue where some video would show as not supported ([a7add80](https://github.com/standardnotes/app/commit/a7add80633ea603f7883db2dbc1f886d40450979))
|
||||
|
||||
## [3.127.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.127.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
@@ -1,5 +1,160 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "3.128.2",
|
||||
"title": "[3.128.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.128.1",
|
||||
"title": "[3.128.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.128.0",
|
||||
"title": "[3.128.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)",
|
||||
"date": null,
|
||||
"body": "### Features\n\n* Added FileSend links to general account menu and preferences menu ([4f07b91](https://github.com/standardnotes/app/commit/4f07b915f0a60632a9e537b3b0394290d25fa071))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Added FileSend links to general account menu and preferences menu (4f07b91)"
|
||||
],
|
||||
"Features": [
|
||||
"Added FileSend links to general account menu and preferences menu (4f07b91)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.14",
|
||||
"title": "[3.127.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-24)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.13",
|
||||
"title": "[3.127.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Fixed issue where navigating to a Daily Notebook on iOS would cause editor panel to be offscreen ([fe36009](https://github.com/standardnotes/app/commit/fe36009482ff1615612ca965abb956318d8bc626))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Fixed issue where navigating to a Daily Notebook on iOS would cause editor panel to be offscreen (fe36009)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Fixed issue where navigating to a Daily Notebook on iOS would cause editor panel to be offscreen (fe36009)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.12",
|
||||
"title": "[3.127.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Made Moments-related toast messages dismissable ([c59d824](https://github.com/standardnotes/app/commit/c59d82424012f98638625e057f88124982ad0734))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Made Moments-related toast messages dismissable (c59d824)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Made Moments-related toast messages dismissable (c59d824)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.11",
|
||||
"title": "[3.127.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-23)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Fixed issue where Super note would convert whole line into tag even after closing menu ([35b0073](https://github.com/standardnotes/app/commit/35b00737bf13ceb8e66d64c67925f2fe9df2587c))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Fixed issue where Super note would convert whole line into tag even after closing menu (35b0073)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Fixed issue where Super note would convert whole line into tag even after closing menu (35b0073)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.10",
|
||||
"title": "[3.127.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-22)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Disabled Moments photo capture when app is passcode or biometrics locked ([#2114](https://github.com/standardnotes/app/issues/2114)) ([94c2802](https://github.com/standardnotes/app/commit/94c280256e0a71fb8078f0d0e4d7ad2b6d3c1462))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Disabled Moments photo capture when app is passcode or biometrics locked (#2114) (94c2802)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Disabled Moments photo capture when app is passcode or biometrics locked (#2114) (94c2802)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.9",
|
||||
"title": "[3.127.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-21)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.8",
|
||||
"title": "[3.127.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Fixed issue where Moments-related toasts would not get correctly dismissed ([eceb46b](https://github.com/standardnotes/app/commit/eceb46bed7117c8bf5ea34d87a800f8bf02881ac))\n* Increased interval between Moments capturing photos ([e4b3028](https://github.com/standardnotes/app/commit/e4b3028d673a037f2740939b294cf6301db0d529))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Fixed issue where Moments-related toasts would not get correctly dismissed (eceb46b)",
|
||||
"Increased interval between Moments capturing photos (e4b3028)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Fixed issue where Moments-related toasts would not get correctly dismissed (eceb46b)",
|
||||
"Increased interval between Moments capturing photos (e4b3028)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.7",
|
||||
"title": "[3.127.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Added potential fix for issue where some video would show as not supported ([a7add80](https://github.com/standardnotes/app/commit/a7add80633ea603f7883db2dbc1f886d40450979))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Added potential fix for issue where some video would show as not supported (a7add80)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Added potential fix for issue where some video would show as not supported (a7add80)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.6",
|
||||
"title": "[3.127.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-20)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.127.5",
|
||||
"title": "[3.127.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-19)",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.127.5",
|
||||
"version": "3.128.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes.",
|
||||
@@ -28,7 +28,7 @@
|
||||
"@babel/plugin-transform-react-jsx": "^7.19.0",
|
||||
"@babel/preset-env": "*",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@lexical/react": "^0.6.5",
|
||||
"@lexical/react": "0.7.5",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@reach/alert": "^0.18.0",
|
||||
"@reach/alert-dialog": "^0.18.0",
|
||||
@@ -84,7 +84,7 @@
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"lexical": "0.6.5",
|
||||
"lexical": "0.7.5",
|
||||
"lint-staged": ">=13",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"minimatch": "^5.1.1",
|
||||
|
||||
@@ -84,6 +84,17 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
setMenuPane(AccountMenuPane.SignIn)
|
||||
}, [setMenuPane])
|
||||
|
||||
const openFileSend = useCallback(() => {
|
||||
const link = 'https://filesend.standardnotes.com/'
|
||||
|
||||
if (application.isNativeMobileWeb()) {
|
||||
application.mobileDevice().openUrl(link)
|
||||
return
|
||||
}
|
||||
|
||||
window.open(link, '_blank')
|
||||
}, [application])
|
||||
|
||||
const CREATE_ACCOUNT_INDEX = 1
|
||||
const SWITCHER_INDEX = 0
|
||||
|
||||
@@ -172,6 +183,10 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
</div>
|
||||
<span className="text-neutral">v{application.version}</span>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={openFileSend}>
|
||||
<Icon type="open-in" className={iconClassName} />
|
||||
Open FileSend
|
||||
</MenuItem>
|
||||
{user ? (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
|
||||
+7
-9
@@ -1,6 +1,6 @@
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
|
||||
export enum PopoverFileItemActionType {
|
||||
export enum FileItemActionType {
|
||||
AttachFileToNote,
|
||||
DetachFileToNote,
|
||||
DeleteFile,
|
||||
@@ -10,34 +10,32 @@ export enum PopoverFileItemActionType {
|
||||
PreviewFile,
|
||||
}
|
||||
|
||||
export type PopoverFileItemAction =
|
||||
export type FileItemAction =
|
||||
| {
|
||||
type: Exclude<
|
||||
PopoverFileItemActionType,
|
||||
| PopoverFileItemActionType.RenameFile
|
||||
| PopoverFileItemActionType.ToggleFileProtection
|
||||
| PopoverFileItemActionType.PreviewFile
|
||||
FileItemActionType,
|
||||
FileItemActionType.RenameFile | FileItemActionType.ToggleFileProtection | FileItemActionType.PreviewFile
|
||||
>
|
||||
payload: {
|
||||
file: FileItem
|
||||
}
|
||||
}
|
||||
| {
|
||||
type: PopoverFileItemActionType.ToggleFileProtection
|
||||
type: FileItemActionType.ToggleFileProtection
|
||||
payload: {
|
||||
file: FileItem
|
||||
}
|
||||
callback: (isProtected: boolean) => void
|
||||
}
|
||||
| {
|
||||
type: PopoverFileItemActionType.RenameFile
|
||||
type: FileItemActionType.RenameFile
|
||||
payload: {
|
||||
file: FileItem
|
||||
name: string
|
||||
}
|
||||
}
|
||||
| {
|
||||
type: PopoverFileItemActionType.PreviewFile
|
||||
type: FileItemActionType.PreviewFile
|
||||
payload: {
|
||||
file: FileItem
|
||||
otherFiles?: FileItem[]
|
||||
|
||||
@@ -13,6 +13,7 @@ import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { ContentType, SNTag } from '@standardnotes/snjs'
|
||||
import { featureTrunkEnabled, FeatureTrunkName } from '@/FeatureTrunk'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -97,6 +98,8 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
|
||||
const hasNotes = items.some((item) => item.content_type === ContentType.Note)
|
||||
|
||||
const isFilesTableViewEnabled = featureTrunkEnabled(FeatureTrunkName.FilesTableView)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -132,8 +135,8 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
onSelect={selectItem}
|
||||
tags={getTagsForItem(item)}
|
||||
notesController={notesController}
|
||||
isPreviousItemTiled={previousItem?.content_type === ContentType.File}
|
||||
isNextItemTiled={nextItem?.content_type === ContentType.File}
|
||||
isPreviousItemTiled={previousItem?.content_type === ContentType.File && !isFilesTableViewEnabled}
|
||||
isNextItemTiled={nextItem?.content_type === ContentType.File && !isFilesTableViewEnabled}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import { featureTrunkEnabled, FeatureTrunkName } from '@/FeatureTrunk'
|
||||
import { ContentType, FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import React, { FunctionComponent } from 'react'
|
||||
import FileListItem from './FileListItem'
|
||||
import FileListItemCard from './FileListItemCard'
|
||||
import NoteListItem from './NoteListItem'
|
||||
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
|
||||
const ContentListItem: FunctionComponent<AbstractListItemProps<ListableContentItem>> = (props) => {
|
||||
const isFilesTableViewEnabled = featureTrunkEnabled(FeatureTrunkName.FilesTableView)
|
||||
|
||||
switch (props.item.content_type) {
|
||||
case ContentType.Note:
|
||||
return <NoteListItem {...props} item={props.item as SNNote} />
|
||||
case ContentType.File:
|
||||
return <FileListItem {...props} item={props.item as FileItem} />
|
||||
case ContentType.File: {
|
||||
if (isFilesTableViewEnabled) {
|
||||
return <FileListItem {...props} item={props.item as FileItem} />
|
||||
}
|
||||
|
||||
return <FileListItemCard {...props} item={props.item as FileItem} />
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||
import { FileItem, PrefKey, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { FileItem, PrefKey, SystemViewId, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { forwardRef, useCallback, useEffect, useMemo } from 'react'
|
||||
import ContentList from '@/Components/ContentListView/ContentList'
|
||||
@@ -37,6 +37,9 @@ import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import { useForwardedRef } from '@/Hooks/useForwardedRef'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import FloatingAddButton from './FloatingAddButton'
|
||||
import FilesTableView from '../FilesTableView/FilesTableView'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { featureTrunkEnabled, FeatureTrunkName } from '@/FeatureTrunk'
|
||||
|
||||
type Props = {
|
||||
accountMenuController: AccountMenuController
|
||||
@@ -49,6 +52,7 @@ type Props = {
|
||||
selectionController: SelectedItemsController
|
||||
searchOptionsController: SearchOptionsController
|
||||
linkingController: LinkingController
|
||||
featuresController: FeaturesController
|
||||
className?: string
|
||||
id: string
|
||||
children?: React.ReactNode
|
||||
@@ -68,6 +72,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
selectionController,
|
||||
searchOptionsController,
|
||||
linkingController,
|
||||
featuresController,
|
||||
className,
|
||||
id,
|
||||
children,
|
||||
@@ -280,6 +285,9 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
}
|
||||
}, [selectedUuids, innerRef, isCurrentNoteTemplate, renderedItems, panes])
|
||||
|
||||
const isFilesTableViewEnabled = featureTrunkEnabled(FeatureTrunkName.FilesTableView)
|
||||
const shouldShowFilesTableView = isFilesTableViewEnabled && selectedTag?.uuid === SystemViewId.Files
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
@@ -300,12 +308,16 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
addButtonLabel={addButtonLabel}
|
||||
addNewItem={addNewItem}
|
||||
isFilesSmartView={isFilesSmartView}
|
||||
isFilesTableViewEnabled={isFilesTableViewEnabled}
|
||||
optionsSubtitle={optionsSubtitle}
|
||||
selectedTag={selectedTag}
|
||||
filesController={filesController}
|
||||
itemListController={itemListController}
|
||||
/>
|
||||
)}
|
||||
<SearchBar itemListController={itemListController} searchOptionsController={searchOptionsController} />
|
||||
{!isFilesTableViewEnabled && (
|
||||
<SearchBar itemListController={itemListController} searchOptionsController={searchOptionsController} />
|
||||
)}
|
||||
<NoAccountWarning
|
||||
accountMenuController={accountMenuController}
|
||||
noAccountWarningController={noAccountWarningController}
|
||||
@@ -324,13 +336,18 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
{!dailyMode && completedFullSync && !renderedItems.length ? (
|
||||
<p className="empty-items-list opacity-50">No items.</p>
|
||||
) : null}
|
||||
|
||||
{!dailyMode && !completedFullSync && !renderedItems.length ? (
|
||||
<p className="empty-items-list opacity-50">Loading...</p>
|
||||
) : null}
|
||||
|
||||
{!dailyMode && renderedItems.length ? (
|
||||
<>
|
||||
shouldShowFilesTableView ? (
|
||||
<FilesTableView
|
||||
application={application}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
linkingController={linkingController}
|
||||
/>
|
||||
) : (
|
||||
<ContentList
|
||||
items={renderedItems}
|
||||
selectedUuids={selectedUuids}
|
||||
@@ -342,7 +359,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
notesController={notesController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
) : null}
|
||||
<div className="absolute bottom-0 h-safe-bottom w-full" />
|
||||
{children}
|
||||
|
||||
@@ -9,13 +9,12 @@ import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||
import ListItemFlagIcons from './ListItemFlagIcons'
|
||||
|
||||
const FileListItem: FunctionComponent<DisplayableListItemProps<FileItem>> = ({
|
||||
const FileListItemCard: FunctionComponent<DisplayableListItemProps<FileItem>> = ({
|
||||
filesController,
|
||||
hideDate,
|
||||
hideIcon,
|
||||
@@ -75,57 +74,38 @@ const FileListItem: FunctionComponent<DisplayableListItemProps<FileItem>> = ({
|
||||
}, [file, onSelect, setPaneLayout])
|
||||
|
||||
const IconComponent = () =>
|
||||
getFileIconComponent(getIconForFileType((file as FileItem).mimeType), 'w-10 h-10 flex-shrink-0')
|
||||
getFileIconComponent(getIconForFileType((file as FileItem).mimeType), 'w-5 h-5 flex-shrink-0')
|
||||
|
||||
useContextMenuEvent(listItemRef, openContextMenu)
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={listItemRef}
|
||||
className={classNames('flex max-h-[300px] w-[190px] cursor-pointer px-1 pt-2 text-text md:w-[200px]')}
|
||||
className={classNames(
|
||||
'content-list-item flex w-full cursor-pointer items-stretch text-text',
|
||||
selected && 'selected border-l-2px border-solid border-info',
|
||||
)}
|
||||
id={file.uuid}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-col justify-between overflow-hidden rounded bg-passive-5 pt-5 transition-all hover:bg-passive-4 ${
|
||||
selected ? 'border-[1px] border-solid border-info' : 'border-[1px] border-solid border-border'
|
||||
}`}
|
||||
>
|
||||
<div className={'px-5'}>
|
||||
{!hideIcon ? (
|
||||
<div className="mr-0">
|
||||
<IconComponent />
|
||||
</div>
|
||||
) : (
|
||||
<div className="pr-4" />
|
||||
)}
|
||||
<div className="min-w-0 flex-grow py-4 px-0">
|
||||
<div className="line-clamp-2 overflow-hidden text-editor font-semibold">
|
||||
<div className="break-word line-clamp-2 mr-2 overflow-hidden">{file.title}</div>
|
||||
</div>
|
||||
<ListItemMetadata item={file} hideDate={hideDate} sortBy={sortBy} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
<ListItemConflictIndicator item={file} />
|
||||
</div>
|
||||
{!hideIcon ? (
|
||||
<div className="mr-0 flex flex-col items-center justify-between p-4.5 pr-3">
|
||||
<IconComponent />
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'border-t-[1px] border-solid border-border p-3 text-xs font-bold',
|
||||
selected ? 'bg-info text-info-contrast' : 'bg-passive-4 text-neutral',
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
{formatSizeToReadableString(file.decryptedSize)}
|
||||
{backupInfo && (
|
||||
<div title="File is backed up locally">
|
||||
<Icon type="check-circle" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="pr-4" />
|
||||
)}
|
||||
<div className="min-w-0 flex-grow border-b border-solid border-border py-4 px-0">
|
||||
<div className="flex items-start justify-between overflow-hidden text-base font-semibold leading-[1.3]">
|
||||
<div className="break-word mr-2">{file.title}</div>
|
||||
</div>
|
||||
<ListItemMetadata item={file} hideDate={hideDate} sortBy={sortBy} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
<ListItemConflictIndicator item={file} />
|
||||
</div>
|
||||
<ListItemFlagIcons className="p-4" item={file} isFileBackedUp={!!backupInfo} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(FileListItem)
|
||||
export default observer(FileListItemCard)
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { FileItem, FileBackupRecord } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { getFileIconComponent } from '../FilePreview/getFileIconComponent'
|
||||
import ListItemConflictIndicator from './ListItemConflictIndicator'
|
||||
import ListItemTags from './ListItemTags'
|
||||
import ListItemMetadata from './ListItemMetadata'
|
||||
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||
|
||||
const FileListItemCard: FunctionComponent<DisplayableListItemProps<FileItem>> = ({
|
||||
filesController,
|
||||
hideDate,
|
||||
hideIcon,
|
||||
hideTags,
|
||||
item: file,
|
||||
onSelect,
|
||||
selected,
|
||||
sortBy,
|
||||
tags,
|
||||
}) => {
|
||||
const { setPaneLayout } = useResponsiveAppPane()
|
||||
const application = useApplication()
|
||||
|
||||
const [backupInfo, setBackupInfo] = useState<FileBackupRecord | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
void application.fileBackups?.getFileBackupInfo(file).then(setBackupInfo)
|
||||
}, [application, file])
|
||||
|
||||
const listItemRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const openFileContextMenu = useCallback(
|
||||
(posX: number, posY: number) => {
|
||||
filesController.setShowFileContextMenu(false)
|
||||
filesController.setFileContextMenuLocation({
|
||||
x: posX,
|
||||
y: posY,
|
||||
})
|
||||
filesController.setShowFileContextMenu(true)
|
||||
},
|
||||
[filesController],
|
||||
)
|
||||
|
||||
const openContextMenu = useCallback(
|
||||
async (posX: number, posY: number) => {
|
||||
let shouldOpenContextMenu = selected
|
||||
|
||||
if (!selected) {
|
||||
const { didSelect } = await onSelect(file)
|
||||
if (didSelect) {
|
||||
shouldOpenContextMenu = true
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldOpenContextMenu) {
|
||||
openFileContextMenu(posX, posY)
|
||||
}
|
||||
},
|
||||
[selected, onSelect, file, openFileContextMenu],
|
||||
)
|
||||
|
||||
const onClick = useCallback(async () => {
|
||||
const { didSelect } = await onSelect(file, true)
|
||||
if (didSelect) {
|
||||
setPaneLayout(PaneLayout.Editing)
|
||||
}
|
||||
}, [file, onSelect, setPaneLayout])
|
||||
|
||||
const IconComponent = () =>
|
||||
getFileIconComponent(getIconForFileType((file as FileItem).mimeType), 'w-10 h-10 flex-shrink-0')
|
||||
|
||||
useContextMenuEvent(listItemRef, openContextMenu)
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={listItemRef}
|
||||
className={classNames('flex max-h-[300px] w-[190px] cursor-pointer px-1 pt-2 text-text md:w-[200px]')}
|
||||
id={file.uuid}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-col justify-between overflow-hidden rounded bg-passive-5 pt-5 transition-all hover:bg-passive-4 ${
|
||||
selected ? 'border-[1px] border-solid border-info' : 'border-[1px] border-solid border-border'
|
||||
}`}
|
||||
>
|
||||
<div className={'px-5'}>
|
||||
{!hideIcon ? (
|
||||
<div className="mr-0">
|
||||
<IconComponent />
|
||||
</div>
|
||||
) : (
|
||||
<div className="pr-4" />
|
||||
)}
|
||||
<div className="min-w-0 flex-grow py-4 px-0">
|
||||
<div className="line-clamp-2 overflow-hidden text-editor font-semibold">
|
||||
<div className="break-word line-clamp-2 mr-2 overflow-hidden">{file.title}</div>
|
||||
</div>
|
||||
<ListItemMetadata item={file} hideDate={hideDate} sortBy={sortBy} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
<ListItemConflictIndicator item={file} />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'border-t-[1px] border-solid border-border p-3 text-xs font-bold',
|
||||
selected ? 'bg-info text-info-contrast' : 'bg-passive-4 text-neutral',
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
{formatSizeToReadableString(file.decryptedSize)}
|
||||
{backupInfo && (
|
||||
<div title="File is backed up locally">
|
||||
<Icon type="check-circle" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(FileListItemCard)
|
||||
+2
-6
@@ -44,12 +44,8 @@ const AddItemMenuButton = ({
|
||||
<>
|
||||
<button
|
||||
className={classNames(
|
||||
'hidden md:flex',
|
||||
'h-8 w-8 hover:brightness-125',
|
||||
'z-editor-title-bar ml-3 cursor-pointer items-center',
|
||||
`justify-center rounded-full border border-solid border-transparent ${
|
||||
isDailyEntry ? 'bg-danger text-danger-contrast' : 'bg-info text-info-contrast'
|
||||
}`,
|
||||
'z-editor-title-bar hidden h-8 w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-transparent hover:brightness-125 md:flex',
|
||||
isDailyEntry ? 'bg-danger text-danger-contrast' : 'bg-info text-info-contrast',
|
||||
)}
|
||||
title={addButtonLabel}
|
||||
aria-label={addButtonLabel}
|
||||
|
||||
+17
-2
@@ -11,6 +11,8 @@ import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
||||
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import AddItemMenuButton from './AddItemMenuButton'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import SearchButton from './SearchButton'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -19,9 +21,11 @@ type Props = {
|
||||
addButtonLabel: string
|
||||
addNewItem: () => void
|
||||
isFilesSmartView: boolean
|
||||
isFilesTableViewEnabled: boolean
|
||||
optionsSubtitle?: string
|
||||
selectedTag: AnyTag
|
||||
filesController: FilesController
|
||||
itemListController: ItemListController
|
||||
}
|
||||
|
||||
const ContentListHeader = ({
|
||||
@@ -31,9 +35,11 @@ const ContentListHeader = ({
|
||||
addButtonLabel,
|
||||
addNewItem,
|
||||
isFilesSmartView,
|
||||
isFilesTableViewEnabled,
|
||||
optionsSubtitle,
|
||||
selectedTag,
|
||||
filesController,
|
||||
itemListController,
|
||||
}: Props) => {
|
||||
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
|
||||
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
||||
@@ -98,6 +104,14 @@ const ContentListHeader = ({
|
||||
)
|
||||
}, [addButtonLabel, addNewItem, filesController, isDailyEntry, isFilesSmartView])
|
||||
|
||||
const SearchBarButton = useMemo(() => {
|
||||
if (!isFilesSmartView || !isFilesTableViewEnabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <SearchButton itemListController={itemListController} />
|
||||
}, [isFilesSmartView, isFilesTableViewEnabled, itemListController])
|
||||
|
||||
const FolderName = useMemo(() => {
|
||||
return (
|
||||
<div className="flex min-w-0 flex-grow flex-col break-words pt-1 lg:pt-0">
|
||||
@@ -125,13 +139,14 @@ const ContentListHeader = ({
|
||||
<div className={'flex w-full justify-between md:flex'}>
|
||||
<NavigationMenuButton />
|
||||
{FolderName}
|
||||
<div className="flex">
|
||||
<div className="flex items-center gap-3">
|
||||
{SearchBarButton}
|
||||
{OptionsMenu}
|
||||
{AddButton}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [OptionsMenu, AddButton, FolderName])
|
||||
}, [FolderName, SearchBarButton, OptionsMenu, AddButton])
|
||||
|
||||
const TabletLayout = useMemo(() => {
|
||||
return (
|
||||
|
||||
+90
-78
@@ -5,6 +5,7 @@ import {
|
||||
isSmartView,
|
||||
isSystemView,
|
||||
PrefKey,
|
||||
SystemViewId,
|
||||
TagMutator,
|
||||
TagPreferences,
|
||||
VectorIconNameOrEmoji,
|
||||
@@ -23,6 +24,7 @@ import NoSubscriptionBanner from '@/Components/NoSubscriptionBanner/NoSubscripti
|
||||
import MenuRadioButtonItem from '@/Components/Menu/MenuRadioButtonItem'
|
||||
import MenuSwitchButtonItem from '@/Components/Menu/MenuSwitchButtonItem'
|
||||
import { Pill } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { featureTrunkEnabled, FeatureTrunkName } from '@/FeatureTrunk'
|
||||
|
||||
const DailyEntryModeEnabled = true
|
||||
|
||||
@@ -201,6 +203,9 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
const isFilesTableViewEnabled = featureTrunkEnabled(FeatureTrunkName.FilesTableView)
|
||||
const shouldHideNonApplicableOptions = isFilesTableViewEnabled && selectedTag?.uuid === SystemViewId.Files
|
||||
|
||||
return (
|
||||
<Menu className="text-sm" a11yLabel="Notes list options menu" isOpen={isOpen}>
|
||||
<div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Preferences for</div>
|
||||
@@ -283,76 +288,80 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
) : null}
|
||||
</div>
|
||||
</MenuRadioButtonItem>
|
||||
<MenuItemSeparator />
|
||||
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">View</div>
|
||||
{!isFilesSmartView && (
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideNotePreview}
|
||||
onChange={toggleHidePreview}
|
||||
>
|
||||
<div className="max-w-3/4 flex flex-col">Show note preview</div>
|
||||
</MenuSwitchButtonItem>
|
||||
{!shouldHideNonApplicableOptions && (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">View</div>
|
||||
{!isFilesSmartView && (
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideNotePreview}
|
||||
onChange={toggleHidePreview}
|
||||
>
|
||||
<div className="max-w-3/4 flex flex-col">Show note preview</div>
|
||||
</MenuSwitchButtonItem>
|
||||
)}
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideDate}
|
||||
onChange={toggleHideDate}
|
||||
>
|
||||
Show date
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideTags}
|
||||
onChange={toggleHideTags}
|
||||
>
|
||||
Show tags
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideEditorIcon}
|
||||
onChange={toggleEditorIcon}
|
||||
>
|
||||
Show icon
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuItemSeparator />
|
||||
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">Other</div>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hidePinned}
|
||||
onChange={toggleHidePinned}
|
||||
>
|
||||
Show pinned
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideProtected}
|
||||
onChange={toggleHideProtected}
|
||||
>
|
||||
Show protected
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={Boolean(preferences.showArchived)}
|
||||
onChange={toggleShowArchived}
|
||||
>
|
||||
Show archived
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={Boolean(preferences.showTrashed)}
|
||||
onChange={toggleShowTrashed}
|
||||
>
|
||||
Show trashed
|
||||
</MenuSwitchButtonItem>
|
||||
</>
|
||||
)}
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideDate}
|
||||
onChange={toggleHideDate}
|
||||
>
|
||||
Show date
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideTags}
|
||||
onChange={toggleHideTags}
|
||||
>
|
||||
Show tags
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideEditorIcon}
|
||||
onChange={toggleEditorIcon}
|
||||
>
|
||||
Show icon
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuItemSeparator />
|
||||
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">Other</div>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hidePinned}
|
||||
onChange={toggleHidePinned}
|
||||
>
|
||||
Show pinned
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={!preferences.hideProtected}
|
||||
onChange={toggleHideProtected}
|
||||
>
|
||||
Show protected
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={Boolean(preferences.showArchived)}
|
||||
onChange={toggleShowArchived}
|
||||
>
|
||||
Show archived
|
||||
</MenuSwitchButtonItem>
|
||||
<MenuSwitchButtonItem
|
||||
disabled={controlsDisabled}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
checked={Boolean(preferences.showTrashed)}
|
||||
onChange={toggleShowTrashed}
|
||||
>
|
||||
Show trashed
|
||||
</MenuSwitchButtonItem>
|
||||
|
||||
{currentMode === 'tag' && DailyEntryModeEnabled && (
|
||||
<>
|
||||
@@ -376,15 +385,18 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
<MenuItemSeparator />
|
||||
|
||||
<NewNotePreferences
|
||||
disabled={controlsDisabled}
|
||||
application={application}
|
||||
selectedTag={selectedTag}
|
||||
mode={currentMode}
|
||||
changePreferencesCallback={changePreferences}
|
||||
/>
|
||||
{!shouldHideNonApplicableOptions && (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
<NewNotePreferences
|
||||
disabled={controlsDisabled}
|
||||
application={application}
|
||||
selectedTag={selectedTag}
|
||||
mode={currentMode}
|
||||
changePreferencesCallback={changePreferences}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import RoundIconButton from '@/Components/Button/RoundIconButton'
|
||||
import ClearInputButton from '@/Components/ClearInputButton/ClearInputButton'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import DecoratedInput from '@/Components/Input/DecoratedInput'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
itemListController: ItemListController
|
||||
}
|
||||
|
||||
const SearchButton = ({ itemListController }: Props) => {
|
||||
const { noteFilterText, setNoteFilterText, clearFilterText } = itemListController
|
||||
|
||||
const [isSearchBarVisible, setIsSearchBarVisible] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
{isSearchBarVisible && (
|
||||
<DecoratedInput
|
||||
autocomplete={false}
|
||||
id={ElementIds.SearchBar}
|
||||
className={{
|
||||
container: 'px-1',
|
||||
input: 'text-base placeholder:text-passive-0 lg:text-sm',
|
||||
}}
|
||||
placeholder={'Search...'}
|
||||
value={noteFilterText}
|
||||
ref={(node) => {
|
||||
if (node && document.activeElement !== node) {
|
||||
node.focus()
|
||||
}
|
||||
}}
|
||||
onChange={(query) => setNoteFilterText(query)}
|
||||
left={[<Icon type="search" className="mr-1 h-4.5 w-4.5 flex-shrink-0 text-passive-1" />]}
|
||||
right={[noteFilterText && <ClearInputButton onClick={clearFilterText} />]}
|
||||
roundedFull
|
||||
/>
|
||||
)}
|
||||
<RoundIconButton
|
||||
onClick={() => {
|
||||
setIsSearchBarVisible(!isSearchBarVisible)
|
||||
}}
|
||||
icon={isSearchBarVisible ? 'close' : 'search'}
|
||||
label="Display options menu"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(SearchButton)
|
||||
@@ -12,10 +12,17 @@ type Props = {
|
||||
}
|
||||
hasFiles?: boolean
|
||||
hasBorder?: boolean
|
||||
isFileBackedUp?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false, hasBorder = true, className }) => {
|
||||
const ListItemFlagIcons: FunctionComponent<Props> = ({
|
||||
item,
|
||||
hasFiles = false,
|
||||
hasBorder = true,
|
||||
isFileBackedUp = false,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<div className={`flex items-start ${hasBorder && 'border-b border-solid border-border'} ${className} pl-0`}>
|
||||
{item.locked && (
|
||||
@@ -48,6 +55,11 @@ const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false, h
|
||||
<Icon ariaLabel="Starred" type="star-filled" className="text-warning" size="medium" />
|
||||
</span>
|
||||
)}
|
||||
{isFileBackedUp && (
|
||||
<span className="ml-1.5 flex items-center" title="File is backed up locally">
|
||||
<Icon ariaLabel="File is backed up locally" type="check-circle" className="text-info" size="medium" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type Props = {
|
||||
|
||||
const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, selectionController }) => {
|
||||
const { showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = filesController
|
||||
const { selectedFiles } = selectionController
|
||||
|
||||
return (
|
||||
<Popover
|
||||
@@ -25,7 +26,7 @@ const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, s
|
||||
<Menu a11yLabel="File context menu" isOpen={showFileContextMenu}>
|
||||
<FileMenuOptions
|
||||
filesController={filesController}
|
||||
selectionController={selectionController}
|
||||
selectedFiles={selectedFiles}
|
||||
closeMenu={() => setShowFileContextMenu(false)}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { FunctionComponent, useCallback, useMemo } from 'react'
|
||||
import { PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { FileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
@@ -11,27 +10,27 @@ import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import MenuItem from '../Menu/MenuItem'
|
||||
import { FileContextMenuBackupOption } from './FileContextMenuBackupOption'
|
||||
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
closeMenu: () => void
|
||||
filesController: FilesController
|
||||
selectionController: SelectedItemsController
|
||||
isFileAttachedToNote?: boolean
|
||||
renameToggleCallback?: (isRenamingFile: boolean) => void
|
||||
shouldShowRenameOption: boolean
|
||||
shouldShowAttachOption: boolean
|
||||
selectedFiles: FileItem[]
|
||||
}
|
||||
|
||||
const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
closeMenu,
|
||||
filesController,
|
||||
selectionController,
|
||||
isFileAttachedToNote,
|
||||
renameToggleCallback,
|
||||
shouldShowRenameOption,
|
||||
shouldShowAttachOption,
|
||||
selectedFiles,
|
||||
}) => {
|
||||
const { selectedFiles } = selectionController
|
||||
const { handleFileAction } = filesController
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
@@ -46,7 +45,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
const onDetach = useCallback(() => {
|
||||
const file = selectedFiles[0]
|
||||
void handleFileAction({
|
||||
type: PopoverFileItemActionType.DetachFileToNote,
|
||||
type: FileItemActionType.DetachFileToNote,
|
||||
payload: { file },
|
||||
})
|
||||
closeMenu()
|
||||
@@ -55,7 +54,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
const onAttach = useCallback(() => {
|
||||
const file = selectedFiles[0]
|
||||
void handleFileAction({
|
||||
type: PopoverFileItemActionType.AttachFileToNote,
|
||||
type: FileItemActionType.AttachFileToNote,
|
||||
payload: { file },
|
||||
})
|
||||
closeMenu()
|
||||
@@ -86,7 +85,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
<MenuSwitchButtonItem
|
||||
checked={hasProtectedFiles}
|
||||
onChange={(hasProtectedFiles) => {
|
||||
void filesController.setProtectionForFiles(hasProtectedFiles, selectionController.selectedFiles)
|
||||
void filesController.setProtectionForFiles(hasProtectedFiles, selectedFiles)
|
||||
}}
|
||||
>
|
||||
<Icon type="lock" className="mr-2 text-neutral" />
|
||||
@@ -95,7 +94,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
<HorizontalSeparator classes="my-1" />
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
void filesController.downloadFiles(selectionController.selectedFiles)
|
||||
void filesController.downloadFiles(selectedFiles)
|
||||
}}
|
||||
>
|
||||
<Icon type="download" className="mr-2 text-neutral" />
|
||||
@@ -114,7 +113,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
closeMenuAndToggleFilesList()
|
||||
void filesController.deleteFilesPermanently(selectionController.selectedFiles)
|
||||
void filesController.deleteFilesPermanently(selectedFiles)
|
||||
}}
|
||||
>
|
||||
<Icon type="trash" className="mr-2 text-danger" />
|
||||
|
||||
@@ -25,7 +25,7 @@ const FilesOptionsPanel = ({ filesController, selectionController }: Props) => {
|
||||
<Menu a11yLabel="File options panel" isOpen={isOpen}>
|
||||
<FileMenuOptions
|
||||
filesController={filesController}
|
||||
selectionController={selectionController}
|
||||
selectedFiles={selectionController.selectedFiles}
|
||||
closeMenu={() => {
|
||||
setIsOpen(false)
|
||||
}}
|
||||
|
||||
@@ -23,10 +23,6 @@ const FilePreviewModal: FunctionComponent<Props> = observer(({ application, view
|
||||
return null
|
||||
}
|
||||
|
||||
if (!application.isAuthorizedToRenderItem(currentFile)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [showFileInfoPanel, setShowFileInfoPanel] = useState(false)
|
||||
const closeButtonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
|
||||
@@ -83,7 +83,11 @@ const PreviewComponent: FunctionComponent<Props> = ({
|
||||
}
|
||||
|
||||
if (file.mimeType.startsWith('video/')) {
|
||||
return <video className="h-full w-full" src={objectUrl} controls autoPlay />
|
||||
return (
|
||||
<video className="h-full w-full" controls autoPlay>
|
||||
<source src={objectUrl} type={file.mimeType} />
|
||||
</video>
|
||||
)
|
||||
}
|
||||
|
||||
if (file.mimeType.startsWith('audio/')) {
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { formatDateForContextMenu } from '@/Utils/DateUtils'
|
||||
import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import {
|
||||
ContentType,
|
||||
FileItem,
|
||||
SortableItem,
|
||||
PrefKey,
|
||||
ApplicationEvent,
|
||||
naturalSort,
|
||||
FileBackupRecord,
|
||||
} from '@standardnotes/snjs'
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from 'react'
|
||||
import { FileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { getFileIconComponent } from '../FilePreview/getFileIconComponent'
|
||||
import Popover from '../Popover/Popover'
|
||||
import Table from '../Table/Table'
|
||||
import { TableColumn } from '../Table/CommonTypes'
|
||||
import { useTable } from '../Table/useTable'
|
||||
import Menu from '../Menu/Menu'
|
||||
import FileMenuOptions from '../FileContextMenu/FileMenuOptions'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
|
||||
import LinkedItemBubble from '../LinkedItems/LinkedItemBubble'
|
||||
import LinkedItemsPanel from '../LinkedItems/LinkedItemsPanel'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
const ContextMenuCell = ({ files, filesController }: { files: FileItem[]; filesController: FilesController }) => {
|
||||
const [contextMenuVisible, setContextMenuVisible] = useState(false)
|
||||
const anchorElementRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="rounded-full border border-border bg-default p-1"
|
||||
ref={anchorElementRef}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setContextMenuVisible((visible) => !visible)
|
||||
}}
|
||||
>
|
||||
<Icon type="more" />
|
||||
</button>
|
||||
<Popover
|
||||
open={contextMenuVisible}
|
||||
anchorElement={anchorElementRef.current}
|
||||
togglePopover={() => {
|
||||
setContextMenuVisible(false)
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<Menu a11yLabel="File context menu" isOpen={contextMenuVisible}>
|
||||
<FileMenuOptions
|
||||
closeMenu={() => {
|
||||
setContextMenuVisible(false)
|
||||
}}
|
||||
filesController={filesController}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
selectedFiles={files}
|
||||
/>
|
||||
</Menu>
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const FileLinksCell = ({
|
||||
file,
|
||||
filesController,
|
||||
linkingController,
|
||||
featuresController,
|
||||
}: {
|
||||
file: FileItem
|
||||
filesController: FilesController
|
||||
linkingController: LinkingController
|
||||
featuresController: FeaturesController
|
||||
}) => {
|
||||
const [contextMenuVisible, setContextMenuVisible] = useState(false)
|
||||
const anchorElementRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="rounded-full border border-border bg-default p-1"
|
||||
ref={anchorElementRef}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setContextMenuVisible((visible) => !visible)
|
||||
}}
|
||||
>
|
||||
<Icon type="link" />
|
||||
</button>
|
||||
<Popover
|
||||
open={contextMenuVisible}
|
||||
anchorElement={anchorElementRef.current}
|
||||
togglePopover={() => {
|
||||
setContextMenuVisible(false)
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<LinkedItemsPanel
|
||||
linkingController={linkingController}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
isOpen={contextMenuVisible}
|
||||
item={file}
|
||||
/>
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const FileNameCell = ({ file }: { file: FileItem }) => {
|
||||
const application = useApplication()
|
||||
const [backupInfo, setBackupInfo] = useState<FileBackupRecord | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
void application.fileBackups?.getFileBackupInfo(file).then(setBackupInfo)
|
||||
}, [application, file])
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 whitespace-normal">
|
||||
<span className="relative">
|
||||
{getFileIconComponent(getIconForFileType(file.mimeType), 'w-6 h-6 flex-shrink-0')}
|
||||
{backupInfo && (
|
||||
<div
|
||||
className="absolute bottom-1 right-1 translate-x-1/2 translate-y-1/2 rounded-full bg-default text-success"
|
||||
title="File is backed up locally"
|
||||
>
|
||||
<Icon size="small" type="check-circle-filled" />
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-sm font-medium">{file.title}</span>
|
||||
{file.protected && (
|
||||
<span className="flex items-center" title="File is protected">
|
||||
<Icon ariaLabel="File is protected" type="lock-filled" className="h-3.5 w-3.5 text-passive-1" size="custom" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
filesController: FilesController
|
||||
featuresController: FeaturesController
|
||||
linkingController: LinkingController
|
||||
}
|
||||
|
||||
const FilesTableView = ({ application, filesController, featuresController, linkingController }: Props) => {
|
||||
const files = application.items
|
||||
.getDisplayableNotesAndFiles()
|
||||
.filter((item) => item.content_type === ContentType.File) as FileItem[]
|
||||
|
||||
const [sortBy, setSortBy] = useState<keyof SortableItem>(
|
||||
application.getPreference(PrefKey.SortNotesBy, PrefDefaults[PrefKey.SortNotesBy]),
|
||||
)
|
||||
const [sortReversed, setSortReversed] = useState(
|
||||
application.getPreference(PrefKey.SortNotesReverse, PrefDefaults[PrefKey.SortNotesReverse]),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
return application.addEventObserver(async (event) => {
|
||||
if (event === ApplicationEvent.PreferencesChanged) {
|
||||
setSortBy(application.getPreference(PrefKey.SortNotesBy, PrefDefaults[PrefKey.SortNotesBy]))
|
||||
setSortReversed(application.getPreference(PrefKey.SortNotesReverse, PrefDefaults[PrefKey.SortNotesReverse]))
|
||||
}
|
||||
})
|
||||
}, [application])
|
||||
|
||||
const onSortChange = useCallback(
|
||||
async (sortBy: keyof SortableItem, sortReversed: boolean) => {
|
||||
await application.setPreference(PrefKey.SortNotesBy, sortBy)
|
||||
await application.setPreference(PrefKey.SortNotesReverse, sortReversed)
|
||||
},
|
||||
[application],
|
||||
)
|
||||
|
||||
const [contextMenuFile, setContextMenuFile] = useState<FileItem | undefined>(undefined)
|
||||
const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | undefined>(undefined)
|
||||
|
||||
const isSmallBreakpoint = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
const isMediumBreakpoint = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.md)
|
||||
const isLargeBreakpoint = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.lg)
|
||||
|
||||
const columnDefs: TableColumn<FileItem>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
name: 'Name',
|
||||
sortBy: 'title',
|
||||
cell: (file) => <FileNameCell file={file} />,
|
||||
},
|
||||
{
|
||||
name: 'Upload date',
|
||||
sortBy: 'created_at',
|
||||
cell: (file) => {
|
||||
return formatDateForContextMenu(file.created_at)
|
||||
},
|
||||
hidden: isSmallBreakpoint,
|
||||
},
|
||||
{
|
||||
name: 'Size',
|
||||
cell: (file) => {
|
||||
return formatSizeToReadableString(file.decryptedSize)
|
||||
},
|
||||
hidden: isSmallBreakpoint,
|
||||
},
|
||||
{
|
||||
name: 'Attached to',
|
||||
hidden: isSmallBreakpoint || isMediumBreakpoint || isLargeBreakpoint,
|
||||
cell: (file) => {
|
||||
const links = [
|
||||
...naturalSort(application.items.referencesForItem(file), 'title').map((item) =>
|
||||
createLinkFromItem(item, 'linked'),
|
||||
),
|
||||
...naturalSort(application.items.itemsReferencingItem(file), 'title').map((item) =>
|
||||
createLinkFromItem(item, 'linked-by'),
|
||||
),
|
||||
...application.items.getSortedTagsForItem(file).map((item) => createLinkFromItem(item, 'linked')),
|
||||
]
|
||||
|
||||
if (!links.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkedItemBubble
|
||||
className="overflow-hidden"
|
||||
link={links[0]}
|
||||
key={links[0].id}
|
||||
unlinkItem={async (itemToUnlink) => {
|
||||
void application.items.unlinkItems(file, itemToUnlink)
|
||||
}}
|
||||
isBidirectional={false}
|
||||
/>
|
||||
{links.length > 1 && <span>and {links.length - 1} more...</span>}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
[application.items, isLargeBreakpoint, isMediumBreakpoint, isSmallBreakpoint],
|
||||
)
|
||||
|
||||
const getRowId = useCallback((file: FileItem) => file.uuid, [])
|
||||
|
||||
const table = useTable({
|
||||
data: files,
|
||||
sortBy,
|
||||
sortReversed,
|
||||
onSortChange,
|
||||
getRowId,
|
||||
columns: columnDefs,
|
||||
enableRowSelection: true,
|
||||
enableMultipleRowSelection: true,
|
||||
onRowDoubleClick(file) {
|
||||
void filesController.handleFileAction({
|
||||
type: FileItemActionType.PreviewFile,
|
||||
payload: {
|
||||
file,
|
||||
otherFiles: files,
|
||||
},
|
||||
})
|
||||
},
|
||||
onRowContextMenu(x, y, file) {
|
||||
setContextMenuPosition({ x, y })
|
||||
setContextMenuFile(file)
|
||||
},
|
||||
rowActions: (file) => {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<FileLinksCell
|
||||
file={file}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
linkingController={linkingController}
|
||||
/>
|
||||
<ContextMenuCell files={[file]} filesController={filesController} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
selectionActions: (fileIds) => (
|
||||
<ContextMenuCell files={files.filter((file) => fileIds.includes(file.uuid))} filesController={filesController} />
|
||||
),
|
||||
showSelectionActions: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table table={table} />
|
||||
{contextMenuPosition && contextMenuFile && (
|
||||
<Popover
|
||||
open={true}
|
||||
anchorPoint={contextMenuPosition}
|
||||
togglePopover={() => {
|
||||
setContextMenuPosition(undefined)
|
||||
setContextMenuFile(undefined)
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<Menu a11yLabel="File context menu" isOpen={true}>
|
||||
<FileMenuOptions
|
||||
closeMenu={() => {
|
||||
setContextMenuPosition(undefined)
|
||||
setContextMenuFile(undefined)
|
||||
}}
|
||||
filesController={filesController}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
selectedFiles={[contextMenuFile]}
|
||||
/>
|
||||
</Menu>
|
||||
</Popover>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default FilesTableView
|
||||
@@ -2,17 +2,20 @@ import * as icons from '@standardnotes/icons'
|
||||
|
||||
export const IconNameToSvgMapping = {
|
||||
'account-circle': icons.AccountCircleIcon,
|
||||
'arrow-down': icons.ArrowDownIcon,
|
||||
'arrow-left': icons.ArrowLeftIcon,
|
||||
'arrow-right': icons.ArrowRightIcon,
|
||||
'arrow-up': icons.ArrowUpIcon,
|
||||
'arrows-sort-down': icons.ArrowsSortDownIcon,
|
||||
'arrows-sort-up': icons.ArrowsSortUpIcon,
|
||||
'attachment-file': icons.AttachmentFileIcon,
|
||||
'check-bold': icons.CheckBoldIcon,
|
||||
'check-circle': icons.CheckCircleIcon,
|
||||
'chevron-up': icons.ChevronUpIcon,
|
||||
'check-circle-filled': icons.CheckCircleFilledIcon,
|
||||
'chevron-down': icons.ChevronDownIcon,
|
||||
'chevron-left': icons.ChevronLeftIcon,
|
||||
'chevron-right': icons.ChevronRightIcon,
|
||||
'chevron-up': icons.ChevronUpIcon,
|
||||
'clear-circle-filled': icons.ClearCircleFilledIcon,
|
||||
'cloud-off': icons.CloudOffIcon,
|
||||
'diamond-filled': icons.DiamondFilledIcon,
|
||||
@@ -26,6 +29,11 @@ export const IconNameToSvgMapping = {
|
||||
'file-ppt': icons.FilePptIcon,
|
||||
'file-xls': icons.FileXlsIcon,
|
||||
'file-zip': icons.FileZipIcon,
|
||||
'format-align-center': icons.FormatAlignCenterIcon,
|
||||
'format-align-justify': icons.FormatAlignJustifyIcon,
|
||||
'format-align-left': icons.FormatAlignLeftIcon,
|
||||
'format-align-right': icons.FormatAlignRightIcon,
|
||||
'fullscreen-exit': icons.FullscreenExitIcon,
|
||||
'hashtag-off': icons.HashtagOffIcon,
|
||||
'link-off': icons.LinkOffIcon,
|
||||
'list-bulleted': icons.ListBulleted,
|
||||
@@ -36,27 +44,22 @@ export const IconNameToSvgMapping = {
|
||||
'menu-close': icons.MenuCloseIcon,
|
||||
'menu-variant': icons.MenuVariantIcon,
|
||||
'notes-filled': icons.NotesFilledIcon,
|
||||
'open-in': icons.OpenInIcon,
|
||||
'pencil-filled': icons.PencilFilledIcon,
|
||||
'pencil-off': icons.PencilOffIcon,
|
||||
'pin-filled': icons.PinFilledIcon,
|
||||
'plain-text': icons.PlainTextIcon,
|
||||
'premium-feature': icons.PremiumFeatureIcon,
|
||||
'rich-text': icons.RichTextIcon,
|
||||
'safe-square': icons.SafeSquareIcon,
|
||||
'sort-descending': icons.SortDescendingIcon,
|
||||
'star-circle-filled': icons.StarCircleFilled,
|
||||
'star-filled': icons.StarFilledIcon,
|
||||
'star-variant-filled': icons.StarVariantFilledIcon,
|
||||
'safe-square': icons.SafeSquareIcon,
|
||||
'trash-filled': icons.TrashFilledIcon,
|
||||
'trash-sweep': icons.TrashSweepIcon,
|
||||
'user-add': icons.UserAddIcon,
|
||||
'user-switch': icons.UserSwitch,
|
||||
'fullscreen-exit': icons.FullscreenExitIcon,
|
||||
'format-align-left': icons.FormatAlignLeftIcon,
|
||||
'format-align-center': icons.FormatAlignCenterIcon,
|
||||
'format-align-right': icons.FormatAlignRightIcon,
|
||||
'format-align-justify': icons.FormatAlignJustifyIcon,
|
||||
drag: icons.DragIcon,
|
||||
accessibility: icons.AccessibilityIcon,
|
||||
add: icons.AddIcon,
|
||||
archive: icons.ArchiveIcon,
|
||||
@@ -70,6 +73,7 @@ export const IconNameToSvgMapping = {
|
||||
dashboard: icons.DashboardIcon,
|
||||
diamond: icons.DiamondIcon,
|
||||
download: icons.DownloadIcon,
|
||||
drag: icons.DragIcon,
|
||||
editor: icons.EditorIcon,
|
||||
email: icons.EmailIcon,
|
||||
eye: icons.EyeIcon,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { useState } from 'react'
|
||||
import { PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { FileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { FileContextMenuBackupOption } from '../FileContextMenu/FileContextMenuBackupOption'
|
||||
import Icon from '../Icon/Icon'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
@@ -24,7 +24,7 @@ const LinkedFileMenuOptions = ({ file, closeMenu, handleFileAction, setIsRenamin
|
||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
||||
onClick={() => {
|
||||
void handleFileAction({
|
||||
type: PopoverFileItemActionType.PreviewFile,
|
||||
type: FileItemActionType.PreviewFile,
|
||||
payload: {
|
||||
file,
|
||||
otherFiles: [],
|
||||
@@ -41,7 +41,7 @@ const LinkedFileMenuOptions = ({ file, closeMenu, handleFileAction, setIsRenamin
|
||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.ToggleFileProtection,
|
||||
type: FileItemActionType.ToggleFileProtection,
|
||||
payload: { file },
|
||||
callback: (isProtected: boolean) => {
|
||||
setIsFileProtected(isProtected)
|
||||
@@ -60,7 +60,7 @@ const LinkedFileMenuOptions = ({ file, closeMenu, handleFileAction, setIsRenamin
|
||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.DownloadFile,
|
||||
type: FileItemActionType.DownloadFile,
|
||||
payload: { file },
|
||||
}).catch(console.error)
|
||||
closeMenu()
|
||||
@@ -83,7 +83,7 @@ const LinkedFileMenuOptions = ({ file, closeMenu, handleFileAction, setIsRenamin
|
||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
||||
onClick={() => {
|
||||
handleFileAction({
|
||||
type: PopoverFileItemActionType.DeleteFile,
|
||||
type: FileItemActionType.DeleteFile,
|
||||
payload: { file },
|
||||
}).catch(console.error)
|
||||
closeMenu()
|
||||
|
||||
@@ -11,7 +11,6 @@ import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||
import { FOCUS_TAGS_INPUT_COMMAND, keyboardStringForShortcut } from '@standardnotes/ui-services'
|
||||
import { useCommandService } from '../CommandProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { useItemLinks } from '@/Hooks/useItemLinks'
|
||||
|
||||
type Props = {
|
||||
@@ -19,14 +18,11 @@ type Props = {
|
||||
}
|
||||
|
||||
const LinkedItemBubblesContainer = ({ linkingController }: Props) => {
|
||||
const application = useApplication()
|
||||
const activeItem = application.itemControllerGroup.activeItemViewController?.item
|
||||
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
const commandService = useCommandService()
|
||||
|
||||
const { unlinkItemFromSelectedItem: unlinkItem, activateItem } = linkingController
|
||||
const { activeItem, unlinkItemFromSelectedItem: unlinkItem, activateItem } = linkingController
|
||||
|
||||
const { notesLinkedToItem, filesLinkedToItem, tagsLinkedToItem, notesLinkingToItem, filesLinkingToItem } =
|
||||
useItemLinks(activeItem)
|
||||
|
||||
@@ -3,7 +3,6 @@ import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useRef, useCallback } from 'react'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
import Popover from '../Popover/Popover'
|
||||
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||
@@ -17,10 +16,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const LinkedItemsButton = ({ linkingController, filesController, onClickPreprocessing, featuresController }: Props) => {
|
||||
const application = useApplication()
|
||||
const activeItem = application.itemControllerGroup.activeItemViewController?.item
|
||||
|
||||
const { isLinkingPanelOpen, setIsLinkingPanelOpen } = linkingController
|
||||
const { activeItem, isLinkingPanelOpen, setIsLinkingPanelOpen } = linkingController
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const toggleMenu = useCallback(async () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import DecoratedInput from '../Input/DecoratedInput'
|
||||
import LinkedItemSearchResults from './LinkedItemSearchResults'
|
||||
import { LinkedItemsSectionItem } from './LinkedItemsSectionItem'
|
||||
import { DecryptedItem } from '@standardnotes/snjs'
|
||||
import { useItemLinks } from '@/Hooks/useItemLinks'
|
||||
|
||||
const LinkedItemsPanel = ({
|
||||
linkingController,
|
||||
@@ -27,22 +28,10 @@ const LinkedItemsPanel = ({
|
||||
isOpen: boolean
|
||||
item: DecryptedItem
|
||||
}) => {
|
||||
const {
|
||||
getLinkedTagsForItem,
|
||||
getFilesLinksForItem,
|
||||
getLinkedNotesForItem,
|
||||
getNotesLinkingToItem,
|
||||
linkItems,
|
||||
unlinkItemFromSelectedItem,
|
||||
activateItem,
|
||||
createAndAddNewTag,
|
||||
isEntitledToNoteLinking,
|
||||
} = linkingController
|
||||
const { linkItems, unlinkItems, activateItem, createAndAddNewTag, isEntitledToNoteLinking } = linkingController
|
||||
|
||||
const tagsLinkedToItem = getLinkedTagsForItem(item) || []
|
||||
const { filesLinkedToItem, filesLinkingToItem } = getFilesLinksForItem(item)
|
||||
const notesLinkedToItem = getLinkedNotesForItem(item) || []
|
||||
const notesLinkingToItem = getNotesLinkingToItem(item) || []
|
||||
const { notesLinkedToItem, notesLinkingToItem, filesLinkedToItem, filesLinkingToItem, tagsLinkedToItem } =
|
||||
useItemLinks(item)
|
||||
|
||||
const { entitledToFiles } = featuresController
|
||||
const application = useApplication()
|
||||
@@ -128,7 +117,7 @@ const LinkedItemsPanel = ({
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItemFromSelectedItem(link.item)}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
/>
|
||||
@@ -148,7 +137,7 @@ const LinkedItemsPanel = ({
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItemFromSelectedItem(link.item)}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
/>
|
||||
@@ -172,7 +161,7 @@ const LinkedItemsPanel = ({
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItemFromSelectedItem(link.item)}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
/>
|
||||
@@ -191,7 +180,7 @@ const LinkedItemsPanel = ({
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItemFromSelectedItem(link.item)}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
/>
|
||||
@@ -208,7 +197,7 @@ const LinkedItemsPanel = ({
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItemFromSelectedItem(link.item)}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
/>
|
||||
@@ -227,7 +216,7 @@ const LinkedItemsPanel = ({
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItemFromSelectedItem(link.item)}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
/>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { FileItem } from '@standardnotes/snjs'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { FileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import Icon from '../Icon/Icon'
|
||||
import MenuItem from '../Menu/MenuItem'
|
||||
import Popover from '../Popover/Popover'
|
||||
@@ -46,7 +46,7 @@ export const LinkedItemsSectionItem = ({
|
||||
return
|
||||
}
|
||||
await handleFileAction({
|
||||
type: PopoverFileItemActionType.RenameFile,
|
||||
type: FileItemActionType.RenameFile,
|
||||
payload: {
|
||||
file: item,
|
||||
name: name,
|
||||
|
||||
@@ -235,10 +235,6 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
|
||||
const isTemplate = this.isTemplateNote
|
||||
|
||||
if (typeof document !== 'undefined' && document.hidden) {
|
||||
void this.application.alertService.alert(InfoStrings.SavingWhileDocumentHidden)
|
||||
}
|
||||
|
||||
if (isTemplate) {
|
||||
await this.insertTemplatedNote()
|
||||
}
|
||||
|
||||
+1
-13
@@ -36,8 +36,6 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
|
||||
minLength: 0,
|
||||
})
|
||||
|
||||
const [popoverOpen, setPopoverOpen] = useState(true)
|
||||
|
||||
const options = useMemo(() => {
|
||||
const indentOutdentOptions = application.isNativeMobileWeb() ? GetIndentOutdentBlocks(editor) : []
|
||||
|
||||
@@ -90,7 +88,6 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
|
||||
nodeToRemove.remove()
|
||||
}
|
||||
selectedOption.onSelect(matchingString)
|
||||
setPopoverOpen(false)
|
||||
closeMenu()
|
||||
})
|
||||
},
|
||||
@@ -105,12 +102,6 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
|
||||
onSelectOption={onSelectOption}
|
||||
triggerFn={checkForTriggerMatch}
|
||||
options={options}
|
||||
onClose={() => {
|
||||
setPopoverOpen(false)
|
||||
}}
|
||||
onOpen={() => {
|
||||
setPopoverOpen(true)
|
||||
}}
|
||||
menuRenderFn={(anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => {
|
||||
if (!anchorElementRef.current || !options.length) {
|
||||
return null
|
||||
@@ -120,10 +111,7 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
|
||||
<Popover
|
||||
align="start"
|
||||
anchorElement={anchorElementRef.current}
|
||||
open={popoverOpen}
|
||||
togglePopover={() => {
|
||||
setPopoverOpen((prevValue) => !prevValue)
|
||||
}}
|
||||
open={true}
|
||||
disableMobileFullscreenTakeover={true}
|
||||
side={isMobileScreen() ? 'top' : 'bottom'}
|
||||
maxHeight={(mh) => mh / 2}
|
||||
|
||||
+1
-13
@@ -31,8 +31,6 @@ export const ItemSelectionPlugin: FunctionComponent<Props> = ({ currentNote }) =
|
||||
minLength: 0,
|
||||
})
|
||||
|
||||
const [popoverOpen, setPopoverOpen] = useState(true)
|
||||
|
||||
const onSelectOption = useCallback(
|
||||
(selectedOption: ItemOption, nodeToRemove: TextNode | null, closeMenu: () => void, matchingString: string) => {
|
||||
editor.update(() => {
|
||||
@@ -40,7 +38,6 @@ export const ItemSelectionPlugin: FunctionComponent<Props> = ({ currentNote }) =
|
||||
nodeToRemove.remove()
|
||||
}
|
||||
selectedOption.options.onSelect(matchingString)
|
||||
setPopoverOpen(false)
|
||||
closeMenu()
|
||||
})
|
||||
},
|
||||
@@ -92,12 +89,6 @@ export const ItemSelectionPlugin: FunctionComponent<Props> = ({ currentNote }) =
|
||||
onSelectOption={onSelectOption}
|
||||
triggerFn={checkForTriggerMatch}
|
||||
options={options}
|
||||
onClose={() => {
|
||||
setPopoverOpen(false)
|
||||
}}
|
||||
onOpen={() => {
|
||||
setPopoverOpen(true)
|
||||
}}
|
||||
menuRenderFn={(anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => {
|
||||
if (!anchorElementRef.current || !options.length) {
|
||||
return null
|
||||
@@ -107,10 +98,7 @@ export const ItemSelectionPlugin: FunctionComponent<Props> = ({ currentNote }) =
|
||||
<Popover
|
||||
align="start"
|
||||
anchorElement={anchorElementRef.current}
|
||||
open={popoverOpen}
|
||||
togglePopover={() => {
|
||||
setPopoverOpen((prevValue) => !prevValue)
|
||||
}}
|
||||
open={true}
|
||||
disableMobileFullscreenTakeover={true}
|
||||
side={isMobileScreen() ? 'top' : 'bottom'}
|
||||
maxHeight={(mh) => mh / 2}
|
||||
|
||||
@@ -28,6 +28,8 @@ export async function animatePaneEntranceTransitionFromOffscreenToTheRight(eleme
|
||||
|
||||
await animation.finished
|
||||
|
||||
performSafariAnimationFix(element)
|
||||
|
||||
animation.finish()
|
||||
}
|
||||
|
||||
@@ -58,3 +60,21 @@ export async function animatePaneExitTransitionOffscreenToTheRight(elementId: st
|
||||
|
||||
animation.finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* Safari has a bug where animations don't properly commit sometimes and a tap on the screen or click anywhere fixes it.
|
||||
* The workaround here is just to get Safari to recompute the element layout. This issue manifests itself when selecting
|
||||
* a Daily Notebook tag from navigation whereby two pane animations are simulatensouly triggered (the items panel, then
|
||||
* the editor panel), causing Safari to get tripped up.
|
||||
*/
|
||||
function performSafariAnimationFix(element: HTMLElement): void {
|
||||
const isSafari = /Safari/.test(navigator.userAgent) || /AppleWebKit/.test(navigator.userAgent)
|
||||
if (!isSafari) {
|
||||
return
|
||||
}
|
||||
|
||||
element.style.opacity = '0.999'
|
||||
setTimeout(() => {
|
||||
element.style.opacity = '1.0'
|
||||
}, 0)
|
||||
}
|
||||
|
||||
@@ -303,6 +303,7 @@ const PanesSystemComponent = () => {
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
searchOptionsController={viewControllerManager.searchOptionsController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
>
|
||||
{showPanelResizers && listRef && (
|
||||
<PanelResizer
|
||||
|
||||
@@ -75,7 +75,7 @@ const Popover = ({
|
||||
|
||||
if (open) {
|
||||
removeListener = addAndroidBackHandler(() => {
|
||||
togglePopover()
|
||||
togglePopover?.()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ const PositionedPopoverContent = ({
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.stopPropagation()
|
||||
togglePopover()
|
||||
togglePopover?.()
|
||||
if (anchorElement) {
|
||||
anchorElement.focus()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user