From 9f11b02e4aeeb20e91540a2ef26158ac7319e6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Fri, 20 May 2022 13:57:01 +0200 Subject: [PATCH 01/39] fix: upgrade deps to introduce PKCE sign in --- .../javascripts/Services/AlertService.ts | 3 +- package.json | 5 +- yarn.lock | 80 ++++++++++++------- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/app/assets/javascripts/Services/AlertService.ts b/app/assets/javascripts/Services/AlertService.ts index b436113fa..125374d56 100644 --- a/app/assets/javascripts/Services/AlertService.ts +++ b/app/assets/javascripts/Services/AlertService.ts @@ -1,4 +1,5 @@ -import { AlertService, ButtonType, sanitizeHtmlString } from '@standardnotes/snjs' +import { ButtonType, sanitizeHtmlString } from '@standardnotes/snjs' +import { AlertService } from '@standardnotes/services' import { SKAlert } from '@standardnotes/stylekit' /** @returns a promise resolving to true if the user confirmed, false if they canceled */ diff --git a/package.json b/package.json index 63b398632..858c7fe1f 100644 --- a/package.json +++ b/package.json @@ -72,8 +72,9 @@ "@standardnotes/components": "1.8.1", "@standardnotes/filepicker": "1.14.9", "@standardnotes/icons": "^1.1.7", - "@standardnotes/sncrypto-web": "1.9.2", - "@standardnotes/snjs": "2.109.5", + "@standardnotes/services": "^1.12.0", + "@standardnotes/sncrypto-web": "1.10.0", + "@standardnotes/snjs": "2.110.0", "@standardnotes/stylekit": "5.27.1", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", diff --git a/yarn.lock b/yarn.lock index 2d6cd3d93..580b4b455 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2311,14 +2311,14 @@ "@standardnotes/auth" "^3.18.16" "@standardnotes/features" "^1.44.0" -"@standardnotes/encryption@^1.7.9": - version "1.7.9" - resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.7.9.tgz#4a66fcdeca8957f4a29ed327cdc677174cf904f7" - integrity sha512-Mqt8XPOXdbn4jbHnORqLsqeqKXAc5PILo1sO51J/lCODxjHAbvnCo+ZQ5mbGzalAp6sZTQ5J+RkwKDnINlzhNA== +"@standardnotes/encryption@^1.7.10": + version "1.7.10" + resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.7.10.tgz#793e9337c7516995f56edd80515dad9860a95b56" + integrity sha512-3F5MADNq9EbhouSKI5CrvqbH78zUcCJNP+HnOJPclUtXGbnIr2X70EuRPgeNjQs62179RnCjiN5zVwc+6URO3g== dependencies: "@standardnotes/models" "^1.8.6" "@standardnotes/responses" "^1.6.23" - "@standardnotes/services" "^1.11.9" + "@standardnotes/services" "^1.12.0" "@standardnotes/features@^1.44.0": version "1.44.0" @@ -2328,7 +2328,7 @@ "@standardnotes/auth" "^3.18.16" "@standardnotes/common" "^1.21.0" -"@standardnotes/filepicker@1.14.9", "@standardnotes/filepicker@^1.14.9": +"@standardnotes/filepicker@1.14.9": version "1.14.9" resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.14.9.tgz#daf1586a7147166dba259a04a17248c69e5b12ca" integrity sha512-Iv0H3NeTmO88k8oesPJY3LRuhfOyyvh4pYp+eD19he91JHNK87K97fdmIc2fa2JkFccW1TthX+1VP/nbUt3YyQ== @@ -2337,15 +2337,24 @@ "@standardnotes/services" "^1.11.9" "@standardnotes/utils" "^1.6.9" -"@standardnotes/files@^1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.1.10.tgz#caa98571cd93588949cd13b8264f5a99f70f372b" - integrity sha512-xdq/aC6x8vh8c97L2eNpBSJwmZwuXKdEveHl+KAOxPvRWMyLm8RLtTBME0QtdmfI2ZwkdJiiR4VK+8ZYNMYUsA== +"@standardnotes/filepicker@^1.14.10": + version "1.14.10" + resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.14.10.tgz#4452120d6296839bfe25f6c564fbcd04c7ff2159" + integrity sha512-srSzW/ciS4Tuj/GV3Ox1+EyZC5DU17SVtmR8YNO4sDvjq4LwCTB+9+IkKA2pY0IcOyeHtjvOh2VsoqsNiZr9DQ== dependencies: - "@standardnotes/encryption" "^1.7.9" + "@standardnotes/common" "^1.21.0" + "@standardnotes/services" "^1.12.0" + "@standardnotes/utils" "^1.6.9" + +"@standardnotes/files@^1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.1.11.tgz#1d1a00ed047ec54e30f4b3a000c8b970c1d2a562" + integrity sha512-E/Ap3yoU21oT/dT4Fv++3oPEd7Vt7+v+oE6CambhwHj4ziq8rgl47zVkIfXbvdWmfJTbejdTxb+aCBE984HQTw== + dependencies: + "@standardnotes/encryption" "^1.7.10" "@standardnotes/models" "^1.8.6" "@standardnotes/responses" "^1.6.23" - "@standardnotes/services" "^1.11.9" + "@standardnotes/services" "^1.12.0" "@standardnotes/utils" "^1.6.9" "@standardnotes/icons@^1.1.7": @@ -2382,42 +2391,53 @@ "@standardnotes/responses" "^1.6.23" "@standardnotes/utils" "^1.6.9" +"@standardnotes/services@^1.12.0": + version "1.12.0" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.12.0.tgz#4f0e7f95746780a477961a4033748e0f879cc1c8" + integrity sha512-2jV/U4RJhBMfRe/hvxdzghANd/yqF4pV7WeXj8v7iijTdkrb79zPlszdWtOoUHw4/HDAp4755BMTm/Pf99BL3A== + dependencies: + "@standardnotes/auth" "^3.18.16" + "@standardnotes/common" "^1.21.0" + "@standardnotes/models" "^1.8.6" + "@standardnotes/responses" "^1.6.23" + "@standardnotes/utils" "^1.6.9" + "@standardnotes/settings@^1.14.3": version "1.14.3" resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.14.3.tgz#021085e8c383a9893a2c49daa74cc0754ccd67b5" integrity sha512-wJQ4apQVXScYcwZbJJoEe1LFx9DMqlp0NKOXGzeL6eNIqAvMQJvLtIdgp/jx2OSJKzdRV5/TYqwVnDMcFdRfCg== -"@standardnotes/sncrypto-common@^1.8.2": - version "1.8.2" - resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-common/-/sncrypto-common-1.8.2.tgz#3fd8a4a110e3c8981538fcb470ce6f2c19a01954" - integrity sha512-2tlXpCaAnvNjj6gXRQnsuo/acb7Q7i9RqBBv3PV8O+rdKnSfQOH/6973+3FxOsjTRcvrQVA2oAuw1+/PzLYXUg== +"@standardnotes/sncrypto-common@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-common/-/sncrypto-common-1.9.0.tgz#7c7387357fbd95f6121c35c88c53d5c72203fe22" + integrity sha512-EoPwt0gU0kDU1feyumXU3neSaNQivC2+LVQp8C5kDYi6OrrLPzq3dGPc21GFLKinAdcZvjfLc/RkFo6b/CEnAA== -"@standardnotes/sncrypto-web@1.9.2": - version "1.9.2" - resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-web/-/sncrypto-web-1.9.2.tgz#3e2ba32ae997ebc129d82c50292847de0d7288c4" - integrity sha512-wrxKmqMrnFQ+K+fxlbRILk84mlBRXrGoVStnRFDCXTpOvrOwxei5GG4hq2w8C5Yb87nEWkIJkJeamr4lvwtoLg== +"@standardnotes/sncrypto-web@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-web/-/sncrypto-web-1.10.0.tgz#e30821c724d12d866c076df1293f6b2e6a0bf9ce" + integrity sha512-nRHLfEXG/+/lUP6yDfuwVAw8yq+jjcixQoyFf8nYQgBecqixmVbQdtRJd7KVRzQjqIVNt0elYuZuvoG/om01yw== dependencies: - "@standardnotes/sncrypto-common" "^1.8.2" + "@standardnotes/sncrypto-common" "^1.9.0" buffer "^6.0.3" libsodium-wrappers "^0.7.9" -"@standardnotes/snjs@2.109.5": - version "2.109.5" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.109.5.tgz#e11c8a1d86b25957dcb09664fa37344321c1f0bd" - integrity sha512-BfGvsE7sthKlJ+RSBqPxDH3+chyYHkzcReQWoXOKPNbimQlG3AWk/aFvf+ypRYPB6CkmlKD32s0cSFhQIl3dMQ== +"@standardnotes/snjs@2.110.0": + version "2.110.0" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.110.0.tgz#463864bc7399a35bbf6d02d6251f6d7662cacd07" + integrity sha512-g24q4MtJluRYEq2kiEYoiWC1M9OiVb2QKHn90OqFaK10zKA1UjW71ns56mWIpF6eZy4vA8P1Mp+QYiUi3jgupw== dependencies: "@standardnotes/auth" "^3.18.16" "@standardnotes/common" "^1.21.0" "@standardnotes/domain-events" "^2.28.7" - "@standardnotes/encryption" "^1.7.9" + "@standardnotes/encryption" "^1.7.10" "@standardnotes/features" "^1.44.0" - "@standardnotes/filepicker" "^1.14.9" - "@standardnotes/files" "^1.1.10" + "@standardnotes/filepicker" "^1.14.10" + "@standardnotes/files" "^1.1.11" "@standardnotes/models" "^1.8.6" "@standardnotes/responses" "^1.6.23" - "@standardnotes/services" "^1.11.9" + "@standardnotes/services" "^1.12.0" "@standardnotes/settings" "^1.14.3" - "@standardnotes/sncrypto-common" "^1.8.2" + "@standardnotes/sncrypto-common" "^1.9.0" "@standardnotes/utils" "^1.6.9" "@standardnotes/stylekit@5.27.1": From e9aafbbe000dfc3e8f79f4a5d36ec805fa789b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Fri, 20 May 2022 15:04:55 +0200 Subject: [PATCH 02/39] fix: upgrade sncrypto-web for base64 url encode without padding --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 858c7fe1f..75fb38ed0 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@standardnotes/filepicker": "1.14.9", "@standardnotes/icons": "^1.1.7", "@standardnotes/services": "^1.12.0", - "@standardnotes/sncrypto-web": "1.10.0", + "@standardnotes/sncrypto-web": "1.10.1", "@standardnotes/snjs": "2.110.0", "@standardnotes/stylekit": "5.27.1", "@zip.js/zip.js": "^2.4.10", diff --git a/yarn.lock b/yarn.lock index 580b4b455..f3ee63082 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2412,10 +2412,10 @@ resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-common/-/sncrypto-common-1.9.0.tgz#7c7387357fbd95f6121c35c88c53d5c72203fe22" integrity sha512-EoPwt0gU0kDU1feyumXU3neSaNQivC2+LVQp8C5kDYi6OrrLPzq3dGPc21GFLKinAdcZvjfLc/RkFo6b/CEnAA== -"@standardnotes/sncrypto-web@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-web/-/sncrypto-web-1.10.0.tgz#e30821c724d12d866c076df1293f6b2e6a0bf9ce" - integrity sha512-nRHLfEXG/+/lUP6yDfuwVAw8yq+jjcixQoyFf8nYQgBecqixmVbQdtRJd7KVRzQjqIVNt0elYuZuvoG/om01yw== +"@standardnotes/sncrypto-web@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-web/-/sncrypto-web-1.10.1.tgz#c3467a074bb76b7ebc7858a479a15486ebf7b3be" + integrity sha512-/drPoz2bg5G6QiC48yqp2kkUVsoDKyJHyAb5x0X5rdnCAHaYv+b4NdkSihsj+enWvpocQ0sr2n/gewqmUIPivQ== dependencies: "@standardnotes/sncrypto-common" "^1.9.0" buffer "^6.0.3" From 57ec49733d265946637838155fe40b4b0f94fb2f Mon Sep 17 00:00:00 2001 From: Mo Date: Fri, 20 May 2022 09:53:42 -0500 Subject: [PATCH 03/39] fix: notes view not selected on launch issue --- .../UIModels/AppState/TagsState.ts | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/UIModels/AppState/TagsState.ts b/app/assets/javascripts/UIModels/AppState/TagsState.ts index 565d5a3d5..36c87bb0b 100644 --- a/app/assets/javascripts/UIModels/AppState/TagsState.ts +++ b/app/assets/javascripts/UIModels/AppState/TagsState.ts @@ -89,7 +89,6 @@ export class TagsState extends AbstractState { this.addingSubtagTo = undefined this.smartViews = this.application.items.getSmartViews() - this.selected_ = this.smartViews[0] makeObservable(this, { tags: observable.ref, @@ -145,17 +144,18 @@ export class TagsState extends AbstractState { return } + const updatedReference = + FindItem(changed, currrentSelectedTag.uuid) || FindItem(this.smartViews, currrentSelectedTag.uuid) + if (updatedReference) { + this.setSelectedTagInstance(updatedReference as AnyTag) + } + if (isSystemView(currrentSelectedTag as SmartView)) { return } if (FindItem(removed, currrentSelectedTag.uuid)) { this.setSelectedTagInstance(this.smartViews[0]) - } else { - const updated = FindItem(changed, currrentSelectedTag.uuid) - if (updated) { - this.setSelectedTagInstance(updated as AnyTag) - } } }) }), @@ -244,22 +244,21 @@ export class TagsState extends AbstractState { const footerElementRect = document.getElementById('footer-bar')?.getBoundingClientRect() const footerHeightInPx = footerElementRect?.height - // Open up-bottom is default behavior let openUpBottom = true if (footerHeightInPx) { const bottomSpace = clientHeight - footerHeightInPx - this.contextMenuClickLocation.y const upSpace = this.contextMenuClickLocation.y - // If not enough space to open up-bottom - if (maxContextMenuHeight > bottomSpace) { - // If there's enough space, open bottom-up - if (upSpace > maxContextMenuHeight) { + const notEnoughSpaceToOpenUpBottom = maxContextMenuHeight > bottomSpace + if (notEnoughSpaceToOpenUpBottom) { + const enoughSpaceToOpenBottomUp = upSpace > maxContextMenuHeight + if (enoughSpaceToOpenBottomUp) { openUpBottom = false this.setContextMenuMaxHeight('auto') - // Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space } else { - if (upSpace > bottomSpace) { + const hasMoreUpSpace = upSpace > bottomSpace + if (hasMoreUpSpace) { this.setContextMenuMaxHeight(upSpace - MENU_MARGIN_FROM_APP_BORDER) openUpBottom = false } else { @@ -414,14 +413,14 @@ export class TagsState extends AbstractState { this.selected = editingTag } - public async createNewTemplate() { + public createNewTemplate() { const isAlreadyEditingATemplate = this.editing_ && this.application.items.isTemplateItem(this.editing_) if (isAlreadyEditingATemplate) { return } - const newTag = (await this.application.mutator.createTemplateItem(ContentType.Tag)) as SNTag + const newTag = this.application.mutator.createTemplateItem(ContentType.Tag) as SNTag runInAction(() => { this.editing_ = newTag From e52e2daf39761c64c32658d7d4554436025febbf Mon Sep 17 00:00:00 2001 From: Mo Date: Fri, 20 May 2022 11:40:51 -0500 Subject: [PATCH 04/39] fix: use component displayName property --- .../ChangeEditor/ChangeEditorMenu.tsx | 2 +- .../ChangeEditor/createEditorMenuGroups.ts | 2 +- .../Components/ComponentView/index.tsx | 8 ++--- .../Components/PermissionsModal/index.tsx | 2 +- .../Panes/Extensions/AnyExtension.ts | 3 ++ .../Extensions/ConfirmCustomExtension.tsx | 5 +-- ...ameExtension.tsx => ExtensionInfoCell.tsx} | 17 +++++++--- .../Panes/Extensions/ExtensionItem.tsx | 34 ++++++++++--------- .../Extensions/ExtensionsLatestVersions.ts | 5 +-- .../Preferences/Panes/Extensions/index.tsx | 23 +++++++++---- .../Preferences/Panes/General/Defaults.tsx | 2 +- .../Components/QuickSettingsMenu/index.tsx | 4 +-- 12 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Extensions/AnyExtension.ts rename app/assets/javascripts/Components/Preferences/Panes/Extensions/{RenameExtension.tsx => ExtensionInfoCell.tsx} (84%) diff --git a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx index 889a920cc..2e49a82c8 100644 --- a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx +++ b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx @@ -43,7 +43,7 @@ export const ChangeEditorMenu: FunctionComponent = ({ }) => { const [editors] = useState(() => application.componentManager.componentsForArea(ComponentArea.Editor).sort((a, b) => { - return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1 + return a.displayName.toLowerCase() < b.displayName.toLowerCase() ? -1 : 1 }), ) const [groups, setGroups] = useState([]) diff --git a/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts b/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts index fc7ddda90..4ac1ad471 100644 --- a/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts +++ b/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts @@ -63,7 +63,7 @@ export const createEditorMenuGroups = (application: WebApplication, editors: SNC editors.forEach((editor) => { const editorItem: EditorMenuItem = { - name: editor.name, + name: editor.displayName, component: editor, isEntitled: application.features.getFeatureStatus(editor.identifier) === FeatureStatus.Entitled, } diff --git a/app/assets/javascripts/Components/ComponentView/index.tsx b/app/assets/javascripts/Components/ComponentView/index.tsx index 65851f376..68e3e2f7a 100644 --- a/app/assets/javascripts/Components/ComponentView/index.tsx +++ b/app/assets/javascripts/Components/ComponentView/index.tsx @@ -49,7 +49,7 @@ export const ComponentView: FunctionalComponent = observer( const [isDeprecationMessageDismissed, setIsDeprecationMessageDismissed] = useState(false) const [didAttemptReload, setDidAttemptReload] = useState(false) - const component = componentViewer.component + const component: SNComponent = componentViewer.component const manageSubscription = useCallback(() => { openSubscriptionDashboard(application) @@ -182,7 +182,7 @@ export const ComponentView: FunctionalComponent = observer( <> {hasIssueLoading && ( { reloadValidityStatus(), requestReload?.(componentViewer, true) }} @@ -193,7 +193,7 @@ export const ComponentView: FunctionalComponent = observer( )} @@ -201,7 +201,7 @@ export const ComponentView: FunctionalComponent = observer( )} {error === ComponentViewerError.OfflineRestricted && } - {error === ComponentViewerError.MissingUrl && } + {error === ComponentViewerError.MissingUrl && } {component.uuid && isComponentValid && ( + )} + {isLoading &&
} + + ) +} - return () => { - unregisterDesktopObserver?.() - } - }, [application, requestReload, componentViewer, component.uuid]) - - return ( - <> - {hasIssueLoading && ( - { - reloadValidityStatus(), requestReload?.(componentViewer, true) - }} - /> - )} - - {featureStatus !== FeatureStatus.Entitled && ( - - )} - {deprecationMessage && !isDeprecationMessageDismissed && ( - - )} - {error === ComponentViewerError.OfflineRestricted && } - {error === ComponentViewerError.MissingUrl && } - {component.uuid && isComponentValid && ( - - )} - {isLoading &&
} - - ) - }, -) +export default observer(ComponentView) diff --git a/app/assets/javascripts/Components/ComponentView/IsDeprecated.tsx b/app/assets/javascripts/Components/ComponentView/IsDeprecated.tsx index fab2a95f8..ee0125870 100644 --- a/app/assets/javascripts/Components/ComponentView/IsDeprecated.tsx +++ b/app/assets/javascripts/Components/ComponentView/IsDeprecated.tsx @@ -1,11 +1,11 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' -interface IProps { +type Props = { deprecationMessage: string | undefined dismissDeprecationMessage: () => void } -export const IsDeprecated: FunctionalComponent = ({ deprecationMessage, dismissDeprecationMessage }) => { +const IsDeprecated: FunctionComponent = ({ deprecationMessage, dismissDeprecationMessage }) => { return (
@@ -23,3 +23,5 @@ export const IsDeprecated: FunctionalComponent = ({ deprecationMessage,
) } + +export default IsDeprecated diff --git a/app/assets/javascripts/Components/ComponentView/IsExpired.tsx b/app/assets/javascripts/Components/ComponentView/IsExpired.tsx index d2df6cee1..61d88aa54 100644 --- a/app/assets/javascripts/Components/ComponentView/IsExpired.tsx +++ b/app/assets/javascripts/Components/ComponentView/IsExpired.tsx @@ -1,7 +1,7 @@ import { FeatureStatus } from '@standardnotes/snjs' -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' -interface IProps { +type Props = { expiredDate: string componentName: string featureStatus: FeatureStatus @@ -21,12 +21,7 @@ const statusString = (featureStatus: FeatureStatus, expiredDate: string, compone } } -export const IsExpired: FunctionalComponent = ({ - expiredDate, - featureStatus, - componentName, - manageSubscription, -}) => { +const IsExpired: FunctionComponent = ({ expiredDate, featureStatus, componentName, manageSubscription }) => { return (
@@ -52,3 +47,5 @@ export const IsExpired: FunctionalComponent = ({
) } + +export default IsExpired diff --git a/app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx b/app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx index 69f3c48ca..3f9a0f4c0 100644 --- a/app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx +++ b/app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx @@ -1,11 +1,11 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' -interface IProps { +type Props = { componentName: string reloadIframe: () => void } -export const IssueOnLoading: FunctionalComponent = ({ componentName, reloadIframe }) => { +const IssueOnLoading: FunctionComponent = ({ componentName, reloadIframe }) => { return (
@@ -23,3 +23,5 @@ export const IssueOnLoading: FunctionalComponent = ({ componentName, rel
) } + +export default IssueOnLoading diff --git a/app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx b/app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx index ff2fa1063..bb9258c9e 100644 --- a/app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx +++ b/app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx @@ -1,6 +1,6 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' -export const OfflineRestricted: FunctionalComponent = () => { +const OfflineRestricted: FunctionComponent = () => { return (
@@ -29,3 +29,5 @@ export const OfflineRestricted: FunctionalComponent = () => {
) } + +export default OfflineRestricted diff --git a/app/assets/javascripts/Components/ComponentView/UrlMissing.tsx b/app/assets/javascripts/Components/ComponentView/UrlMissing.tsx index ee70ebde6..cd74f3bcc 100644 --- a/app/assets/javascripts/Components/ComponentView/UrlMissing.tsx +++ b/app/assets/javascripts/Components/ComponentView/UrlMissing.tsx @@ -1,10 +1,10 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' -interface IProps { +type Props = { componentName: string } -export const UrlMissing: FunctionalComponent = ({ componentName }) => { +const UrlMissing: FunctionComponent = ({ componentName }) => { return (
@@ -20,3 +20,5 @@ export const UrlMissing: FunctionalComponent = ({ componentName }) => {
) } + +export default UrlMissing diff --git a/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx b/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx index ed8679b15..98e99bc33 100644 --- a/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx +++ b/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'preact/hooks' +import { FunctionComponent, useEffect, useRef, useState } from 'react' import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog' import { STRING_SIGN_OUT_CONFIRMATION } from '@/Strings' import { WebApplication } from '@/UIModels/Application' @@ -13,14 +13,7 @@ type Props = { applicationGroup: ApplicationGroup } -export const ConfirmSignoutContainer = observer((props: Props) => { - if (!props.appState.accountMenu.signingOut) { - return null - } - return -}) - -export const ConfirmSignoutModal = observer(({ application, appState, applicationGroup }: Props) => { +const ConfirmSignoutModal: FunctionComponent = ({ application, appState, applicationGroup }) => { const [deleteLocalBackups, setDeleteLocalBackups] = useState(false) const cancelRef = useRef(null) @@ -114,4 +107,15 @@ export const ConfirmSignoutModal = observer(({ application, appState, applicatio
) -}) +} + +ConfirmSignoutModal.displayName = 'ConfirmSignoutModal' + +const ConfirmSignoutContainer = (props: Props) => { + if (!props.appState.accountMenu.signingOut) { + return null + } + return +} + +export default observer(ConfirmSignoutContainer) diff --git a/app/assets/javascripts/Components/ContentListView/ContentList.tsx b/app/assets/javascripts/Components/ContentListView/ContentList.tsx index b249e6429..e084539bd 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentList.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentList.tsx @@ -3,11 +3,10 @@ import { KeyboardKey } from '@/Services/IOService' import { AppState } from '@/UIModels/AppState' import { UuidString } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' +import { FunctionComponent, KeyboardEventHandler, UIEventHandler, useCallback } from 'react' import { FOCUSABLE_BUT_NOT_TABBABLE, NOTES_LIST_SCROLL_THRESHOLD } from '@/Constants' import { ListableContentItem } from './Types/ListableContentItem' -import { ContentListItem } from './ContentListItem' -import { useCallback } from 'preact/hooks' +import ContentListItem from './ContentListItem' type Props = { application: WebApplication @@ -17,59 +16,59 @@ type Props = { paginate: () => void } -export const ContentList: FunctionComponent = observer( - ({ application, appState, items, selectedItems, paginate }) => { - const { selectPreviousItem, selectNextItem } = appState.contentListView - const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = appState.contentListView.webDisplayOptions - const { sortBy } = appState.contentListView.displayOptions +const ContentList: FunctionComponent = ({ application, appState, items, selectedItems, paginate }) => { + const { selectPreviousItem, selectNextItem } = appState.contentListView + const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = appState.contentListView.webDisplayOptions + const { sortBy } = appState.contentListView.displayOptions - const onScroll = useCallback( - (e: Event) => { - const offset = NOTES_LIST_SCROLL_THRESHOLD - const element = e.target as HTMLElement - if (element.scrollTop + element.offsetHeight >= element.scrollHeight - offset) { - paginate() - } - }, - [paginate], - ) + const onScroll: UIEventHandler = useCallback( + (e) => { + const offset = NOTES_LIST_SCROLL_THRESHOLD + const element = e.target as HTMLElement + if (element.scrollTop + element.offsetHeight >= element.scrollHeight - offset) { + paginate() + } + }, + [paginate], + ) - const onKeyDown = useCallback( - (e: KeyboardEvent) => { - if (e.key === KeyboardKey.Up) { - e.preventDefault() - selectPreviousItem() - } else if (e.key === KeyboardKey.Down) { - e.preventDefault() - selectNextItem() - } - }, - [selectNextItem, selectPreviousItem], - ) + const onKeyDown: KeyboardEventHandler = useCallback( + (e) => { + if (e.key === KeyboardKey.Up) { + e.preventDefault() + selectPreviousItem() + } else if (e.key === KeyboardKey.Down) { + e.preventDefault() + selectNextItem() + } + }, + [selectNextItem, selectPreviousItem], + ) - return ( -
- {items.map((item) => ( - - ))} -
- ) - }, -) + return ( +
+ {items.map((item) => ( + + ))} +
+ ) +} + +export default observer(ContentList) diff --git a/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx b/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx index df29c4870..4a92569c9 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx @@ -1,10 +1,10 @@ import { ContentType, SNTag } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { FileListItem } from './FileListItem' -import { NoteListItem } from './NoteListItem' +import { FunctionComponent } from 'react' +import FileListItem from './FileListItem' +import NoteListItem from './NoteListItem' import { AbstractListItemProps } from './Types/AbstractListItemProps' -export const ContentListItem: FunctionComponent = (props) => { +const ContentListItem: FunctionComponent = (props) => { const getTags = () => { if (props.hideTags) { return [] @@ -34,3 +34,5 @@ export const ContentListItem: FunctionComponent = (props) return null } } + +export default ContentListItem diff --git a/app/assets/javascripts/Components/ContentListView/ContentListView.tsx b/app/assets/javascripts/Components/ContentListView/ContentListView.tsx index cf5796d42..cb793be26 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentListView.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentListView.tsx @@ -4,27 +4,29 @@ import { AppState } from '@/UIModels/AppState' import { PANEL_NAME_NOTES } from '@/Constants' import { PrefKey } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' -import { ContentList } from '@/Components/ContentListView/ContentList' -import { NotesListOptionsMenu } from '@/Components/ContentListView/NotesListOptionsMenu' -import { NoAccountWarning } from '@/Components/NoAccountWarning/NoAccountWarning' -import { SearchOptions } from '@/Components/SearchOptions/SearchOptions' -import { PanelSide, ResizeFinishCallback, PanelResizer, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' +import { + ChangeEventHandler, + FunctionComponent, + KeyboardEventHandler, + useCallback, + useEffect, + useRef, + useState, +} from 'react' +import ContentList from '@/Components/ContentListView/ContentList' +import NotesListOptionsMenu from '@/Components/ContentListView/NotesListOptionsMenu' +import NoAccountWarningWrapper from '@/Components/NoAccountWarning/NoAccountWarning' +import SearchOptions from '@/Components/SearchOptions/SearchOptions' +import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' -import { isStateDealloced } from '@/UIModels/AppState/AbstractState' type Props = { application: WebApplication appState: AppState } -export const ContentListView: FunctionComponent = observer(({ application, appState }) => { - if (isStateDealloced(appState)) { - return null - } - +const ContentListView: FunctionComponent = ({ application, appState }) => { const itemsViewPanelRef = useRef(null) const displayOptionsMenuRef = useRef(null) @@ -104,9 +106,9 @@ export const ContentListView: FunctionComponent = observer(({ application } }, [application.io, createNewNote, searchBarElement, selectNextItem, selectPreviousItem]) - const onNoteFilterTextChange = useCallback( - (e: Event) => { - setNoteFilterText((e.target as HTMLInputElement).value) + const onNoteFilterTextChange: ChangeEventHandler = useCallback( + (e) => { + setNoteFilterText(e.target.value) }, [setNoteFilterText], ) @@ -114,8 +116,8 @@ export const ContentListView: FunctionComponent = observer(({ application const onSearchFocused = useCallback(() => setFocusedSearch(true), []) const onSearchBlurred = useCallback(() => setFocusedSearch(false), []) - const onNoteFilterKeyUp = useCallback( - (e: KeyboardEvent) => { + const onNoteFilterKeyUp: KeyboardEventHandler = useCallback( + (e) => { if (e.key === KeyboardKey.Enter) { onFilterEnter() } @@ -176,10 +178,10 @@ export const ContentListView: FunctionComponent = observer(({ application onKeyUp={onNoteFilterKeyUp} onFocus={onSearchFocused} onBlur={onSearchBlurred} - autocomplete="off" + autoComplete="off" /> {noteFilterText && ( - )} @@ -191,7 +193,7 @@ export const ContentListView: FunctionComponent = observer(({ application
)}
- +
@@ -253,4 +255,6 @@ export const ContentListView: FunctionComponent = observer(({ application )}
) -}) +} + +export default observer(ContentListView) diff --git a/app/assets/javascripts/Components/ContentListView/FileListItem.tsx b/app/assets/javascripts/Components/ContentListView/FileListItem.tsx index 33bc666c6..e8358298f 100644 --- a/app/assets/javascripts/Components/ContentListView/FileListItem.tsx +++ b/app/assets/javascripts/Components/ContentListView/FileListItem.tsx @@ -1,81 +1,89 @@ import { FileItem } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback } from 'preact/hooks' -import { getFileIconComponent } from '../AttachedFilesPopover/PopoverFileItem' -import { ListItemConflictIndicator } from './ListItemConflictIndicator' -import { ListItemFlagIcons } from './ListItemFlagIcons' -import { ListItemTags } from './ListItemTags' -import { ListItemMetadata } from './ListItemMetadata' +import { FunctionComponent, useCallback } from 'react' +import { getFileIconComponent } from '../AttachedFilesPopover/getFileIconComponent' +import ListItemConflictIndicator from './ListItemConflictIndicator' +import ListItemFlagIcons from './ListItemFlagIcons' +import ListItemTags from './ListItemTags' +import ListItemMetadata from './ListItemMetadata' import { DisplayableListItemProps } from './Types/DisplayableListItemProps' -export const FileListItem: FunctionComponent = observer( - ({ application, appState, hideDate, hideIcon, hideTags, item, selected, sortBy, tags }) => { - const openFileContextMenu = useCallback( - (posX: number, posY: number) => { - appState.files.setFileContextMenuLocation({ - x: posX, - y: posY, - }) - appState.files.setShowFileContextMenu(true) - }, - [appState.files], - ) - - const openContextMenu = useCallback( - (posX: number, posY: number) => { - void appState.contentListView.selectItemWithScrollHandling(item, { - userTriggered: true, - scrollIntoView: false, - }) - openFileContextMenu(posX, posY) - }, - [appState.contentListView, item, openFileContextMenu], - ) - - const onClick = useCallback(() => { - void appState.selectedItems.selectItem(item.uuid, true).then(({ didSelect }) => { - if (didSelect && appState.selectedItems.selectedItemsCount < 2) { - appState.filePreviewModal.activate(item as FileItem, appState.files.allFiles) - } +const FileListItem: FunctionComponent = ({ + application, + appState, + hideDate, + hideIcon, + hideTags, + item, + selected, + sortBy, + tags, +}) => { + const openFileContextMenu = useCallback( + (posX: number, posY: number) => { + appState.files.setFileContextMenuLocation({ + x: posX, + y: posY, }) - }, [appState.filePreviewModal, appState.files.allFiles, appState.selectedItems, item]) + appState.files.setShowFileContextMenu(true) + }, + [appState.files], + ) - const IconComponent = () => - getFileIconComponent( - application.iconsController.getIconForFileType((item as FileItem).mimeType), - 'w-5 h-5 flex-shrink-0', - ) + const openContextMenu = useCallback( + async (posX: number, posY: number) => { + const { didSelect } = await appState.selectedItems.selectItem(item.uuid) + if (didSelect) { + openFileContextMenu(posX, posY) + } + }, + [appState.selectedItems, item.uuid, openFileContextMenu], + ) - return ( -
{ - event.preventDefault() - openContextMenu(event.clientX, event.clientY) - }} - > - {!hideIcon ? ( -
- -
- ) : ( -
- )} -
-
-
{item.title}
-
- - - -
- -
+ const onClick = useCallback(() => { + void appState.selectedItems.selectItem(item.uuid, true).then(({ didSelect }) => { + if (didSelect && appState.selectedItems.selectedItemsCount < 2) { + appState.filePreviewModal.activate(item as FileItem, appState.files.allFiles) + } + }) + }, [appState.filePreviewModal, appState.files.allFiles, appState.selectedItems, item]) + + const IconComponent = () => + getFileIconComponent( + application.iconsController.getIconForFileType((item as FileItem).mimeType), + 'w-5 h-5 flex-shrink-0', ) - }, -) + + return ( +
{ + event.preventDefault() + void openContextMenu(event.clientX, event.clientY) + }} + > + {!hideIcon ? ( +
+ +
+ ) : ( +
+ )} +
+
+
{item.title}
+
+ + + +
+ +
+ ) +} + +export default observer(FileListItem) diff --git a/app/assets/javascripts/Components/ContentListView/ListItemConflictIndicator.tsx b/app/assets/javascripts/Components/ContentListView/ListItemConflictIndicator.tsx index b1146b707..1849b286d 100644 --- a/app/assets/javascripts/Components/ContentListView/ListItemConflictIndicator.tsx +++ b/app/assets/javascripts/Components/ContentListView/ListItemConflictIndicator.tsx @@ -1,11 +1,13 @@ -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' import { ListableContentItem } from './Types/ListableContentItem' -export const ListItemConflictIndicator: FunctionComponent<{ +type Props = { item: { conflictOf?: ListableContentItem['conflictOf'] } -}> = ({ item }) => { +} + +const ListItemConflictIndicator: FunctionComponent = ({ item }) => { return item.conflictOf ? (
@@ -14,3 +16,5 @@ export const ListItemConflictIndicator: FunctionComponent<{
) : null } + +export default ListItemConflictIndicator diff --git a/app/assets/javascripts/Components/ContentListView/ListItemFlagIcons.tsx b/app/assets/javascripts/Components/ContentListView/ListItemFlagIcons.tsx index 1cfb57dd3..9fd7b2d28 100644 --- a/app/assets/javascripts/Components/ContentListView/ListItemFlagIcons.tsx +++ b/app/assets/javascripts/Components/ContentListView/ListItemFlagIcons.tsx @@ -1,5 +1,5 @@ -import { FunctionComponent } from 'preact' -import { Icon } from '@/Components/Icon/Icon' +import { FunctionComponent } from 'react' +import Icon from '@/Components/Icon/Icon' import { ListableContentItem } from './Types/ListableContentItem' type Props = { @@ -12,7 +12,7 @@ type Props = { hasFiles?: boolean } -export const ListItemFlagIcons: FunctionComponent = ({ item, hasFiles = false }) => { +const ListItemFlagIcons: FunctionComponent = ({ item, hasFiles = false }) => { return (
{item.locked && ( @@ -43,3 +43,5 @@ export const ListItemFlagIcons: FunctionComponent = ({ item, hasFiles = f
) } + +export default ListItemFlagIcons diff --git a/app/assets/javascripts/Components/ContentListView/ListItemMetadata.tsx b/app/assets/javascripts/Components/ContentListView/ListItemMetadata.tsx index 94f54a2af..51c812542 100644 --- a/app/assets/javascripts/Components/ContentListView/ListItemMetadata.tsx +++ b/app/assets/javascripts/Components/ContentListView/ListItemMetadata.tsx @@ -1,5 +1,5 @@ import { CollectionSort, SortableItem } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' import { ListableContentItem } from './Types/ListableContentItem' type Props = { @@ -12,7 +12,7 @@ type Props = { sortBy: keyof SortableItem | undefined } -export const ListItemMetadata: FunctionComponent = ({ item, hideDate, sortBy }) => { +const ListItemMetadata: FunctionComponent = ({ item, hideDate, sortBy }) => { const showModifiedDate = sortBy === CollectionSort.UpdatedAt if (hideDate && !item.protected) { @@ -27,3 +27,5 @@ export const ListItemMetadata: FunctionComponent = ({ item, hideDate, sor
) } + +export default ListItemMetadata diff --git a/app/assets/javascripts/Components/ContentListView/ListItemTags.tsx b/app/assets/javascripts/Components/ContentListView/ListItemTags.tsx index 4d9258c85..31c040ecb 100644 --- a/app/assets/javascripts/Components/ContentListView/ListItemTags.tsx +++ b/app/assets/javascripts/Components/ContentListView/ListItemTags.tsx @@ -1,10 +1,12 @@ -import { FunctionComponent } from 'preact' -import { Icon } from '@/Components/Icon/Icon' +import { FunctionComponent } from 'react' +import Icon from '@/Components/Icon/Icon' -export const ListItemTags: FunctionComponent<{ +type Props = { hideTags: boolean tags: string[] -}> = ({ hideTags, tags }) => { +} + +const ListItemTags: FunctionComponent = ({ hideTags, tags }) => { if (hideTags || !tags.length) { return null } @@ -12,7 +14,10 @@ export const ListItemTags: FunctionComponent<{ return (
{tags.map((tag) => ( - + {tag} @@ -20,3 +25,5 @@ export const ListItemTags: FunctionComponent<{
) } + +export default ListItemTags diff --git a/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx b/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx index f51b530bf..b6fa0e960 100644 --- a/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx +++ b/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx @@ -1,84 +1,97 @@ import { PLAIN_EDITOR_NAME } from '@/Constants' import { sanitizeHtmlString, SNNote } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { Icon } from '@/Components/Icon/Icon' -import { ListItemConflictIndicator } from './ListItemConflictIndicator' -import { ListItemFlagIcons } from './ListItemFlagIcons' -import { ListItemTags } from './ListItemTags' -import { ListItemMetadata } from './ListItemMetadata' +import { FunctionComponent } from 'react' +import Icon from '@/Components/Icon/Icon' +import ListItemConflictIndicator from './ListItemConflictIndicator' +import ListItemFlagIcons from './ListItemFlagIcons' +import ListItemTags from './ListItemTags' +import ListItemMetadata from './ListItemMetadata' import { DisplayableListItemProps } from './Types/DisplayableListItemProps' -export const NoteListItem: FunctionComponent = observer( - ({ application, appState, hideDate, hideIcon, hideTags, hidePreview, item, selected, sortBy, tags }) => { - const editorForNote = application.componentManager.editorForNote(item as SNNote) - const editorName = editorForNote?.name ?? PLAIN_EDITOR_NAME - const [icon, tint] = application.iconsController.getIconAndTintForNoteType(editorForNote?.package_info.note_type) - const hasFiles = application.items.getFilesForNote(item as SNNote).length > 0 +const NoteListItem: FunctionComponent = ({ + application, + appState, + hideDate, + hideIcon, + hideTags, + hidePreview, + item, + selected, + sortBy, + tags, +}) => { + const editorForNote = application.componentManager.editorForNote(item as SNNote) + const editorName = editorForNote?.name ?? PLAIN_EDITOR_NAME + const [icon, tint] = application.iconsController.getIconAndTintForNoteType(editorForNote?.package_info.note_type) + const hasFiles = application.items.getFilesForNote(item as SNNote).length > 0 - const openNoteContextMenu = (posX: number, posY: number) => { - appState.notes.setContextMenuClickLocation({ - x: posX, - y: posY, - }) - appState.notes.reloadContextMenuLayout() - appState.notes.setContextMenuOpen(true) - } + const openNoteContextMenu = (posX: number, posY: number) => { + appState.notes.setContextMenuClickLocation({ + x: posX, + y: posY, + }) + appState.notes.reloadContextMenuLayout() + appState.notes.setContextMenuOpen(true) + } - const openContextMenu = (posX: number, posY: number) => { - void appState.selectedItems.selectItem(item.uuid, true) + const openContextMenu = async (posX: number, posY: number) => { + const { didSelect } = await appState.selectedItems.selectItem(item.uuid, true) + if (didSelect) { openNoteContextMenu(posX, posY) } + } - return ( -
{ - void appState.selectedItems.selectItem(item.uuid, true) - }} - onContextMenu={(event) => { - event.preventDefault() - openContextMenu(event.clientX, event.clientY) - }} - > - {!hideIcon ? ( -
- -
- ) : ( -
- )} -
-
-
{item.title}
-
- {!hidePreview && !item.hidePreview && !item.protected && ( -
- {item.preview_html && ( -
- )} - {!item.preview_html && item.preview_plain && ( -
{item.preview_plain}
- )} - {!item.preview_html && !item.preview_plain && item.text && ( -
{item.text}
- )} -
- )} - - - + return ( +
{ + void appState.selectedItems.selectItem(item.uuid, true) + }} + onContextMenu={(event) => { + event.preventDefault() + void openContextMenu(event.clientX, event.clientY) + }} + > + {!hideIcon ? ( +
+
- + ) : ( +
+ )} +
+
+
{item.title}
+
+ {!hidePreview && !item.hidePreview && !item.protected && ( +
+ {item.preview_html && ( +
+ )} + {!item.preview_html && item.preview_plain && ( +
{item.preview_plain}
+ )} + {!item.preview_html && !item.preview_plain && item.text && ( +
{item.text}
+ )} +
+ )} + + +
- ) - }, -) + +
+ ) +} + +export default observer(NoteListItem) diff --git a/app/assets/javascripts/Components/ContentListView/NotesListOptionsMenu.tsx b/app/assets/javascripts/Components/ContentListView/NotesListOptionsMenu.tsx index 23bd29e1b..569b8650f 100644 --- a/app/assets/javascripts/Components/ContentListView/NotesListOptionsMenu.tsx +++ b/app/assets/javascripts/Components/ContentListView/NotesListOptionsMenu.tsx @@ -1,11 +1,12 @@ import { WebApplication } from '@/UIModels/Application' import { CollectionSort, CollectionSortProperty, PrefKey } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useState } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' -import { Menu } from '@/Components/Menu/Menu' -import { MenuItem, MenuItemSeparator, MenuItemType } from '@/Components/Menu/MenuItem' +import { FunctionComponent, useCallback, useState } from 'react' +import Icon from '@/Components/Icon/Icon' +import Menu from '@/Components/Menu/Menu' +import MenuItem from '@/Components/Menu/MenuItem' +import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator' +import { MenuItemType } from '@/Components/Menu/MenuItemType' type Props = { application: WebApplication @@ -14,235 +15,238 @@ type Props = { isOpen: boolean } -export const NotesListOptionsMenu: FunctionComponent = observer( - ({ closeDisplayOptionsMenu, closeOnBlur, application, isOpen }) => { - const [sortBy, setSortBy] = useState(() => application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)) - const [sortReverse, setSortReverse] = useState(() => application.getPreference(PrefKey.SortNotesReverse, false)) - const [hidePreview, setHidePreview] = useState(() => application.getPreference(PrefKey.NotesHideNotePreview, false)) - const [hideDate, setHideDate] = useState(() => application.getPreference(PrefKey.NotesHideDate, false)) - const [hideTags, setHideTags] = useState(() => application.getPreference(PrefKey.NotesHideTags, true)) - const [hidePinned, setHidePinned] = useState(() => application.getPreference(PrefKey.NotesHidePinned, false)) - const [showArchived, setShowArchived] = useState(() => application.getPreference(PrefKey.NotesShowArchived, false)) - const [showTrashed, setShowTrashed] = useState(() => application.getPreference(PrefKey.NotesShowTrashed, false)) - const [hideProtected, setHideProtected] = useState(() => - application.getPreference(PrefKey.NotesHideProtected, false), - ) - const [hideEditorIcon, setHideEditorIcon] = useState(() => - application.getPreference(PrefKey.NotesHideEditorIcon, false), - ) +const NotesListOptionsMenu: FunctionComponent = ({ + closeDisplayOptionsMenu, + closeOnBlur, + application, + isOpen, +}) => { + const [sortBy, setSortBy] = useState(() => application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)) + const [sortReverse, setSortReverse] = useState(() => application.getPreference(PrefKey.SortNotesReverse, false)) + const [hidePreview, setHidePreview] = useState(() => application.getPreference(PrefKey.NotesHideNotePreview, false)) + const [hideDate, setHideDate] = useState(() => application.getPreference(PrefKey.NotesHideDate, false)) + const [hideTags, setHideTags] = useState(() => application.getPreference(PrefKey.NotesHideTags, true)) + const [hidePinned, setHidePinned] = useState(() => application.getPreference(PrefKey.NotesHidePinned, false)) + const [showArchived, setShowArchived] = useState(() => application.getPreference(PrefKey.NotesShowArchived, false)) + const [showTrashed, setShowTrashed] = useState(() => application.getPreference(PrefKey.NotesShowTrashed, false)) + const [hideProtected, setHideProtected] = useState(() => application.getPreference(PrefKey.NotesHideProtected, false)) + const [hideEditorIcon, setHideEditorIcon] = useState(() => + application.getPreference(PrefKey.NotesHideEditorIcon, false), + ) - const toggleSortReverse = useCallback(() => { - application.setPreference(PrefKey.SortNotesReverse, !sortReverse).catch(console.error) - setSortReverse(!sortReverse) - }, [application, sortReverse]) + const toggleSortReverse = useCallback(() => { + application.setPreference(PrefKey.SortNotesReverse, !sortReverse).catch(console.error) + setSortReverse(!sortReverse) + }, [application, sortReverse]) - const toggleSortBy = useCallback( - (sort: CollectionSortProperty) => { - if (sortBy === sort) { - toggleSortReverse() - } else { - setSortBy(sort) - application.setPreference(PrefKey.SortNotesBy, sort).catch(console.error) - } - }, - [application, sortBy, toggleSortReverse], - ) + const toggleSortBy = useCallback( + (sort: CollectionSortProperty) => { + if (sortBy === sort) { + toggleSortReverse() + } else { + setSortBy(sort) + application.setPreference(PrefKey.SortNotesBy, sort).catch(console.error) + } + }, + [application, sortBy, toggleSortReverse], + ) - const toggleSortByDateModified = useCallback(() => { - toggleSortBy(CollectionSort.UpdatedAt) - }, [toggleSortBy]) + const toggleSortByDateModified = useCallback(() => { + toggleSortBy(CollectionSort.UpdatedAt) + }, [toggleSortBy]) - const toggleSortByCreationDate = useCallback(() => { - toggleSortBy(CollectionSort.CreatedAt) - }, [toggleSortBy]) + const toggleSortByCreationDate = useCallback(() => { + toggleSortBy(CollectionSort.CreatedAt) + }, [toggleSortBy]) - const toggleSortByTitle = useCallback(() => { - toggleSortBy(CollectionSort.Title) - }, [toggleSortBy]) + const toggleSortByTitle = useCallback(() => { + toggleSortBy(CollectionSort.Title) + }, [toggleSortBy]) - const toggleHidePreview = useCallback(() => { - setHidePreview(!hidePreview) - application.setPreference(PrefKey.NotesHideNotePreview, !hidePreview).catch(console.error) - }, [application, hidePreview]) + const toggleHidePreview = useCallback(() => { + setHidePreview(!hidePreview) + application.setPreference(PrefKey.NotesHideNotePreview, !hidePreview).catch(console.error) + }, [application, hidePreview]) - const toggleHideDate = useCallback(() => { - setHideDate(!hideDate) - application.setPreference(PrefKey.NotesHideDate, !hideDate).catch(console.error) - }, [application, hideDate]) + const toggleHideDate = useCallback(() => { + setHideDate(!hideDate) + application.setPreference(PrefKey.NotesHideDate, !hideDate).catch(console.error) + }, [application, hideDate]) - const toggleHideTags = useCallback(() => { - setHideTags(!hideTags) - application.setPreference(PrefKey.NotesHideTags, !hideTags).catch(console.error) - }, [application, hideTags]) + const toggleHideTags = useCallback(() => { + setHideTags(!hideTags) + application.setPreference(PrefKey.NotesHideTags, !hideTags).catch(console.error) + }, [application, hideTags]) - const toggleHidePinned = useCallback(() => { - setHidePinned(!hidePinned) - application.setPreference(PrefKey.NotesHidePinned, !hidePinned).catch(console.error) - }, [application, hidePinned]) + const toggleHidePinned = useCallback(() => { + setHidePinned(!hidePinned) + application.setPreference(PrefKey.NotesHidePinned, !hidePinned).catch(console.error) + }, [application, hidePinned]) - const toggleShowArchived = useCallback(() => { - setShowArchived(!showArchived) - application.setPreference(PrefKey.NotesShowArchived, !showArchived).catch(console.error) - }, [application, showArchived]) + const toggleShowArchived = useCallback(() => { + setShowArchived(!showArchived) + application.setPreference(PrefKey.NotesShowArchived, !showArchived).catch(console.error) + }, [application, showArchived]) - const toggleShowTrashed = useCallback(() => { - setShowTrashed(!showTrashed) - application.setPreference(PrefKey.NotesShowTrashed, !showTrashed).catch(console.error) - }, [application, showTrashed]) + const toggleShowTrashed = useCallback(() => { + setShowTrashed(!showTrashed) + application.setPreference(PrefKey.NotesShowTrashed, !showTrashed).catch(console.error) + }, [application, showTrashed]) - const toggleHideProtected = useCallback(() => { - setHideProtected(!hideProtected) - application.setPreference(PrefKey.NotesHideProtected, !hideProtected).catch(console.error) - }, [application, hideProtected]) + const toggleHideProtected = useCallback(() => { + setHideProtected(!hideProtected) + application.setPreference(PrefKey.NotesHideProtected, !hideProtected).catch(console.error) + }, [application, hideProtected]) - const toggleEditorIcon = useCallback(() => { - setHideEditorIcon(!hideEditorIcon) - application.setPreference(PrefKey.NotesHideEditorIcon, !hideEditorIcon).catch(console.error) - }, [application, hideEditorIcon]) + const toggleEditorIcon = useCallback(() => { + setHideEditorIcon(!hideEditorIcon) + application.setPreference(PrefKey.NotesHideEditorIcon, !hideEditorIcon).catch(console.error) + }, [application, hideEditorIcon]) - return ( - +
Sort by
+ -
Sort by
- -
- Date modified - {sortBy === CollectionSort.UpdatedAt ? ( - sortReverse ? ( - - ) : ( - - ) - ) : null} -
-
- -
- Creation date - {sortBy === CollectionSort.CreatedAt ? ( - sortReverse ? ( - - ) : ( - - ) - ) : null} -
-
- -
- Title - {sortBy === CollectionSort.Title ? ( - sortReverse ? ( - - ) : ( - - ) - ) : null} -
-
- -
View
- -
Show note preview
-
- - Show date - - - Show tags - - - Show editor icon - -
-
Other
- - Show pinned notes - - - Show protected notes - - - Show archived notes - - - Show trashed notes - -
- ) - }, -) +
+ Date modified + {sortBy === CollectionSort.UpdatedAt ? ( + sortReverse ? ( + + ) : ( + + ) + ) : null} +
+ + +
+ Creation date + {sortBy === CollectionSort.CreatedAt ? ( + sortReverse ? ( + + ) : ( + + ) + ) : null} +
+
+ +
+ Title + {sortBy === CollectionSort.Title ? ( + sortReverse ? ( + + ) : ( + + ) + ) : null} +
+
+ +
View
+ +
Show note preview
+
+ + Show date + + + Show tags + + + Show editor icon + +
+
Other
+ + Show pinned notes + + + Show protected notes + + + Show archived notes + + + Show trashed notes + + + ) +} + +export default observer(NotesListOptionsMenu) diff --git a/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx b/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx new file mode 100644 index 000000000..770c4cc12 --- /dev/null +++ b/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx @@ -0,0 +1,17 @@ +import { WebApplication } from '@/UIModels/Application' +import { observer } from 'mobx-react-lite' +import { FunctionComponent } from 'react' + +type Props = { + application: WebApplication +} + +const DeallocateHandler: FunctionComponent = ({ application, children }) => { + if (application.dealloced) { + return null + } + + return <>{children} +} + +export default observer(DeallocateHandler) diff --git a/app/assets/javascripts/Components/Dropdown/Dropdown.tsx b/app/assets/javascripts/Components/Dropdown/Dropdown.tsx index abf75f76d..806be187f 100644 --- a/app/assets/javascripts/Components/Dropdown/Dropdown.tsx +++ b/app/assets/javascripts/Components/Dropdown/Dropdown.tsx @@ -1,16 +1,8 @@ import { ListboxArrow, ListboxButton, ListboxInput, ListboxList, ListboxOption, ListboxPopover } from '@reach/listbox' import VisuallyHidden from '@reach/visually-hidden' -import { FunctionComponent } from 'preact' -import { Icon } from '@/Components/Icon/Icon' -import { IconType } from '@standardnotes/snjs' - -export type DropdownItem = { - icon?: IconType - iconClassName?: string - label: string - value: string - disabled?: boolean -} +import { FunctionComponent } from 'react' +import Icon from '@/Components/Icon/Icon' +import { DropdownItem } from './DropdownItem' type DropdownProps = { id: string @@ -46,7 +38,7 @@ const CustomDropdownButton: FunctionComponent = ({ ) -export const Dropdown: FunctionComponent = ({ id, label, items, value, onChange, disabled }) => { +const Dropdown: FunctionComponent = ({ id, label, items, value, onChange, disabled }) => { const labelId = `${id}-label` const handleChange = (value: string) => { @@ -79,6 +71,7 @@ export const Dropdown: FunctionComponent = ({ id, label, items, v {items.map((item) => ( = ({ id, label, items, v ) } + +export default Dropdown diff --git a/app/assets/javascripts/Components/Dropdown/DropdownItem.tsx b/app/assets/javascripts/Components/Dropdown/DropdownItem.tsx new file mode 100644 index 000000000..05aaf556d --- /dev/null +++ b/app/assets/javascripts/Components/Dropdown/DropdownItem.tsx @@ -0,0 +1,9 @@ +import { IconType } from '@standardnotes/snjs' + +export type DropdownItem = { + icon?: IconType + iconClassName?: string + label: string + value: string + disabled?: boolean +} diff --git a/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx b/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx index ec27d4636..1243073b0 100644 --- a/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx +++ b/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx @@ -3,18 +3,16 @@ import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside' import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' -import React from 'react' +import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' import { PopoverFileItemAction } from '../AttachedFilesPopover/PopoverFileItemAction' import { PopoverTabs } from '../AttachedFilesPopover/PopoverTabs' -import { FileMenuOptions } from './FileMenuOptions' +import FileMenuOptions from './FileMenuOptions' type Props = { appState: AppState } -export const FileContextMenu: FunctionComponent = observer(({ appState }) => { +const FileContextMenu: FunctionComponent = observer(({ appState }) => { const { selectedFiles, showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = appState.files const [contextMenuStyle, setContextMenuStyle] = useState({ @@ -28,9 +26,6 @@ export const FileContextMenu: FunctionComponent = observer(({ appState }) useCloseOnClickOutside(contextMenuRef, () => appState.files.setShowFileContextMenu(false)) const selectedFile = selectedFiles[0] - if (!showFileContextMenu || !selectedFile) { - return null - } const reloadContextMenuLayout = useCallback(() => { const { clientHeight } = document.documentElement @@ -118,3 +113,19 @@ export const FileContextMenu: FunctionComponent = observer(({ appState })
) }) + +FileContextMenu.displayName = 'FileContextMenu' + +const FileContextMenuWrapper: FunctionComponent = ({ appState }) => { + const { selectedFiles, showFileContextMenu } = appState.files + + const selectedFile = selectedFiles[0] + + if (!showFileContextMenu || !selectedFile) { + return null + } + + return +} + +export default observer(FileContextMenuWrapper) diff --git a/app/assets/javascripts/Components/FileContextMenu/FileMenuOptions.tsx b/app/assets/javascripts/Components/FileContextMenu/FileMenuOptions.tsx index 17bcbcb2b..01909bee6 100644 --- a/app/assets/javascripts/Components/FileContextMenu/FileMenuOptions.tsx +++ b/app/assets/javascripts/Components/FileContextMenu/FileMenuOptions.tsx @@ -1,9 +1,9 @@ import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' import { FileItem } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' import { PopoverFileItemAction, PopoverFileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction' -import { Icon } from '@/Components/Icon/Icon' -import { Switch } from '@/Components/Switch/Switch' +import Icon from '@/Components/Icon/Icon' +import Switch from '@/Components/Switch/Switch' type Props = { closeMenu: () => void @@ -17,7 +17,7 @@ type Props = { shouldShowAttachOption: boolean } -export const FileMenuOptions: FunctionComponent = ({ +const FileMenuOptions: FunctionComponent = ({ closeMenu, closeOnBlur, file, @@ -139,3 +139,5 @@ export const FileMenuOptions: FunctionComponent = ({ ) } + +export default FileMenuOptions diff --git a/app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx b/app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx index c12f9e356..20afa53d2 100644 --- a/app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx +++ b/app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx @@ -1,13 +1,13 @@ import { formatSizeToReadableString } from '@standardnotes/filepicker' import { FileItem } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { Icon } from '@/Components/Icon/Icon' +import { FunctionComponent } from 'react' +import Icon from '@/Components/Icon/Icon' type Props = { file: FileItem } -export const FilePreviewInfoPanel: FunctionComponent = ({ file }) => { +const FilePreviewInfoPanel: FunctionComponent = ({ file }) => { return (
@@ -35,3 +35,5 @@ export const FilePreviewInfoPanel: FunctionComponent = ({ file }) => {
) } + +export default FilePreviewInfoPanel diff --git a/app/assets/javascripts/Components/Files/FilePreviewModal.tsx b/app/assets/javascripts/Components/Files/FilePreviewModal.tsx index b9795bd6a..503f9d76d 100644 --- a/app/assets/javascripts/Components/Files/FilePreviewModal.tsx +++ b/app/assets/javascripts/Components/Files/FilePreviewModal.tsx @@ -3,14 +3,13 @@ import { concatenateUint8Arrays } from '@/Utils/ConcatenateUint8Arrays' import { DialogContent, DialogOverlay } from '@reach/dialog' import { addToast, ToastType } from '@standardnotes/stylekit' import { NoPreviewIllustration } from '@standardnotes/icons' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' -import { getFileIconComponent } from '@/Components/AttachedFilesPopover/PopoverFileItem' -import { Button } from '@/Components/Button/Button' -import { Icon } from '@/Components/Icon/Icon' -import { FilePreviewInfoPanel } from './FilePreviewInfoPanel' +import { FunctionComponent, KeyboardEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { getFileIconComponent } from '@/Components/AttachedFilesPopover/getFileIconComponent' +import Button from '@/Components/Button/Button' +import Icon from '@/Components/Icon/Icon' +import FilePreviewInfoPanel from './FilePreviewInfoPanel' import { isFileTypePreviewable } from './isFilePreviewable' -import { PreviewComponent } from './PreviewComponent' +import PreviewComponent from './PreviewComponent' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' import { KeyboardKey } from '@/Services/IOService' import { AppState } from '@/UIModels/AppState' @@ -21,10 +20,6 @@ type Props = { appState: AppState } -export const FilePreviewModalWrapper: FunctionComponent = observer(({ application, appState }) => { - return appState.filePreviewModal.isOpen ? : null -}) - const FilePreviewModal: FunctionComponent = observer(({ application, appState }) => { const { currentFile, setCurrentFile, otherFiles, dismiss } = appState.filePreviewModal @@ -91,8 +86,8 @@ const FilePreviewModal: FunctionComponent = observer(({ application, appS } }, [currentFile, getObjectUrl, objectUrl]) - const keyDownHandler = useCallback( - (event: KeyboardEvent) => { + const keyDownHandler: KeyboardEventHandler = useCallback( + (event) => { if (event.key !== KeyboardKey.Left && event.key !== KeyboardKey.Right) { return } @@ -141,6 +136,7 @@ const FilePreviewModal: FunctionComponent = observer(({ application, appS dangerouslyBypassScrollLock > = observer(({ application, appS ) }) + +FilePreviewModal.displayName = 'FilePreviewModal' + +const FilePreviewModalWrapper: FunctionComponent = ({ application, appState }) => { + return appState.filePreviewModal.isOpen ? : null +} + +export default observer(FilePreviewModalWrapper) diff --git a/app/assets/javascripts/Components/Files/ImagePreview.tsx b/app/assets/javascripts/Components/Files/ImagePreview.tsx index a07d75d29..6d4e4fb82 100644 --- a/app/assets/javascripts/Components/Files/ImagePreview.tsx +++ b/app/assets/javascripts/Components/Files/ImagePreview.tsx @@ -1,13 +1,12 @@ import { IconType } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { useRef, useState } from 'preact/hooks' -import { IconButton } from '../Button/IconButton' +import { FunctionComponent, useRef, useState } from 'react' +import IconButton from '../Button/IconButton' type Props = { objectUrl: string } -export const ImagePreview: FunctionComponent = ({ objectUrl }) => { +const ImagePreview: FunctionComponent = ({ objectUrl }) => { const initialImgHeightRef = useRef() const [imageZoomPercent, setImageZoomPercent] = useState(100) @@ -21,8 +20,8 @@ export const ImagePreview: FunctionComponent = ({ objectUrl }) => { height: `${imageZoomPercent}%`, ...(imageZoomPercent <= 100 ? { - 'min-width': '100%', - 'object-fit': 'contain', + minWidth: '100%', + objectFit: 'contain', } : { position: 'absolute', @@ -69,3 +68,5 @@ export const ImagePreview: FunctionComponent = ({ objectUrl }) => {
) } + +export default ImagePreview diff --git a/app/assets/javascripts/Components/Files/PreviewComponent.tsx b/app/assets/javascripts/Components/Files/PreviewComponent.tsx index 4ab0e352e..b0729bbd0 100644 --- a/app/assets/javascripts/Components/Files/PreviewComponent.tsx +++ b/app/assets/javascripts/Components/Files/PreviewComponent.tsx @@ -1,13 +1,13 @@ import { FileItem } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { ImagePreview } from './ImagePreview' +import { FunctionComponent } from 'react' +import ImagePreview from './ImagePreview' type Props = { file: FileItem objectUrl: string } -export const PreviewComponent: FunctionComponent = ({ file, objectUrl }) => { +const PreviewComponent: FunctionComponent = ({ file, objectUrl }) => { if (file.mimeType.startsWith('image/')) { return } @@ -22,3 +22,5 @@ export const PreviewComponent: FunctionComponent = ({ file, objectUrl }) return } + +export default PreviewComponent diff --git a/app/assets/javascripts/Components/Footer/Footer.tsx b/app/assets/javascripts/Components/Footer/Footer.tsx index cc3a80f6b..71403bfa4 100644 --- a/app/assets/javascripts/Components/Footer/Footer.tsx +++ b/app/assets/javascripts/Components/Footer/Footer.tsx @@ -11,12 +11,12 @@ import { STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON, } from '@/Strings' import { alertDialog, confirmDialog } from '@/Services/AlertService' -import { AccountMenu } from '@/Components/AccountMenu/AccountMenu' +import AccountMenu from '@/Components/AccountMenu/AccountMenu' import { AppStateEvent, EventSource } from '@/UIModels/AppState' -import { Icon } from '@/Components/Icon/Icon' -import { QuickSettingsMenu } from '@/Components/QuickSettingsMenu/QuickSettingsMenu' -import { SyncResolutionMenu } from '@/Components/SyncResolutionMenu/SyncResolutionMenu' -import { Fragment } from 'preact' +import Icon from '@/Components/Icon/Icon' +import QuickSettingsMenu from '@/Components/QuickSettingsMenu/QuickSettingsMenu' +import SyncResolutionMenu from '@/Components/SyncResolutionMenu/SyncResolutionMenu' +import { Fragment } from 'react' import { AccountMenuPane } from '../AccountMenu/AccountMenuPane' type Props = { @@ -39,7 +39,7 @@ type State = { arbitraryStatusMessage?: string } -export class Footer extends PureComponent { +class Footer extends PureComponent { public user?: unknown private didCheckForOffline = false private completedInitialSync = false @@ -455,3 +455,5 @@ export class Footer extends PureComponent { ) } } + +export default Footer diff --git a/app/assets/javascripts/Components/Icon/Icon.tsx b/app/assets/javascripts/Components/Icon/Icon.tsx index cb3593814..c9a0ce930 100644 --- a/app/assets/javascripts/Components/Icon/Icon.tsx +++ b/app/assets/javascripts/Components/Icon/Icon.tsx @@ -1,4 +1,4 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' import { IconType } from '@standardnotes/snjs' import { @@ -187,7 +187,7 @@ type Props = { ariaLabel?: string } -export const Icon: FunctionalComponent = ({ type, className = '', ariaLabel }) => { +const Icon: FunctionComponent = ({ type, className = '', ariaLabel }) => { const IconComponent = ICONS[type as keyof typeof ICONS] if (!IconComponent) { return null @@ -200,3 +200,5 @@ export const Icon: FunctionalComponent = ({ type, className = '', ariaLab /> ) } + +export default Icon diff --git a/app/assets/javascripts/Components/Input/DecoratedInput.tsx b/app/assets/javascripts/Components/Input/DecoratedInput.tsx index 6c738d418..21919ed64 100644 --- a/app/assets/javascripts/Components/Input/DecoratedInput.tsx +++ b/app/assets/javascripts/Components/Input/DecoratedInput.tsx @@ -1,5 +1,4 @@ -import { FunctionalComponent, Ref } from 'preact' -import { forwardRef } from 'preact/compat' +import { forwardRef, Fragment, Ref } from 'react' import { DecoratedInputProps } from './DecoratedInputProps' const getClassNames = (hasLeftDecorations: boolean, hasRightDecorations: boolean) => { @@ -17,7 +16,7 @@ const getClassNames = (hasLeftDecorations: boolean, hasRightDecorations: boolean /** * Input that can be decorated on the left and right side */ -export const DecoratedInput: FunctionalComponent = forwardRef( +const DecoratedInput = forwardRef( ( { type = 'text', @@ -42,8 +41,8 @@ export const DecoratedInput: FunctionalComponent = forwardR
{left && (
- {left.map((leftChild) => ( - <>{leftChild} + {left.map((leftChild, index) => ( + {leftChild} ))}
)} @@ -58,14 +57,16 @@ export const DecoratedInput: FunctionalComponent = forwardR onFocus={onFocus} onKeyDown={onKeyDown} data-lpignore={type !== 'password' ? true : false} - autocomplete={autocomplete ? 'on' : 'off'} + autoComplete={autocomplete ? 'on' : 'off'} ref={ref} /> {right && (
{right.map((rightChild, index) => ( -
0 ? 'ml-3' : ''}>{rightChild}
+
0 ? 'ml-3' : ''} key={index}> + {rightChild} +
))}
)} @@ -73,3 +74,5 @@ export const DecoratedInput: FunctionalComponent = forwardR ) }, ) + +export default DecoratedInput diff --git a/app/assets/javascripts/Components/Input/DecoratedInputProps.ts b/app/assets/javascripts/Components/Input/DecoratedInputProps.ts index 6499f000e..05272efdb 100644 --- a/app/assets/javascripts/Components/Input/DecoratedInputProps.ts +++ b/app/assets/javascripts/Components/Input/DecoratedInputProps.ts @@ -1,15 +1,15 @@ -import { ComponentChild } from 'preact' +import { FocusEventHandler, KeyboardEventHandler, ReactNode } from 'react' export type DecoratedInputProps = { type?: 'text' | 'email' | 'password' className?: string disabled?: boolean - left?: ComponentChild[] - right?: ComponentChild[] + left?: ReactNode[] + right?: ReactNode[] value?: string placeholder?: string onChange?: (text: string) => void - onFocus?: (event: FocusEvent) => void - onKeyDown?: (event: KeyboardEvent) => void + onFocus?: FocusEventHandler + onKeyDown?: KeyboardEventHandler autocomplete?: boolean } diff --git a/app/assets/javascripts/Components/Input/DecoratedPasswordInput.tsx b/app/assets/javascripts/Components/Input/DecoratedPasswordInput.tsx index 7c1db6294..31ddd09c0 100644 --- a/app/assets/javascripts/Components/Input/DecoratedPasswordInput.tsx +++ b/app/assets/javascripts/Components/Input/DecoratedPasswordInput.tsx @@ -1,13 +1,11 @@ -import { FunctionComponent, Ref } from 'preact' -import { forwardRef } from 'preact/compat' -import { StateUpdater, useState } from 'preact/hooks' -import { DecoratedInput } from './DecoratedInput' -import { IconButton } from '@/Components/Button/IconButton' +import { Dispatch, FunctionComponent, Ref, SetStateAction, forwardRef, useState } from 'react' +import DecoratedInput from './DecoratedInput' +import IconButton from '@/Components/Button/IconButton' import { DecoratedInputProps } from './DecoratedInputProps' const Toggle: FunctionComponent<{ isToggled: boolean - setIsToggled: StateUpdater + setIsToggled: Dispatch> }> = ({ isToggled, setIsToggled }) => ( > = forwardRef( - (props, ref: Ref) => { - const [isToggled, setIsToggled] = useState(false) +const DecoratedPasswordInput = forwardRef((props: DecoratedInputProps, ref: Ref) => { + const [isToggled, setIsToggled] = useState(false) - const rightSideDecorations = props.right ? [...props.right] : [] + const rightSideDecorations = props.right ? [...props.right] : [] - return ( - ]} - /> - ) - }, -) + return ( + ]} + /> + ) +}) + +export default DecoratedPasswordInput diff --git a/app/assets/javascripts/Components/Input/FloatingLabelInput.tsx b/app/assets/javascripts/Components/Input/FloatingLabelInput.tsx index d554f80ef..049ac9f04 100644 --- a/app/assets/javascripts/Components/Input/FloatingLabelInput.tsx +++ b/app/assets/javascripts/Components/Input/FloatingLabelInput.tsx @@ -1,14 +1,11 @@ -import { FunctionComponent, Ref } from 'preact' -import { JSXInternal } from 'preact/src/jsx' -import { forwardRef } from 'preact/compat' -import { useState } from 'preact/hooks' +import { ChangeEventHandler, Ref, forwardRef, useState } from 'react' type Props = { id: string type: 'text' | 'email' | 'password' label: string value: string - onChange: JSXInternal.GenericEventHandler + onChange: ChangeEventHandler disabled?: boolean className?: string labelClassName?: string @@ -16,7 +13,7 @@ type Props = { isInvalid?: boolean } -export const FloatingLabelInput: FunctionComponent = forwardRef( +const FloatingLabelInput = forwardRef( ( { id, @@ -71,3 +68,5 @@ export const FloatingLabelInput: FunctionComponent = forwardRef( ) }, ) + +export default FloatingLabelInput diff --git a/app/assets/javascripts/Components/Input/Input.tsx b/app/assets/javascripts/Components/Input/Input.tsx index a5fc45476..8edf7a4fa 100644 --- a/app/assets/javascripts/Components/Input/Input.tsx +++ b/app/assets/javascripts/Components/Input/Input.tsx @@ -1,4 +1,4 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' interface Props { text?: string @@ -6,9 +6,11 @@ interface Props { className?: string } -export const Input: FunctionalComponent = ({ className = '', disabled = false, text }) => { +const Input: FunctionComponent = ({ className = '', disabled = false, text }) => { const base = 'rounded py-1.5 px-3 text-input my-1 h-8 bg-contrast' const stateClasses = disabled ? 'no-border' : 'border-solid border-1 border-main' const classes = `${base} ${stateClasses} ${className}` return } + +export default Input diff --git a/app/assets/javascripts/Components/Menu/Menu.tsx b/app/assets/javascripts/Components/Menu/Menu.tsx index 74410c2f9..61b714f32 100644 --- a/app/assets/javascripts/Components/Menu/Menu.tsx +++ b/app/assets/javascripts/Components/Menu/Menu.tsx @@ -1,21 +1,26 @@ -import { JSX, FunctionComponent, ComponentChildren, VNode, RefCallback, ComponentChild, toChildArray } from 'preact' -import { useCallback, useEffect, useRef } from 'preact/hooks' -import { JSXInternal } from 'preact/src/jsx' -import { MenuItem, MenuItemListElement } from './MenuItem' +import { + CSSProperties, + FunctionComponent, + KeyboardEventHandler, + ReactNode, + useCallback, + useEffect, + useRef, +} from 'react' import { KeyboardKey } from '@/Services/IOService' import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation' type MenuProps = { className?: string - style?: string | JSX.CSSProperties | undefined + style?: CSSProperties | undefined a11yLabel: string - children: ComponentChildren + children: ReactNode closeMenu?: () => void isOpen: boolean initialFocus?: number } -export const Menu: FunctionComponent = ({ +const Menu: FunctionComponent = ({ children, className = '', style, @@ -24,16 +29,10 @@ export const Menu: FunctionComponent = ({ isOpen, initialFocus, }: MenuProps) => { - const menuItemRefs = useRef<(HTMLButtonElement | null)[]>([]) - const menuElementRef = useRef(null) - const handleKeyDown: JSXInternal.KeyboardEventHandler = useCallback( + const handleKeyDown: KeyboardEventHandler = useCallback( (event) => { - if (!menuItemRefs.current) { - return - } - if (event.key === KeyboardKey.Escape) { closeMenu?.() return @@ -45,58 +44,13 @@ export const Menu: FunctionComponent = ({ useListKeyboardNavigation(menuElementRef, initialFocus) useEffect(() => { - if (isOpen && menuItemRefs.current.length > 0) { + if (isOpen) { setTimeout(() => { menuElementRef.current?.focus() }) } }, [isOpen]) - const pushRefToArray: RefCallback = useCallback((instance) => { - if (instance && instance.children) { - Array.from(instance.children).forEach((child) => { - if ( - child.getAttribute('role')?.includes('menuitem') && - !menuItemRefs.current.includes(child as HTMLButtonElement) - ) { - menuItemRefs.current.push(child as HTMLButtonElement) - } - }) - } - }, []) - - const mapMenuItems = useCallback( - (child: ComponentChild, index: number, array: ComponentChild[]): ComponentChild => { - if (!child || (Array.isArray(child) && child.length < 1)) { - return - } - - if (Array.isArray(child)) { - return child.map(mapMenuItems) - } - - const _child = child as VNode - const isFirstMenuItem = index === array.findIndex((child) => (child as VNode).type === MenuItem) - - const hasMultipleItems = Array.isArray(_child.props.children) - ? Array.from(_child.props.children as ComponentChild[]).some( - (child) => (child as VNode).type === MenuItem, - ) - : false - - const items = hasMultipleItems ? [...(_child.props.children as ComponentChild[])] : [_child] - - return items.map((child) => { - return ( - - {child} - - ) - }) - }, - [pushRefToArray], - ) - return ( = ({ style={style} aria-label={a11yLabel} > - {toChildArray(children).map(mapMenuItems)} + {children} ) } + +export default Menu diff --git a/app/assets/javascripts/Components/Menu/MenuItem.tsx b/app/assets/javascripts/Components/Menu/MenuItem.tsx index aa2be8a30..afdae1e3a 100644 --- a/app/assets/javascripts/Components/Menu/MenuItem.tsx +++ b/app/assets/javascripts/Components/Menu/MenuItem.tsx @@ -1,21 +1,15 @@ -import { ComponentChildren, FunctionComponent, VNode } from 'preact' -import { forwardRef, Ref } from 'preact/compat' -import { JSXInternal } from 'preact/src/jsx' -import { Icon } from '@/Components/Icon/Icon' -import { Switch, SwitchProps } from '@/Components/Switch/Switch' +import { forwardRef, MouseEventHandler, ReactNode, Ref } from 'react' +import Icon from '@/Components/Icon/Icon' +import Switch from '@/Components/Switch/Switch' +import { SwitchProps } from '@/Components/Switch/SwitchProps' import { IconType } from '@standardnotes/snjs' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' - -export enum MenuItemType { - IconButton, - RadioButton, - SwitchButton, -} +import { MenuItemType } from './MenuItemType' type MenuItemProps = { type: MenuItemType - children: ComponentChildren - onClick?: JSXInternal.MouseEventHandler + children: ReactNode + onClick?: MouseEventHandler onChange?: SwitchProps['onChange'] onBlur?: (event: { relatedTarget: EventTarget | null }) => void className?: string @@ -25,7 +19,7 @@ type MenuItemProps = { tabIndex?: number } -export const MenuItem: FunctionComponent = forwardRef( +const MenuItem = forwardRef( ( { children, @@ -42,63 +36,42 @@ export const MenuItem: FunctionComponent = forwardRef( ref: Ref, ) => { return type === MenuItemType.SwitchButton && typeof onChange === 'function' ? ( - +
  • + +
  • ) : ( - - ) - }, -) - -export const MenuItemSeparator: FunctionComponent = () =>
    - -type ListElementProps = { - isFirstMenuItem: boolean - children: ComponentChildren -} - -export const MenuItemListElement: FunctionComponent = forwardRef( - ({ children, isFirstMenuItem }: ListElementProps, ref: Ref) => { - const child = children as VNode - return ( -
  • - {{ - ...child, - props: { - ...(child.props ? { ...child.props } : {}), - ...(child.type === MenuItem - ? { - tabIndex: isFirstMenuItem ? 0 : -1, - } - : {}), - }, - }} +
  • +
  • ) }, ) + +export default MenuItem diff --git a/app/assets/javascripts/Components/Menu/MenuItemSeparator.tsx b/app/assets/javascripts/Components/Menu/MenuItemSeparator.tsx new file mode 100644 index 000000000..00a6d97b3 --- /dev/null +++ b/app/assets/javascripts/Components/Menu/MenuItemSeparator.tsx @@ -0,0 +1,9 @@ +import { FunctionComponent } from 'react' + +const MenuItemSeparator: FunctionComponent = () => ( +
  • +
    +
  • +) + +export default MenuItemSeparator diff --git a/app/assets/javascripts/Components/Menu/MenuItemType.ts b/app/assets/javascripts/Components/Menu/MenuItemType.ts new file mode 100644 index 000000000..cfeb9eb53 --- /dev/null +++ b/app/assets/javascripts/Components/Menu/MenuItemType.ts @@ -0,0 +1,5 @@ +export enum MenuItemType { + IconButton, + RadioButton, + SwitchButton, +} diff --git a/app/assets/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx b/app/assets/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx index b22577124..cc8bb217b 100644 --- a/app/assets/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx +++ b/app/assets/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx @@ -1,18 +1,18 @@ import { AppState } from '@/UIModels/AppState' import { IlNotesIcon } from '@standardnotes/icons' import { observer } from 'mobx-react-lite' -import { NotesOptionsPanel } from '@/Components/NotesOptions/NotesOptionsPanel' +import NotesOptionsPanel from '@/Components/NotesOptions/NotesOptionsPanel' import { WebApplication } from '@/UIModels/Application' -import { PinNoteButton } from '@/Components/PinNoteButton/PinNoteButton' -import { Button } from '../Button/Button' -import { useCallback } from 'preact/hooks' +import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton' +import Button from '../Button/Button' +import { useCallback } from 'react' type Props = { application: WebApplication appState: AppState } -export const MultipleSelectedNotes = observer(({ application, appState }: Props) => { +const MultipleSelectedNotes = ({ application, appState }: Props) => { const count = appState.notes.selectedNotesCount const cancelMultipleSelection = useCallback(() => { @@ -40,4 +40,6 @@ export const MultipleSelectedNotes = observer(({ application, appState }: Props)
    ) -}) +} + +export default observer(MultipleSelectedNotes) diff --git a/app/assets/javascripts/Components/Navigation/Navigation.tsx b/app/assets/javascripts/Components/Navigation/Navigation.tsx index 648a9890f..f88ef3ff5 100644 --- a/app/assets/javascripts/Components/Navigation/Navigation.tsx +++ b/app/assets/javascripts/Components/Navigation/Navigation.tsx @@ -1,18 +1,17 @@ -import { SmartViewsSection } from '@/Components/Tags/SmartViewsSection' -import { TagsSection } from '@/Components/Tags/TagsSection' +import SmartViewsSection from '@/Components/Tags/SmartViewsSection' +import TagsSection from '@/Components/Tags/TagsSection' import { WebApplication } from '@/UIModels/Application' import { PANEL_NAME_NAVIGATION } from '@/Constants' import { ApplicationEvent, PrefKey } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useMemo, useState } from 'preact/hooks' -import { PanelSide, ResizeFinishCallback, PanelResizer, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' +import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react' +import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' type Props = { application: WebApplication } -export const Navigation: FunctionComponent = observer(({ application }) => { +const Navigation: FunctionComponent = ({ application }) => { const appState = useMemo(() => application.getAppState(), [application]) const [ref, setRef] = useState() const [panelWidth, setPanelWidth] = useState(0) @@ -79,4 +78,6 @@ export const Navigation: FunctionComponent = observer(({ application }) = )} ) -}) +} + +export default observer(Navigation) diff --git a/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx b/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx index 52b30c563..2792fc803 100644 --- a/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx +++ b/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx @@ -1,18 +1,13 @@ -import { Icon } from '@/Components/Icon/Icon' +import Icon from '@/Components/Icon/Icon' import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { useCallback } from 'preact/hooks' +import { MouseEventHandler, useCallback } from 'react' type Props = { appState: AppState } -export const NoAccountWarning = observer(({ appState }: Props) => { - const canShow = appState.noAccountWarning.show - if (!canShow) { - return null - } - - const showAccountMenu = useCallback( - (event: Event) => { +const NoAccountWarning = observer(({ appState }: Props) => { + const showAccountMenu: MouseEventHandler = useCallback( + (event) => { event.stopPropagation() appState.accountMenu.setShow(true) }, @@ -32,9 +27,9 @@ export const NoAccountWarning = observer(({ appState }: Props) => { ) -}) +} + +export default observer(NoteTag) diff --git a/app/assets/javascripts/Components/NoteTags/NoteTagsContainer.tsx b/app/assets/javascripts/Components/NoteTags/NoteTagsContainer.tsx index 3ce97c92c..c3f392b65 100644 --- a/app/assets/javascripts/Components/NoteTags/NoteTagsContainer.tsx +++ b/app/assets/javascripts/Components/NoteTags/NoteTagsContainer.tsx @@ -1,19 +1,14 @@ import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { AutocompleteTagInput } from '@/Components/TagAutocomplete/AutocompleteTagInput' -import { NoteTag } from './NoteTag' -import { useEffect } from 'preact/hooks' -import { isStateDealloced } from '@/UIModels/AppState/AbstractState' +import AutocompleteTagInput from '@/Components/TagAutocomplete/AutocompleteTagInput' +import NoteTag from './NoteTag' +import { useEffect } from 'react' type Props = { appState: AppState } -export const NoteTagsContainer = observer(({ appState }: Props) => { - if (isStateDealloced(appState)) { - return null - } - +const NoteTagsContainer = ({ appState }: Props) => { const { tags, tagsContainerMaxWidth } = appState.noteTags useEffect(() => { @@ -33,4 +28,6 @@ export const NoteTagsContainer = observer(({ appState }: Props) => { ) -}) +} + +export default observer(NoteTagsContainer) diff --git a/app/assets/javascripts/Components/NoteView/EditingDisabledBanner.tsx b/app/assets/javascripts/Components/NoteView/EditingDisabledBanner.tsx index 3584b3218..bb47c4842 100644 --- a/app/assets/javascripts/Components/NoteView/EditingDisabledBanner.tsx +++ b/app/assets/javascripts/Components/NoteView/EditingDisabledBanner.tsx @@ -1,5 +1,5 @@ -import { FunctionComponent } from 'preact' -import { Icon } from '../Icon/Icon' +import { FunctionComponent } from 'react' +import Icon from '../Icon/Icon' type Props = { onMouseLeave: () => void @@ -9,7 +9,7 @@ type Props = { lockText: string } -export const EditingDisabledBanner: FunctionComponent = ({ +const EditingDisabledBanner: FunctionComponent = ({ onMouseLeave, onMouseOver, onClick, @@ -36,3 +36,5 @@ export const EditingDisabledBanner: FunctionComponent = ({ ) } + +export default EditingDisabledBanner diff --git a/app/assets/javascripts/Components/NoteView/NoteView.test.ts b/app/assets/javascripts/Components/NoteView/NoteView.test.ts index 2e6317cce..0ec6d0a1e 100644 --- a/app/assets/javascripts/Components/NoteView/NoteView.test.ts +++ b/app/assets/javascripts/Components/NoteView/NoteView.test.ts @@ -12,7 +12,7 @@ import { SNNote, } from '@standardnotes/snjs' -import { NoteView } from './NoteView' +import NoteView from './NoteView' describe('NoteView', () => { let noteViewController: NoteViewController diff --git a/app/assets/javascripts/Components/NoteView/NoteView.tsx b/app/assets/javascripts/Components/NoteView/NoteView.tsx index 7e44b11be..2be437fe7 100644 --- a/app/assets/javascripts/Components/NoteView/NoteView.tsx +++ b/app/assets/javascripts/Components/NoteView/NoteView.tsx @@ -1,4 +1,4 @@ -import { createRef, JSX, RefObject } from 'preact' +import { ChangeEventHandler, createRef, KeyboardEventHandler, RefObject } from 'react' import { ApplicationEvent, isPayloadSourceRetrieved, @@ -19,16 +19,16 @@ import { KeyboardModifier, KeyboardKey } from '@/Services/IOService' import { STRING_DELETE_PLACEHOLDER_ATTEMPT, STRING_DELETE_LOCKED_ATTEMPT, StringDeleteNote } from '@/Strings' import { confirmDialog } from '@/Services/AlertService' import { PureComponent } from '@/Components/Abstract/PureComponent' -import { ProtectedNoteOverlay } from '@/Components/ProtectedNoteOverlay/ProtectedNoteOverlay' -import { PinNoteButton } from '@/Components/PinNoteButton/PinNoteButton' -import { NotesOptionsPanel } from '@/Components/NotesOptions/NotesOptionsPanel' -import { NoteTagsContainer } from '@/Components/NoteTags/NoteTagsContainer' -import { ComponentView } from '@/Components/ComponentView/ComponentView' -import { PanelSide, PanelResizer, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' +import ProtectedNoteOverlay from '@/Components/ProtectedNoteOverlay/ProtectedNoteOverlay' +import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton' +import NotesOptionsPanel from '@/Components/NotesOptions/NotesOptionsPanel' +import NoteTagsContainer from '@/Components/NoteTags/NoteTagsContainer' +import ComponentView from '@/Components/ComponentView/ComponentView' +import PanelResizer, { PanelSide, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' import { ElementIds } from '@/ElementIDs' -import { ChangeEditorButton } from '@/Components/ChangeEditor/ChangeEditorButton' -import { AttachedFilesButton } from '@/Components/AttachedFilesPopover/AttachedFilesButton' -import { EditingDisabledBanner } from './EditingDisabledBanner' +import ChangeEditorButton from '@/Components/ChangeEditor/ChangeEditorButton' +import AttachedFilesButton from '@/Components/AttachedFilesPopover/AttachedFilesButton' +import EditingDisabledBanner from './EditingDisabledBanner' import { transactionForAssociateComponentWithCurrentNote, transactionForDisassociateComponentWithCurrentNote, @@ -78,7 +78,7 @@ type State = { rightResizerOffset: number } -export class NoteView extends PureComponent { +class NoteView extends PureComponent { readonly controller!: NoteViewController private statusTimeout?: NodeJS.Timeout @@ -528,7 +528,7 @@ export class NoteView extends PureComponent { } } - onTextAreaChange = ({ currentTarget }: JSX.TargetedEvent) => { + onTextAreaChange: ChangeEventHandler = ({ currentTarget }) => { const text = currentTarget.value this.setState({ editorText: text, @@ -548,12 +548,16 @@ export class NoteView extends PureComponent { .catch(console.error) } - onTitleEnter = ({ currentTarget }: JSX.TargetedEvent) => { + onTitleEnter: KeyboardEventHandler = ({ key, currentTarget }) => { + if (key !== KeyboardKey.Enter) { + return + } + currentTarget.blur() this.focusEditor() } - onTitleChange = ({ currentTarget }: JSX.TargetedEvent) => { + onTitleChange: ChangeEventHandler = ({ currentTarget }) => { const title = currentTarget.value this.setState({ editorTitle: title, @@ -911,12 +915,12 @@ export class NoteView extends PureComponent { id={ElementIds.NoteTitleEditor} onChange={this.onTitleChange} onFocus={(event) => { - ;(event.target as HTMLTextAreaElement).select() + event.target.select() }} - onKeyUp={(event) => event.keyCode == 13 && this.onTitleEnter(event)} - spellcheck={false} + onKeyUp={this.onTitleEnter} + spellCheck={false} value={this.state.editorTitle} - autocomplete="off" + autoComplete="off" /> @@ -996,15 +1000,15 @@ export class NoteView extends PureComponent { {this.state.editorStateDidLoad && !this.state.editorComponentViewer && !this.state.textareaUnloading && ( )} @@ -1059,7 +1063,7 @@ export class NoteView extends PureComponent {
    {this.state.stackComponentViewers.map((viewer) => { return ( -
    +
    { ) } } + +export default NoteView diff --git a/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx b/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx index d3968d2ab..33f0a1be6 100644 --- a/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx +++ b/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx @@ -2,8 +2,8 @@ import { AppState } from '@/UIModels/AppState' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside' import { observer } from 'mobx-react-lite' -import { NotesOptions } from '@/Components/NotesOptions/NotesOptions' -import { useCallback, useEffect, useRef } from 'preact/hooks' +import NotesOptions from '@/Components/NotesOptions/NotesOptions' +import { useCallback, useEffect, useRef } from 'react' import { WebApplication } from '@/UIModels/Application' type Props = { @@ -11,7 +11,7 @@ type Props = { appState: AppState } -export const NotesContextMenu = observer(({ application, appState }: Props) => { +const NotesContextMenu = ({ application, appState }: Props) => { const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = appState.notes const contextMenuRef = useRef(null) @@ -42,4 +42,6 @@ export const NotesContextMenu = observer(({ application, appState }: Props) => {
    ) : null -}) +} + +export default observer(NotesContextMenu) diff --git a/app/assets/javascripts/Components/NotesOptions/AccordionMenuGroup.tsx b/app/assets/javascripts/Components/NotesOptions/AccordionMenuGroup.tsx new file mode 100644 index 000000000..afd41da6d --- /dev/null +++ b/app/assets/javascripts/Components/NotesOptions/AccordionMenuGroup.tsx @@ -0,0 +1,8 @@ +import { IconType } from '@standardnotes/snjs' + +export type AccordionMenuGroup = { + icon?: IconType + iconClassName?: string + title: string + items: Array +} diff --git a/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx b/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx index f409eb62a..1eb63d3f7 100644 --- a/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx +++ b/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx @@ -2,16 +2,15 @@ import { AppState } from '@/UIModels/AppState' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' +import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' +import Icon from '@/Components/Icon/Icon' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' type Props = { appState: AppState } -export const AddTagOption: FunctionComponent = observer(({ appState }) => { +const AddTagOption: FunctionComponent = ({ appState }) => { const menuContainerRef = useRef(null) const menuRef = useRef(null) const menuButtonRef = useRef(null) @@ -87,7 +86,7 @@ export const AddTagOption: FunctionComponent = observer(({ appState }) => > {appState.tags.tags.map((tag) => (
    ) -}) +} + +export default observer(AddTagOption) diff --git a/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx b/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx index 344661488..4d6ab456e 100644 --- a/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx +++ b/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx @@ -2,11 +2,10 @@ import { KeyboardKey } from '@/Services/IOService' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' -import { IconType, SNComponent, SNNote } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' -import { ChangeEditorMenu } from '@/Components/ChangeEditor/ChangeEditorMenu' +import { SNNote } from '@standardnotes/snjs' +import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' +import Icon from '@/Components/Icon/Icon' +import ChangeEditorMenu from '@/Components/ChangeEditor/ChangeEditorMenu' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' @@ -16,22 +15,7 @@ type ChangeEditorOptionProps = { note: SNNote } -type AccordionMenuGroup = { - icon?: IconType - iconClassName?: string - title: string - items: Array -} - -export type EditorMenuItem = { - name: string - component?: SNComponent - isEntitled: boolean -} - -export type EditorMenuGroup = AccordionMenuGroup - -export const ChangeEditorOption: FunctionComponent = ({ application, note }) => { +const ChangeEditorOption: FunctionComponent = ({ application, note }) => { const [isOpen, setIsOpen] = useState(false) const [isVisible, setIsVisible] = useState(false) const [menuStyle, setMenuStyle] = useState({ @@ -121,3 +105,5 @@ export const ChangeEditorOption: FunctionComponent = ({
    ) } + +export default ChangeEditorOption diff --git a/app/assets/javascripts/Components/NotesOptions/EditorMenuGroup.tsx b/app/assets/javascripts/Components/NotesOptions/EditorMenuGroup.tsx new file mode 100644 index 000000000..cf541458b --- /dev/null +++ b/app/assets/javascripts/Components/NotesOptions/EditorMenuGroup.tsx @@ -0,0 +1,4 @@ +import { EditorMenuItem } from './EditorMenuItem' +import { AccordionMenuGroup } from './AccordionMenuGroup' + +export type EditorMenuGroup = AccordionMenuGroup diff --git a/app/assets/javascripts/Components/NotesOptions/EditorMenuItem.tsx b/app/assets/javascripts/Components/NotesOptions/EditorMenuItem.tsx new file mode 100644 index 000000000..d39a2850d --- /dev/null +++ b/app/assets/javascripts/Components/NotesOptions/EditorMenuItem.tsx @@ -0,0 +1,7 @@ +import { SNComponent } from '@standardnotes/snjs' + +export type EditorMenuItem = { + name: string + component?: SNComponent + isEntitled: boolean +} diff --git a/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx b/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx index 176bd9d4e..985bebb3c 100644 --- a/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx +++ b/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx @@ -2,9 +2,8 @@ import { WebApplication } from '@/UIModels/Application' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { Action, ListedAccount, SNNote } from '@standardnotes/snjs' -import { Fragment, FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' +import { Fragment, FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' +import Icon from '@/Components/Icon/Icon' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' type Props = { @@ -206,7 +205,7 @@ const ListedActionsMenu: FunctionComponent = ({ applicat ) } -export const ListedActionsOption: FunctionComponent = ({ application, note }) => { +const ListedActionsOption: FunctionComponent = ({ application, note }) => { const menuContainerRef = useRef(null) const menuRef = useRef(null) const menuButtonRef = useRef(null) @@ -273,3 +272,5 @@ export const ListedActionsOption: FunctionComponent = ({ application, not ) } + +export default ListedActionsOption diff --git a/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx b/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx index 793f2d27d..0fbb9af06 100644 --- a/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx +++ b/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx @@ -1,23 +1,16 @@ import { AppState } from '@/UIModels/AppState' -import { Icon } from '@/Components/Icon/Icon' -import { Switch } from '@/Components/Switch/Switch' +import Icon from '@/Components/Icon/Icon' +import Switch from '@/Components/Switch/Switch' import { observer } from 'mobx-react-lite' -import { useState, useEffect, useMemo, useCallback } from 'preact/hooks' +import { useState, useEffect, useMemo, useCallback, FunctionComponent } from 'react' import { SNApplication, SNNote } from '@standardnotes/snjs' -import { WebApplication } from '@/UIModels/Application' import { KeyboardModifier } from '@/Services/IOService' -import { FunctionComponent } from 'preact' -import { ChangeEditorOption } from './ChangeEditorOption' +import ChangeEditorOption from './ChangeEditorOption' import { BYTES_IN_ONE_MEGABYTE } from '@/Constants' -import { ListedActionsOption } from './ListedActionsOption' -import { AddTagOption } from './AddTagOption' +import ListedActionsOption from './ListedActionsOption' +import AddTagOption from './AddTagOption' import { addToast, dismissToast, ToastType } from '@standardnotes/stylekit' - -export type NotesOptionsProps = { - application: WebApplication - appState: AppState - closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void -} +import { NotesOptionsProps } from './NotesOptionsProps' type DeletePermanentlyButtonProps = { closeOnBlur: NotesOptionsProps['closeOnBlur'] @@ -176,7 +169,7 @@ const NoteSizeWarning: FunctionComponent<{ ) : null } -export const NotesOptions = observer(({ application, appState, closeOnBlur }: NotesOptionsProps) => { +const NotesOptions = ({ application, appState, closeOnBlur }: NotesOptionsProps) => { const [altKeyDown, setAltKeyDown] = useState(false) const toggleOn = (condition: (note: SNNote) => boolean) => { @@ -440,4 +433,6 @@ export const NotesOptions = observer(({ application, appState, closeOnBlur }: No ) : null} ) -}) +} + +export default observer(NotesOptions) diff --git a/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx b/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx index 33c26269b..23b0a50bc 100644 --- a/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx +++ b/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx @@ -1,11 +1,11 @@ import { AppState } from '@/UIModels/AppState' -import { Icon } from '@/Components/Icon/Icon' +import Icon from '@/Components/Icon/Icon' import VisuallyHidden from '@reach/visually-hidden' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' -import { useRef, useState } from 'preact/hooks' +import { useRef, useState } from 'react' import { observer } from 'mobx-react-lite' -import { NotesOptions } from './NotesOptions' +import NotesOptions from './NotesOptions' import { WebApplication } from '@/UIModels/Application' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' @@ -15,7 +15,7 @@ type Props = { onClickPreprocessing?: () => Promise } -export const NotesOptionsPanel = observer(({ application, appState, onClickPreprocessing }: Props) => { +const NotesOptionsPanel = ({ application, appState, onClickPreprocessing }: Props) => { const [open, setOpen] = useState(false) const [position, setPosition] = useState({ top: 0, @@ -83,4 +83,6 @@ export const NotesOptionsPanel = observer(({ application, appState, onClickPrepr ) -}) +} + +export default observer(NotesOptionsPanel) diff --git a/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts b/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts new file mode 100644 index 000000000..2a969165c --- /dev/null +++ b/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts @@ -0,0 +1,8 @@ +import { WebApplication } from '@/UIModels/Application' +import { AppState } from '@/UIModels/AppState' + +export type NotesOptionsProps = { + application: WebApplication + appState: AppState + closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void +} diff --git a/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx b/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx index b5fa46311..c10ce1f16 100644 --- a/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx +++ b/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx @@ -1,4 +1,4 @@ -import { useCallback, useRef } from 'preact/hooks' +import { useCallback, useRef } from 'react' import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' @@ -9,13 +9,6 @@ type Props = { appState: AppState } -export const OtherSessionsSignOutContainer = observer((props: Props) => { - if (!props.appState.accountMenu.otherSessionsSignOut) { - return null - } - return -}) - const ConfirmOtherSessionsSignOut = observer(({ application, appState }: Props) => { const cancelRef = useRef(null) @@ -65,3 +58,14 @@ const ConfirmOtherSessionsSignOut = observer(({ application, appState }: Props) ) }) + +ConfirmOtherSessionsSignOut.displayName = 'ConfirmOtherSessionsSignOut' + +const OtherSessionsSignOutContainer = (props: Props) => { + if (!props.appState.accountMenu.otherSessionsSignOut) { + return null + } + return +} + +export default observer(OtherSessionsSignOutContainer) diff --git a/app/assets/javascripts/Components/PanelResizer/PanelResizer.tsx b/app/assets/javascripts/Components/PanelResizer/PanelResizer.tsx index d150bb6bd..6de7a49ab 100644 --- a/app/assets/javascripts/Components/PanelResizer/PanelResizer.tsx +++ b/app/assets/javascripts/Components/PanelResizer/PanelResizer.tsx @@ -1,4 +1,4 @@ -import { Component, createRef } from 'preact' +import { Component, createRef, MouseEventHandler } from 'react' import { debounce } from '@/Utils' export type ResizeFinishCallback = ( @@ -38,7 +38,7 @@ type State = { pressed: boolean } -export class PanelResizer extends Component { +class PanelResizer extends Component { private overlay?: HTMLDivElement private resizerElementRef = createRef() private debouncedResizeHandler: () => void @@ -76,6 +76,10 @@ export class PanelResizer extends Component { } } + override componentDidMount() { + this.resizerElementRef.current?.addEventListener('dblclick', this.onDblClick) + } + override componentDidUpdate(prevProps: Props) { if (this.props.width != prevProps.width) { this.setWidth(this.props.width) @@ -92,6 +96,7 @@ export class PanelResizer extends Component { } override componentWillUnmount() { + this.resizerElementRef.current?.removeEventListener('dblclick', this.onDblClick) document.removeEventListener('mouseup', this.onMouseUp) document.removeEventListener('mousemove', this.onMouseMove) window.removeEventListener('resize', this.debouncedResizeHandler) @@ -241,7 +246,7 @@ export class PanelResizer extends Component { this.finishSettingWidth() } - onMouseDown = (event: MouseEvent) => { + onMouseDown: MouseEventHandler = (event) => { this.addInvisibleOverlay() this.lastDownX = event.clientX this.startWidth = this.props.panel.scrollWidth @@ -299,16 +304,17 @@ export class PanelResizer extends Component { } } - render() { + override render() { return (
    ) } } + +export default PanelResizer diff --git a/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx b/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx index ad020ccaa..2cb0d1180 100644 --- a/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx +++ b/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx @@ -1,9 +1,10 @@ import { WebApplication } from '@/UIModels/Application' -import { createRef, JSX } from 'preact' +import { ChangeEventHandler, createRef } from 'react' import { PureComponent } from '@/Components/Abstract/PureComponent' interface Props { application: WebApplication + dismissModal: () => void } type State = { @@ -31,7 +32,7 @@ type FormData = { status?: string } -export class PasswordWizard extends PureComponent { +class PasswordWizard extends PureComponent { private currentPasswordInput = createRef() constructor(props: Props) { @@ -188,7 +189,7 @@ export class PasswordWizard extends PureComponent { if (this.state.lockContinue) { this.application.alertService.alert('Cannot close window until pending tasks are complete.').catch(console.error) } else { - this.dismissModal() + this.props.dismissModal() } } @@ -201,19 +202,19 @@ export class PasswordWizard extends PureComponent { }) } - handleCurrentPasswordInputChange = ({ currentTarget }: JSX.TargetedEvent) => { + handleCurrentPasswordInputChange: ChangeEventHandler = ({ currentTarget }) => { this.setFormDataState({ currentPassword: currentTarget.value, }).catch(console.error) } - handleNewPasswordInputChange = ({ currentTarget }: JSX.TargetedEvent) => { + handleNewPasswordInputChange: ChangeEventHandler = ({ currentTarget }) => { this.setFormDataState({ newPassword: currentTarget.value, }).catch(console.error) } - handleNewPasswordConfirmationInputChange = ({ currentTarget }: JSX.TargetedEvent) => { + handleNewPasswordConfirmationInputChange: ChangeEventHandler = ({ currentTarget }) => { this.setFormDataState({ newPasswordConfirmation: currentTarget.value, }).catch(console.error) @@ -310,3 +311,5 @@ export class PasswordWizard extends PureComponent { ) } } + +export default PasswordWizard diff --git a/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx b/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx index c9f146c57..0682c8960 100644 --- a/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx +++ b/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx @@ -1,45 +1,27 @@ import { WebApplication } from '@/UIModels/Application' import { SNComponent } from '@standardnotes/snjs' -import { Component } from 'preact' -import { findDOMNode, unmountComponentAtNode } from 'preact/compat' +import { Component } from 'react' interface Props { application: WebApplication callback: (approved: boolean) => void + dismiss: () => void component: SNComponent permissionsString: string } -export class PermissionsModal extends Component { - getElement(): Element | null { - return findDOMNode(this) - } - - dismiss = () => { - const elem = this.getElement() - if (!elem) { - return - } - - const parent = elem.parentElement - if (!parent) { - return - } - parent.remove() - unmountComponentAtNode(parent) - } - +class PermissionsModal extends Component { accept = () => { this.props.callback(true) - this.dismiss() + this.props.dismiss() } deny = () => { this.props.callback(false) - this.dismiss() + this.props.dismiss() } - render() { + override render() { return (
    @@ -88,3 +70,5 @@ export class PermissionsModal extends Component { ) } } + +export default PermissionsModal diff --git a/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx b/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx new file mode 100644 index 000000000..308074fff --- /dev/null +++ b/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx @@ -0,0 +1,56 @@ +import { WebApplication } from '@/UIModels/Application' +import { ApplicationEvent, PermissionDialog } from '@standardnotes/snjs' +import { FunctionComponent, useCallback, useEffect, useState } from 'react' +import PermissionsModal from './PermissionsModal' + +type Props = { + application: WebApplication +} + +const PermissionsModalWrapper: FunctionComponent = ({ application }) => { + const [dialog, setDialog] = useState() + + const presentPermissionsDialog = useCallback((permissionDialog: PermissionDialog) => { + setDialog(permissionDialog) + }, []) + + const dismissPermissionsDialog = useCallback(() => { + setDialog(undefined) + }, []) + + const onAppStart = useCallback(() => { + application.componentManager.presentPermissionsDialog = presentPermissionsDialog + + return () => { + ;(application.componentManager.presentPermissionsDialog as unknown) = undefined + } + }, [application, presentPermissionsDialog]) + + useEffect(() => { + if (application.isStarted()) { + onAppStart() + } + + const removeAppObserver = application.addEventObserver(async (eventName) => { + if (eventName === ApplicationEvent.Started) { + onAppStart() + } + }) + + return () => { + removeAppObserver() + } + }, [application, onAppStart]) + + return dialog ? ( + + ) : null +} + +export default PermissionsModalWrapper diff --git a/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx b/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx index 75c5d9929..6738391a5 100644 --- a/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx +++ b/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx @@ -1,10 +1,8 @@ import { AppState } from '@/UIModels/AppState' import VisuallyHidden from '@reach/visually-hidden' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { Icon } from '@/Components/Icon/Icon' -import { useCallback } from 'preact/hooks' -import { isStateDealloced } from '@/UIModels/AppState/AbstractState' +import { FunctionComponent, useCallback } from 'react' +import Icon from '@/Components/Icon/Icon' type Props = { appState: AppState @@ -12,34 +10,27 @@ type Props = { onClickPreprocessing?: () => Promise } -export const PinNoteButton: FunctionComponent = observer( - ({ appState, className = '', onClickPreprocessing }: Props) => { - if (isStateDealloced(appState)) { - return null +const PinNoteButton: FunctionComponent = ({ appState, className = '', onClickPreprocessing }: Props) => { + const notes = appState.notes.selectedNotes + const pinned = notes.some((note) => note.pinned) + + const togglePinned = useCallback(async () => { + if (onClickPreprocessing) { + await onClickPreprocessing() } + if (!pinned) { + appState.notes.setPinSelectedNotes(true) + } else { + appState.notes.setPinSelectedNotes(false) + } + }, [appState, onClickPreprocessing, pinned]) - const notes = appState.notes.selectedNotes - const pinned = notes.some((note) => note.pinned) + return ( + + ) +} - const togglePinned = useCallback(async () => { - if (onClickPreprocessing) { - await onClickPreprocessing() - } - if (!pinned) { - appState.notes.setPinSelectedNotes(true) - } else { - appState.notes.setPinSelectedNotes(false) - } - }, [appState, onClickPreprocessing, pinned]) - - return ( - - ) - }, -) +export default observer(PinNoteButton) diff --git a/app/assets/javascripts/Components/Preferences/PaneSelector.tsx b/app/assets/javascripts/Components/Preferences/PaneSelector.tsx new file mode 100644 index 000000000..3452816da --- /dev/null +++ b/app/assets/javascripts/Components/Preferences/PaneSelector.tsx @@ -0,0 +1,60 @@ +import { FunctionComponent } from 'react' +import { observer } from 'mobx-react-lite' +import { PreferencesMenu } from './PreferencesMenu' +import Backups from '@/Components/Preferences/Panes/Backups/Backups' +import Appearance from './Panes/Appearance' +import General from './Panes/General/General' +import AccountPreferences from './Panes/Account/AccountPreferences' +import Security from './Panes/Security/Security' +import Listed from './Panes/Listed/Listed' +import HelpAndFeedback from './Panes/HelpFeedback' +import { PreferencesProps } from './PreferencesProps' + +const PaneSelector: FunctionComponent = ({ + menu, + appState, + application, + mfaProvider, + userProvider, +}) => { + switch (menu.selectedPaneId) { + case 'general': + return ( + + ) + case 'account': + return + case 'appearance': + return + case 'security': + return ( + + ) + case 'backups': + return + case 'listed': + return + case 'shortcuts': + return null + case 'accessibility': + return null + case 'get-free-month': + return null + case 'help-feedback': + return + default: + return ( + + ) + } +} + +export default observer(PaneSelector) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/AccountPreferences.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/AccountPreferences.tsx index bed91f9a0..6e832c9c9 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/AccountPreferences.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/AccountPreferences.tsx @@ -1,20 +1,20 @@ -import { PreferencesPane } from '@/Components/Preferences/PreferencesComponents' import { observer } from 'mobx-react-lite' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' -import { Authentication } from './Authentication' -import { Credentials } from './Credentials' -import { Sync } from './Sync' -import { Subscription } from './Subscription/Subscription' -import { SignOutWrapper } from './SignOutView' -import { FilesSection } from './Files' +import Authentication from './Authentication' +import Credentials from './Credentials' +import Sync from './Sync' +import Subscription from './Subscription/Subscription' +import SignOutWrapper from './SignOutView' +import FilesSection from './Files' +import PreferencesPane from '../../PreferencesComponents/PreferencesPane' type Props = { application: WebApplication appState: AppState } -export const AccountPreferences = observer(({ application, appState }: Props) => ( +const AccountPreferences = ({ application, appState }: Props) => ( {!application.hasAccount() ? ( @@ -28,4 +28,6 @@ export const AccountPreferences = observer(({ application, appState }: Props) => {application.hasAccount() && appState.features.hasFiles && } -)) +) + +export default observer(AccountPreferences) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Advanced.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Advanced.tsx index ce00ff781..ed16a7af5 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Advanced.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Advanced.tsx @@ -1,20 +1,21 @@ -import { FunctionalComponent } from 'preact' -import { PreferencesGroup, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents' -import { OfflineSubscription } from '@/Components/Preferences/Panes/Account/OfflineSubscription' +import { FunctionComponent } from 'react' +import OfflineSubscription from '@/Components/Preferences/Panes/Account/OfflineSubscription' import { WebApplication } from '@/UIModels/Application' import { observer } from 'mobx-react-lite' import { AppState } from '@/UIModels/AppState' -import { Extensions } from '@/Components/Preferences/Panes/Extensions/Extensions' +import Extensions from '@/Components/Preferences/Panes/Extensions/Extensions' import { ExtensionsLatestVersions } from '@/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions' -import { AccordionItem } from '@/Components/Shared/AccordionItem' +import AccordionItem from '@/Components/Shared/AccordionItem' +import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' +import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' -interface IProps { +type Props = { application: WebApplication appState: AppState extensionsLatestVersions: ExtensionsLatestVersions } -export const Advanced: FunctionalComponent = observer(({ application, appState, extensionsLatestVersions }) => { +const Advanced: FunctionComponent = ({ application, appState, extensionsLatestVersions }) => { return ( @@ -33,4 +34,6 @@ export const Advanced: FunctionalComponent = observer(({ application, ap ) -}) +} + +export default observer(Advanced) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Authentication.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Authentication.tsx index 4d40cb020..f12cb7628 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Authentication.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Authentication.tsx @@ -1,16 +1,20 @@ -import { Button } from '@/Components/Button/Button' -import { PreferencesGroup, PreferencesSegment, Text, Title } from '@/Components/Preferences/PreferencesComponents' +import Button from '@/Components/Button/Button' +import { Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' import { AccountIllustration } from '@standardnotes/icons' import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane' +import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' +import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' -export const Authentication: FunctionComponent<{ +type Props = { application: WebApplication appState: AppState -}> = observer(({ appState }) => { +} + +const Authentication: FunctionComponent = ({ appState }) => { const clickSignIn = () => { appState.preferences.closePreferences() appState.accountMenu.setCurrentPane(AccountMenuPane.SignIn) @@ -43,4 +47,6 @@ export const Authentication: FunctionComponent<{ ) -}) +} + +export default observer(Authentication) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx index 4ca8e618a..217e6df42 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx @@ -1,16 +1,13 @@ -import { useState } from '@node_modules/preact/hooks' -import { - ModalDialog, - ModalDialogButtons, - ModalDialogDescription, - ModalDialogLabel, -} from '@/Components/Shared/ModalDialog' -import { Button } from '@/Components/Button/Button' -import { FunctionalComponent } from 'preact' +import ModalDialog from '@/Components/Shared/ModalDialog' +import ModalDialogButtons from '@/Components/Shared/ModalDialogButtons' +import ModalDialogDescription from '@/Components/Shared/ModalDialogDescription' +import ModalDialogLabel from '@/Components/Shared/ModalDialogLabel' +import Button from '@/Components/Button/Button' +import { FunctionComponent, useState } from 'react' import { WebApplication } from '@/UIModels/Application' import { useBeforeUnload } from '@/Hooks/useBeforeUnload' -import { ChangeEmailForm } from './ChangeEmailForm' -import { ChangeEmailSuccess } from './ChangeEmailSuccess' +import ChangeEmailForm from './ChangeEmailForm' +import ChangeEmailSuccess from './ChangeEmailSuccess' import { isEmailValid } from '@/Utils' enum SubmitButtonTitles { @@ -29,7 +26,7 @@ type Props = { application: WebApplication } -export const ChangeEmail: FunctionalComponent = ({ onCloseDialog, application }) => { +const ChangeEmail: FunctionComponent = ({ onCloseDialog, application }) => { const [currentPassword, setCurrentPassword] = useState('') const [newEmail, setNewEmail] = useState('') const [isContinuing, setIsContinuing] = useState(false) @@ -158,3 +155,5 @@ export const ChangeEmail: FunctionalComponent = ({ onCloseDialog, applica
    ) } + +export default ChangeEmail diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx index 6f94004b6..6526fdbb1 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx @@ -1,16 +1,15 @@ -import { StateUpdater } from 'preact/hooks' -import { FunctionalComponent } from 'preact' +import { Dispatch, SetStateAction, FunctionComponent } from 'react' type Props = { - setNewEmail: StateUpdater - setCurrentPassword: StateUpdater + setNewEmail: Dispatch> + setCurrentPassword: Dispatch> } const labelClassName = 'block mb-1' const inputClassName = 'sk-input contrast' -export const ChangeEmailForm: FunctionalComponent = ({ setNewEmail, setCurrentPassword }) => { +const ChangeEmailForm: FunctionComponent = ({ setNewEmail, setCurrentPassword }) => { return (
    @@ -42,3 +41,5 @@ export const ChangeEmailForm: FunctionalComponent = ({ setNewEmail, setCu
    ) } + +export default ChangeEmailForm diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailSuccess.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailSuccess.tsx index 1e735eae0..f6ca3f81b 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailSuccess.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailSuccess.tsx @@ -1,6 +1,6 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' -export const ChangeEmailSuccess: FunctionalComponent = () => { +const ChangeEmailSuccess: FunctionComponent = () => { return (
    Your email has been successfully changed.
    @@ -11,3 +11,5 @@ export const ChangeEmailSuccess: FunctionalComponent = () => {
    ) } + +export default ChangeEmailSuccess diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx new file mode 100644 index 000000000..da3764d50 --- /dev/null +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx @@ -0,0 +1,30 @@ +import Button from '@/Components/Button/Button' +import { AppState } from '@/UIModels/AppState' +import { observer } from 'mobx-react-lite' +import { FunctionComponent } from 'react' +import { Title, Text } from '../../PreferencesComponents/Content' +import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' +import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' + +const ClearSessionDataView: FunctionComponent<{ + appState: AppState +}> = ({ appState }) => { + return ( + + + Clear workspace + Remove all data related to the current workspace from the application. +
    +
    + This workspace Remove all data related to the current workspace from the application. @@ -54,33 +55,13 @@ const SignOutView: FunctionComponent<{ ) }) -const ClearSessionDataView: FunctionComponent<{ - appState: AppState -}> = observer(({ appState }) => { - return ( - - - Clear workspace - Remove all data related to the current workspace from the application. -
    -
    - +
    Automatic Light Theme Theme to be used for system light mode: @@ -135,7 +131,7 @@ export const Appearance: FunctionComponent = observer(({ application }) = />
    - +
    Automatic Dark Theme Theme to be used for system dark mode: @@ -155,4 +151,6 @@ export const Appearance: FunctionComponent = observer(({ application }) = ) -}) +} + +export default observer(Appearance) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx index 13e0d3a7c..0671fa0b5 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx @@ -1,24 +1,27 @@ import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' -import { FunctionComponent } from 'preact' -import { PreferencesPane } from '@/Components/Preferences/PreferencesComponents' -import { CloudLink } from './CloudBackups/CloudBackups' -import { DataBackups } from './DataBackups' -import { EmailBackups } from './EmailBackups' -import { FileBackups } from './Files/FileBackups' +import { FunctionComponent } from 'react' +import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane' +import CloudLink from './CloudBackups/CloudBackups' +import DataBackups from './DataBackups' +import EmailBackups from './EmailBackups' +import FileBackupsCrossPlatform from './Files/FileBackupsCrossPlatform' +import { observer } from 'mobx-react-lite' -interface Props { +type Props = { appState: AppState application: WebApplication } -export const Backups: FunctionComponent = ({ application, appState }) => { +const Backups: FunctionComponent = ({ application, appState }) => { return ( - + ) } + +export default observer(Backups) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx index aa66e0058..b7d99e38d 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx @@ -1,4 +1,12 @@ -import { useCallback, useEffect, useState } from 'preact/hooks' +import { + useCallback, + useEffect, + useState, + FunctionComponent, + KeyboardEventHandler, + ChangeEventHandler, + MouseEventHandler, +} from 'react' import { ButtonType, SettingName, @@ -8,11 +16,10 @@ import { OneDriveBackupFrequency, } from '@standardnotes/snjs' import { WebApplication } from '@/UIModels/Application' -import { Button } from '@/Components/Button/Button' +import Button from '@/Components/Button/Button' import { isDev, openInNewTab } from '@/Utils' -import { Subtitle } from '@/Components/Preferences/PreferencesComponents' +import { Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' import { KeyboardKey } from '@/Services/IOService' -import { FunctionComponent } from 'preact' type Props = { application: WebApplication @@ -20,17 +27,13 @@ type Props = { isEntitledToCloudBackups: boolean } -export const CloudBackupProvider: FunctionComponent = ({ - application, - providerName, - isEntitledToCloudBackups, -}) => { +const CloudBackupProvider: FunctionComponent = ({ application, providerName, isEntitledToCloudBackups }) => { const [authBegan, setAuthBegan] = useState(false) const [successfullyInstalled, setSuccessfullyInstalled] = useState(false) const [backupFrequency, setBackupFrequency] = useState(undefined) const [confirmation, setConfirmation] = useState('') - const disable = async (event: Event) => { + const disable: MouseEventHandler = async (event) => { event.stopPropagation() try { @@ -52,7 +55,7 @@ export const CloudBackupProvider: FunctionComponent = ({ } } - const installIntegration = (event: Event) => { + const installIntegration: MouseEventHandler = (event) => { if (!isEntitledToCloudBackups) { return } @@ -117,7 +120,7 @@ export const CloudBackupProvider: FunctionComponent = ({ return urlSearchParams.get(integrationTokenKeyInUrl) } - const handleKeyPress = async (event: KeyboardEvent) => { + const handleKeyPress: KeyboardEventHandler = async (event) => { if (event.key === KeyboardKey.Enter) { try { const decryptedCode = atob(confirmation) @@ -145,8 +148,8 @@ export const CloudBackupProvider: FunctionComponent = ({ } } - const handleChange = (event: Event) => { - setConfirmation((event.target as HTMLInputElement).value) + const handleChange: ChangeEventHandler = (event) => { + setConfirmation(event.target.value) } const getIntegrationStatus = useCallback(async () => { @@ -219,3 +222,5 @@ export const CloudBackupProvider: FunctionComponent = ({
    ) } + +export default CloudBackupProvider diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx index baa8a0b2a..f7794647e 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx @@ -1,14 +1,8 @@ -import { CloudBackupProvider } from './CloudBackupProvider' -import { useCallback, useEffect, useState } from 'preact/hooks' +import CloudBackupProvider from './CloudBackupProvider' +import { useCallback, useEffect, useState, FunctionComponent, Fragment } from 'react' import { WebApplication } from '@/UIModels/Application' -import { - PreferencesGroup, - PreferencesSegment, - Subtitle, - Text, - Title, -} from '@/Components/Preferences/PreferencesComponents' -import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator' +import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' +import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import { FeatureStatus, FeatureIdentifier, @@ -16,11 +10,12 @@ import { MuteFailedCloudBackupsEmailsOption, SettingName, } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { Switch } from '@/Components/Switch/Switch' +import Switch from '@/Components/Switch/Switch' import { convertStringifiedBooleanToBoolean } from '@/Utils' import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/Strings' +import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup' +import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment' const providerData = [{ name: CloudProvider.Dropbox }, { name: CloudProvider.Google }, { name: CloudProvider.OneDrive }] @@ -28,7 +23,7 @@ type Props = { application: WebApplication } -export const CloudLink: FunctionComponent = ({ application }) => { +const CloudLink: FunctionComponent = ({ application }) => { const [isEntitledToCloudBackups, setIsEntitledToCloudBackups] = useState(false) const [isFailedCloudBackupEmailMuted, setIsFailedCloudBackupEmailMuted] = useState(true) const [isLoading, setIsLoading] = useState(false) @@ -121,14 +116,14 @@ export const CloudLink: FunctionComponent = ({ application }) => {
    {providerData.map(({ name }) => ( - <> + - + ))}
    @@ -155,3 +150,5 @@ export const CloudLink: FunctionComponent = ({ application }) => { ) } + +export default CloudLink diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx index 1763af116..1067dafe9 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx @@ -11,27 +11,22 @@ import { STRING_ENC_NOT_ENABLED, } from '@/Strings' import { BackupFile } from '@standardnotes/snjs' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' +import { ChangeEventHandler, MouseEventHandler, useCallback, useEffect, useRef, useState } from 'react' import { WebApplication } from '@/UIModels/Application' -import { JSXInternal } from 'preact/src/jsx' -import TargetedEvent = JSXInternal.TargetedEvent import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { - PreferencesGroup, - PreferencesSegment, - Title, - Text, - Subtitle, -} from '@/Components/Preferences/PreferencesComponents' -import { Button } from '@/Components/Button/Button' +import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' +import Button from '@/Components/Button/Button' +import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' +import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' +import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' type Props = { application: WebApplication appState: AppState } -export const DataBackups = observer(({ application, appState }: Props) => { +const DataBackups = ({ application, appState }: Props) => { const fileInputRef = useRef(null) const [isImportDataLoading, setIsImportDataLoading] = useState(false) const { @@ -109,8 +104,8 @@ export const DataBackups = observer(({ application, appState }: Props) => { }) } - const importFileSelected = async (event: TargetedEvent) => { - const { files } = event.target as HTMLInputElement + const importFileSelected: ChangeEventHandler = async (event) => { + const { files } = event.target if (!files) { return @@ -136,7 +131,7 @@ export const DataBackups = observer(({ application, appState }: Props) => { } // Whenever "Import Backup" is either clicked or key-pressed, proceed the import - const handleImportFile = (event: TargetedEvent | KeyboardEvent) => { + const handleImportFile: MouseEventHandler = (event) => { if (event instanceof KeyboardEvent) { const { code } = event @@ -158,7 +153,7 @@ export const DataBackups = observer(({ application, appState }: Props) => { Data Backups - {!isDesktopApplication() && ( + {isDesktopApplication() && ( Backups are automatically created on desktop and can be managed via the "Backups" top-level menu. @@ -183,10 +178,11 @@ export const DataBackups = observer(({ application, appState }: Props) => { ) } + +export default ThemesMenuButton diff --git a/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListContainer.tsx b/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListContainer.tsx index 871d7765c..57e8bd5d6 100644 --- a/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListContainer.tsx +++ b/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListContainer.tsx @@ -1,11 +1,10 @@ import { WebApplication } from '@/UIModels/Application' import { Action, ActionVerb, HistoryEntry, NoteHistoryEntry, RevisionListEntry, SNNote } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { StateUpdater, useCallback, useState, useEffect } from 'preact/hooks' -import { LegacyHistoryList } from './LegacyHistoryList' -import { RemoteHistoryList } from './RemoteHistoryList' -import { SessionHistoryList } from './SessionHistoryList' +import { FunctionComponent, useCallback, useState, useEffect, SetStateAction, Dispatch } from 'react' +import LegacyHistoryList from './LegacyHistoryList' +import RemoteHistoryList from './RemoteHistoryList' +import SessionHistoryList from './SessionHistoryList' import { LegacyHistoryEntry, RemoteRevisionListGroup, sortRevisionListIntoGroups } from './utils' export enum RevisionListTabType { @@ -19,169 +18,169 @@ type Props = { isFetchingRemoteHistory: boolean note: SNNote remoteHistory: RemoteRevisionListGroup[] | undefined - setIsFetchingSelectedRevision: StateUpdater - setSelectedRemoteEntry: StateUpdater - setSelectedRevision: StateUpdater - setShowContentLockedScreen: StateUpdater + setIsFetchingSelectedRevision: Dispatch> + setSelectedRemoteEntry: Dispatch> + setSelectedRevision: Dispatch> + setShowContentLockedScreen: Dispatch> } -export const HistoryListContainer: FunctionComponent = observer( - ({ - application, - isFetchingRemoteHistory, - note, - remoteHistory, - setIsFetchingSelectedRevision, - setSelectedRemoteEntry, - setSelectedRevision, - setShowContentLockedScreen, - }) => { - const sessionHistory = sortRevisionListIntoGroups( - application.historyManager.sessionHistoryForItem(note) as NoteHistoryEntry[], - ) - const [legacyHistory, setLegacyHistory] = useState() +const HistoryListContainer: FunctionComponent = ({ + application, + isFetchingRemoteHistory, + note, + remoteHistory, + setIsFetchingSelectedRevision, + setSelectedRemoteEntry, + setSelectedRevision, + setShowContentLockedScreen, +}) => { + const sessionHistory = sortRevisionListIntoGroups( + application.historyManager.sessionHistoryForItem(note) as NoteHistoryEntry[], + ) + const [legacyHistory, setLegacyHistory] = useState() - const [selectedTab, setSelectedTab] = useState(RevisionListTabType.Remote) + const [selectedTab, setSelectedTab] = useState(RevisionListTabType.Remote) - useEffect(() => { - const fetchLegacyHistory = async () => { - const actionExtensions = application.actionsManager.getExtensions() - actionExtensions.forEach(async (ext) => { - const actionExtension = await application.actionsManager.loadExtensionInContextOfItem(ext, note) + useEffect(() => { + const fetchLegacyHistory = async () => { + const actionExtensions = application.actionsManager.getExtensions() + actionExtensions.forEach(async (ext) => { + const actionExtension = await application.actionsManager.loadExtensionInContextOfItem(ext, note) - if (!actionExtension) { - return - } + if (!actionExtension) { + return + } - const isLegacyNoteHistoryExt = actionExtension?.actions.some((action) => action.verb === ActionVerb.Nested) + const isLegacyNoteHistoryExt = actionExtension?.actions.some((action) => action.verb === ActionVerb.Nested) - if (!isLegacyNoteHistoryExt) { - return - } + if (!isLegacyNoteHistoryExt) { + return + } - const legacyHistoryEntries = actionExtension.actions.filter((action) => action.subactions?.[0]) + const legacyHistoryEntries = actionExtension.actions.filter((action) => action.subactions?.[0]) - setLegacyHistory(legacyHistoryEntries) - }) - } - - fetchLegacyHistory().catch(console.error) - }, [application, note]) - - const TabButton: FunctionComponent<{ - type: RevisionListTabType - }> = ({ type }) => { - const isSelected = selectedTab === type - - return ( - - ) + setLegacyHistory(legacyHistoryEntries) + }) } - const fetchAndSetLegacyRevision = useCallback( - async (revisionListEntry: Action) => { - setSelectedRemoteEntry(undefined) + fetchLegacyHistory().catch(console.error) + }, [application, note]) + + const TabButton: FunctionComponent<{ + type: RevisionListTabType + }> = ({ type }) => { + const isSelected = selectedTab === type + + return ( + + ) + } + + const fetchAndSetLegacyRevision = useCallback( + async (revisionListEntry: Action) => { + setSelectedRemoteEntry(undefined) + setSelectedRevision(undefined) + setIsFetchingSelectedRevision(true) + + try { + if (!revisionListEntry.subactions?.[0]) { + throw new Error('Could not find revision action url') + } + + const response = await application.actionsManager.runAction(revisionListEntry.subactions[0], note) + + if (!response) { + throw new Error('Could not fetch revision') + } + + setSelectedRevision(response.item as unknown as HistoryEntry) + } catch (error) { + console.error(error) setSelectedRevision(undefined) + } finally { + setIsFetchingSelectedRevision(false) + } + }, + [application.actionsManager, note, setIsFetchingSelectedRevision, setSelectedRemoteEntry, setSelectedRevision], + ) + + const fetchAndSetRemoteRevision = useCallback( + async (revisionListEntry: RevisionListEntry) => { + setShowContentLockedScreen(false) + + if (application.features.hasMinimumRole(revisionListEntry.required_role)) { setIsFetchingSelectedRevision(true) + setSelectedRevision(undefined) + setSelectedRemoteEntry(undefined) try { - if (!revisionListEntry.subactions?.[0]) { - throw new Error('Could not find revision action url') - } - - const response = await application.actionsManager.runAction(revisionListEntry.subactions[0], note) - - if (!response) { - throw new Error('Could not fetch revision') - } - - setSelectedRevision(response.item as unknown as HistoryEntry) - } catch (error) { - console.error(error) - setSelectedRevision(undefined) + const remoteRevision = await application.historyManager.fetchRemoteRevision(note, revisionListEntry) + setSelectedRevision(remoteRevision) + setSelectedRemoteEntry(revisionListEntry) + } catch (err) { + console.error(err) } finally { setIsFetchingSelectedRevision(false) } - }, - [application.actionsManager, note, setIsFetchingSelectedRevision, setSelectedRemoteEntry, setSelectedRevision], - ) + } else { + setShowContentLockedScreen(true) + setSelectedRevision(undefined) + } + }, + [ + application, + note, + setIsFetchingSelectedRevision, + setSelectedRemoteEntry, + setSelectedRevision, + setShowContentLockedScreen, + ], + ) - const fetchAndSetRemoteRevision = useCallback( - async (revisionListEntry: RevisionListEntry) => { - setShowContentLockedScreen(false) - - if (application.features.hasMinimumRole(revisionListEntry.required_role)) { - setIsFetchingSelectedRevision(true) - setSelectedRevision(undefined) - setSelectedRemoteEntry(undefined) - - try { - const remoteRevision = await application.historyManager.fetchRemoteRevision(note, revisionListEntry) - setSelectedRevision(remoteRevision) - setSelectedRemoteEntry(revisionListEntry) - } catch (err) { - console.error(err) - } finally { - setIsFetchingSelectedRevision(false) - } - } else { - setShowContentLockedScreen(true) - setSelectedRevision(undefined) - } - }, - [ - application, - note, - setIsFetchingSelectedRevision, - setSelectedRemoteEntry, - setSelectedRevision, - setShowContentLockedScreen, - ], - ) - - return ( -
    -
    - - - {legacyHistory && legacyHistory.length > 0 && } -
    -
    - {selectedTab === RevisionListTabType.Session && ( - - )} - {selectedTab === RevisionListTabType.Remote && ( - - )} - {selectedTab === RevisionListTabType.Legacy && ( - - )} -
    + return ( +
    +
    + + + {legacyHistory && legacyHistory.length > 0 && }
    - ) - }, -) +
    + {selectedTab === RevisionListTabType.Session && ( + + )} + {selectedTab === RevisionListTabType.Remote && ( + + )} + {selectedTab === RevisionListTabType.Legacy && ( + + )} +
    +
    + ) +} + +export default observer(HistoryListContainer) diff --git a/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListItem.tsx b/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListItem.tsx index 4ee116cbe..6b9821366 100644 --- a/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListItem.tsx +++ b/app/assets/javascripts/Components/RevisionHistoryModal/HistoryListItem.tsx @@ -1,12 +1,12 @@ import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' type HistoryListItemProps = { isSelected: boolean onClick: () => void } -export const HistoryListItem: FunctionComponent = ({ children, isSelected, onClick }) => { +const HistoryListItem: FunctionComponent = ({ children, isSelected, onClick }) => { return ( ) } + +export default HistoryListItem diff --git a/app/assets/javascripts/Components/RevisionHistoryModal/LegacyHistoryList.tsx b/app/assets/javascripts/Components/RevisionHistoryModal/LegacyHistoryList.tsx index 2b9c468e5..57d09ab8c 100644 --- a/app/assets/javascripts/Components/RevisionHistoryModal/LegacyHistoryList.tsx +++ b/app/assets/javascripts/Components/RevisionHistoryModal/LegacyHistoryList.tsx @@ -1,18 +1,17 @@ import { Action, HistoryEntry, RevisionListEntry } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { StateUpdater, useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' +import { Dispatch, FunctionComponent, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation' -import { HistoryListItem } from './HistoryListItem' +import HistoryListItem from './HistoryListItem' import { LegacyHistoryEntry } from './utils' type Props = { legacyHistory: Action[] | undefined - setSelectedRevision: StateUpdater - setSelectedRemoteEntry: StateUpdater + setSelectedRevision: Dispatch> + setSelectedRemoteEntry: Dispatch> fetchAndSetLegacyRevision: (revisionListEntry: Action) => Promise } -export const LegacyHistoryList: FunctionComponent = ({ +const LegacyHistoryList: FunctionComponent = ({ legacyHistory, setSelectedRevision, setSelectedRemoteEntry, @@ -72,3 +71,5 @@ export const LegacyHistoryList: FunctionComponent = ({
    ) } + +export default LegacyHistoryList diff --git a/app/assets/javascripts/Components/RevisionHistoryModal/RemoteHistoryList.tsx b/app/assets/javascripts/Components/RevisionHistoryModal/RemoteHistoryList.tsx index c940e4793..2c392e1ea 100644 --- a/app/assets/javascripts/Components/RevisionHistoryModal/RemoteHistoryList.tsx +++ b/app/assets/javascripts/Components/RevisionHistoryModal/RemoteHistoryList.tsx @@ -1,11 +1,10 @@ import { WebApplication } from '@/UIModels/Application' import { RevisionListEntry } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { Fragment, FunctionComponent } from 'preact' -import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' +import { Fragment, FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import Icon from '@/Components/Icon/Icon' import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation' -import { HistoryListItem } from './HistoryListItem' +import HistoryListItem from './HistoryListItem' import { previewHistoryEntryTitle, RemoteRevisionListGroup } from './utils' type RemoteHistoryListProps = { @@ -15,76 +14,78 @@ type RemoteHistoryListProps = { fetchAndSetRemoteRevision: (revisionListEntry: RevisionListEntry) => Promise } -export const RemoteHistoryList: FunctionComponent = observer( - ({ application, remoteHistory, isFetchingRemoteHistory, fetchAndSetRemoteRevision }) => { - const remoteHistoryListRef = useRef(null) +const RemoteHistoryList: FunctionComponent = ({ + application, + remoteHistory, + isFetchingRemoteHistory, + fetchAndSetRemoteRevision, +}) => { + const remoteHistoryListRef = useRef(null) - useListKeyboardNavigation(remoteHistoryListRef) + useListKeyboardNavigation(remoteHistoryListRef) - const remoteHistoryLength = useMemo( - () => remoteHistory?.map((group) => group.entries).flat().length, - [remoteHistory], - ) + const remoteHistoryLength = useMemo(() => remoteHistory?.map((group) => group.entries).flat().length, [remoteHistory]) - const [selectedEntryUuid, setSelectedEntryUuid] = useState('') + const [selectedEntryUuid, setSelectedEntryUuid] = useState('') - const firstEntry = useMemo(() => { - return remoteHistory?.find((group) => group.entries?.length)?.entries?.[0] - }, [remoteHistory]) + const firstEntry = useMemo(() => { + return remoteHistory?.find((group) => group.entries?.length)?.entries?.[0] + }, [remoteHistory]) - const selectFirstEntry = useCallback(() => { - if (firstEntry) { - setSelectedEntryUuid(firstEntry.uuid) - fetchAndSetRemoteRevision(firstEntry).catch(console.error) - } - }, [fetchAndSetRemoteRevision, firstEntry]) + const selectFirstEntry = useCallback(() => { + if (firstEntry) { + setSelectedEntryUuid(firstEntry.uuid) + fetchAndSetRemoteRevision(firstEntry).catch(console.error) + } + }, [fetchAndSetRemoteRevision, firstEntry]) - useEffect(() => { - if (firstEntry && !selectedEntryUuid.length) { - selectFirstEntry() - } - }, [fetchAndSetRemoteRevision, firstEntry, remoteHistory, selectFirstEntry, selectedEntryUuid.length]) + useEffect(() => { + if (firstEntry && !selectedEntryUuid.length) { + selectFirstEntry() + } + }, [fetchAndSetRemoteRevision, firstEntry, remoteHistory, selectFirstEntry, selectedEntryUuid.length]) - return ( -
    - {isFetchingRemoteHistory &&
    } - {remoteHistory?.map((group) => { - if (group.entries && group.entries.length) { - return ( - -
    - {group.title} -
    - {group.entries.map((entry) => ( - { - setSelectedEntryUuid(entry.uuid) - fetchAndSetRemoteRevision(entry).catch(console.error) - }} - > -
    -
    {previewHistoryEntryTitle(entry)}
    - {!application.features.hasMinimumRole(entry.required_role) && } -
    -
    - ))} -
    - ) - } else { - return null - } - })} - {!remoteHistoryLength && !isFetchingRemoteHistory && ( -
    No remote history found
    - )} -
    - ) - }, -) + return ( +
    + {isFetchingRemoteHistory &&
    } + {remoteHistory?.map((group) => { + if (group.entries && group.entries.length) { + return ( + +
    + {group.title} +
    + {group.entries.map((entry) => ( + { + setSelectedEntryUuid(entry.uuid) + fetchAndSetRemoteRevision(entry).catch(console.error) + }} + > +
    +
    {previewHistoryEntryTitle(entry)}
    + {!application.features.hasMinimumRole(entry.required_role) && } +
    +
    + ))} +
    + ) + } else { + return null + } + })} + {!remoteHistoryLength && !isFetchingRemoteHistory && ( +
    No remote history found
    + )} +
    + ) +} + +export default observer(RemoteHistoryList) diff --git a/app/assets/javascripts/Components/RevisionHistoryModal/RevisionContentLocked.tsx b/app/assets/javascripts/Components/RevisionHistoryModal/RevisionContentLocked.tsx index f22885185..52edcbf95 100644 --- a/app/assets/javascripts/Components/RevisionHistoryModal/RevisionContentLocked.tsx +++ b/app/assets/javascripts/Components/RevisionHistoryModal/RevisionContentLocked.tsx @@ -1,8 +1,8 @@ import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' import { HistoryLockedIllustration } from '@standardnotes/icons' -import { Button } from '@/Components/Button/Button' +import Button from '@/Components/Button/Button' const getPlanHistoryDuration = (planName: string | undefined) => { switch (planName) { @@ -19,16 +19,18 @@ const getPremiumContentCopy = (planName: string | undefined) => { return `Version history is limited to ${getPlanHistoryDuration(planName)} in the ${planName} plan` } -export const RevisionContentLocked: FunctionComponent<{ +type Props = { appState: AppState -}> = observer(({ appState }) => { +} + +const RevisionContentLocked: FunctionComponent = ({ appState }) => { const { userSubscriptionName, isUserSubscriptionExpired, isUserSubscriptionCanceled } = appState.subscription return (
    -
    Can't access this version
    +
    Can't access this version
    {getPremiumContentCopy( !isUserSubscriptionCanceled && !isUserSubscriptionExpired && userSubscriptionName @@ -49,4 +51,6 @@ export const RevisionContentLocked: FunctionComponent<{
    ) -}) +} + +export default observer(RevisionContentLocked) diff --git a/app/assets/javascripts/Components/RevisionHistoryModal/RevisionHistoryModalWrapper.tsx b/app/assets/javascripts/Components/RevisionHistoryModal/RevisionHistoryModalWrapper.tsx index 5b483b67c..24d54382a 100644 --- a/app/assets/javascripts/Components/RevisionHistoryModal/RevisionHistoryModalWrapper.tsx +++ b/app/assets/javascripts/Components/RevisionHistoryModal/RevisionHistoryModalWrapper.tsx @@ -13,12 +13,11 @@ import { SNNote, } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' -import { Button } from '@/Components/Button/Button' -import { HistoryListContainer } from './HistoryListContainer' -import { RevisionContentLocked } from './RevisionContentLocked' -import { SelectedRevisionContent } from './SelectedRevisionContent' +import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import Button from '@/Components/Button/Button' +import HistoryListContainer from './HistoryListContainer' +import RevisionContentLocked from './RevisionContentLocked' +import SelectedRevisionContent from './SelectedRevisionContent' import { LegacyHistoryEntry, RemoteRevisionListGroup, sortRevisionListIntoGroups } from './utils' type RevisionHistoryModalProps = { @@ -209,6 +208,7 @@ export const RevisionHistoryModal: FunctionComponent aria-label="Note revision history" > />
    {selectedRevision && ( -
    +
    {selectedRemoteEntry && (
    ) -}) +} + +export default observer(SearchOptions) diff --git a/app/assets/javascripts/Components/SessionsModal/SessionsModal.tsx b/app/assets/javascripts/Components/SessionsModal/SessionsModal.tsx index df6ff0f17..833cf791d 100644 --- a/app/assets/javascripts/Components/SessionsModal/SessionsModal.tsx +++ b/app/assets/javascripts/Components/SessionsModal/SessionsModal.tsx @@ -1,7 +1,6 @@ import { AppState } from '@/UIModels/AppState' import { SNApplication, SessionStrings, UuidString, isNullOrUndefined, RemoteSession } from '@standardnotes/snjs' -import { FunctionComponent } from 'preact' -import { useState, useEffect, useRef, useMemo } from 'preact/hooks' +import { FunctionComponent, useState, useEffect, useRef, useMemo } from 'react' import { Dialog } from '@reach/dialog' import { Alert } from '@reach/alert' import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog' @@ -103,23 +102,23 @@ const SessionsModalContent: FunctionComponent<{ <>
    -
    -
    -
    -
    Active Sessions
    +
    +
    +
    +
    Active Sessions
    - -
    -
    +
    {refreshing ? ( <> -
    +

    Loading sessions

    ) : ( @@ -128,7 +127,7 @@ const SessionsModalContent: FunctionComponent<{ {sessions.length > 0 && (
      {sessions.map((session) => ( -
    • +
    • {session.device_info}

      {session.current ? ( Current session @@ -202,13 +201,15 @@ const SessionsModalContent: FunctionComponent<{ ) } -export const SessionsModal: FunctionComponent<{ +const SessionsModal: FunctionComponent<{ appState: AppState application: WebApplication -}> = observer(({ appState, application }) => { +}> = ({ appState, application }) => { if (appState.isSessionsModalVisible) { return } else { return null } -}) +} + +export default observer(SessionsModal) diff --git a/app/assets/javascripts/Components/Shared/AccordionItem.tsx b/app/assets/javascripts/Components/Shared/AccordionItem.tsx index c33dcb692..45449010e 100644 --- a/app/assets/javascripts/Components/Shared/AccordionItem.tsx +++ b/app/assets/javascripts/Components/Shared/AccordionItem.tsx @@ -1,14 +1,13 @@ -import { FunctionalComponent } from 'preact' -import { useRef, useState } from 'preact/hooks' +import { FunctionComponent, useRef, useState } from 'react' import { ArrowDownCheckmarkIcon } from '@standardnotes/icons' -import { Title } from '@/Components/Preferences/PreferencesComponents' +import { Title } from '@/Components/Preferences/PreferencesComponents/Content' type Props = { title: string | JSX.Element className?: string } -export const AccordionItem: FunctionalComponent = ({ title, className = '', children }) => { +const AccordionItem: FunctionComponent = ({ title, className = '', children }) => { const elementRef = useRef(null) const [isExpanded, setIsExpanded] = useState(false) @@ -34,3 +33,5 @@ export const AccordionItem: FunctionalComponent = ({ title, className = '
    ) } + +export default AccordionItem diff --git a/app/assets/javascripts/Components/Shared/HorizontalSeparator.tsx b/app/assets/javascripts/Components/Shared/HorizontalSeparator.tsx index bf5ac0de8..43739ffef 100644 --- a/app/assets/javascripts/Components/Shared/HorizontalSeparator.tsx +++ b/app/assets/javascripts/Components/Shared/HorizontalSeparator.tsx @@ -1,8 +1,10 @@ -import { FunctionalComponent } from 'preact' +import { FunctionComponent } from 'react' type Props = { classes?: string } -export const HorizontalSeparator: FunctionalComponent = ({ classes = '' }) => { +const HorizontalSeparator: FunctionComponent = ({ classes = '' }) => { return
    } + +export default HorizontalSeparator diff --git a/app/assets/javascripts/Components/Shared/ModalDialog.tsx b/app/assets/javascripts/Components/Shared/ModalDialog.tsx index 11af27610..522896254 100644 --- a/app/assets/javascripts/Components/Shared/ModalDialog.tsx +++ b/app/assets/javascripts/Components/Shared/ModalDialog.tsx @@ -1,8 +1,7 @@ -import { FunctionComponent } from 'preact' -import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog' -import { useRef } from 'preact/hooks' +import { FunctionComponent, useRef } from 'react' +import { AlertDialog } from '@reach/alert-dialog' -export const ModalDialog: FunctionComponent = ({ children }) => { +const ModalDialog: FunctionComponent = ({ children }) => { const ldRef = useRef(null) return ( @@ -20,43 +19,4 @@ export const ModalDialog: FunctionComponent = ({ children }) => { ) } -export const ModalDialogLabel: FunctionComponent<{ - closeDialog: () => void - className?: string -}> = ({ children, closeDialog, className }) => ( - -
    -
    {children}
    -
    - Close -
    -
    -
    -
    -) - -export const ModalDialogDescription: FunctionComponent<{ - className?: string -}> = ({ children, className = '' }) => ( - - {children} - -) - -export const ModalDialogButtons: FunctionComponent<{ className?: string }> = ({ children, className }) => ( - <> -
    -
    - {children != undefined && Array.isArray(children) - ? children.map((child, idx, arr) => ( - <> - {child} - {idx < arr.length - 1 ?
    : undefined} - - )) - : children} -
    - -) - export default ModalDialog diff --git a/app/assets/javascripts/Components/Shared/ModalDialogButtons.tsx b/app/assets/javascripts/Components/Shared/ModalDialogButtons.tsx new file mode 100644 index 000000000..1ab16496d --- /dev/null +++ b/app/assets/javascripts/Components/Shared/ModalDialogButtons.tsx @@ -0,0 +1,23 @@ +import { FunctionComponent } from 'react' + +type Props = { + className?: string +} + +const ModalDialogButtons: FunctionComponent = ({ children, className }) => ( + <> +
    +
    + {children != undefined && Array.isArray(children) + ? children.map((child, idx, arr) => ( + <> + {child} + {idx < arr.length - 1 ?
    : undefined} + + )) + : children} +
    + +) + +export default ModalDialogButtons diff --git a/app/assets/javascripts/Components/Shared/ModalDialogDescription.tsx b/app/assets/javascripts/Components/Shared/ModalDialogDescription.tsx new file mode 100644 index 000000000..99675626c --- /dev/null +++ b/app/assets/javascripts/Components/Shared/ModalDialogDescription.tsx @@ -0,0 +1,14 @@ +import { FunctionComponent } from 'react' +import { AlertDialogDescription } from '@reach/alert-dialog' + +type Props = { + className?: string +} + +const ModalDialogDescription: FunctionComponent = ({ children, className = '' }) => ( + + {children} + +) + +export default ModalDialogDescription diff --git a/app/assets/javascripts/Components/Shared/ModalDialogLabel.tsx b/app/assets/javascripts/Components/Shared/ModalDialogLabel.tsx new file mode 100644 index 000000000..74ec5a2c3 --- /dev/null +++ b/app/assets/javascripts/Components/Shared/ModalDialogLabel.tsx @@ -0,0 +1,21 @@ +import { FunctionComponent } from 'react' +import { AlertDialogLabel } from '@reach/alert-dialog' + +type Props = { + closeDialog: () => void + className?: string +} + +const ModalDialogLabel: FunctionComponent = ({ children, closeDialog, className }) => ( + +
    +
    {children}
    +
    + Close +
    +
    +
    +
    +) + +export default ModalDialogLabel diff --git a/app/assets/javascripts/Components/Switch/Switch.tsx b/app/assets/javascripts/Components/Switch/Switch.tsx index b07a54e3c..86e68ea1f 100644 --- a/app/assets/javascripts/Components/Switch/Switch.tsx +++ b/app/assets/javascripts/Components/Switch/Switch.tsx @@ -1,20 +1,9 @@ import { CustomCheckboxContainer, CustomCheckboxInput, CustomCheckboxInputProps } from '@reach/checkbox' import '@reach/checkbox/styles.css' -import { ComponentChildren, FunctionalComponent } from 'preact' -import { useState } from 'preact/hooks' +import { FunctionComponent, useState } from 'react' +import { SwitchProps } from './SwitchProps' -export type SwitchProps = { - checked?: boolean - // Optional in case it is wrapped in a button (e.g. a menu item) - onChange?: (checked: boolean) => void - className?: string - children?: ComponentChildren - role?: string - disabled?: boolean - tabIndex?: number -} - -export const Switch: FunctionalComponent = (props: SwitchProps) => { +const Switch: FunctionComponent = (props: SwitchProps) => { const [checkedState, setChecked] = useState(props.checked || false) const checked = props.checked ?? checkedState const className = props.className ?? '' @@ -51,3 +40,5 @@ export const Switch: FunctionalComponent = (props: SwitchProps) => ) } + +export default Switch diff --git a/app/assets/javascripts/Components/Switch/SwitchProps.tsx b/app/assets/javascripts/Components/Switch/SwitchProps.tsx new file mode 100644 index 000000000..0e5e02e68 --- /dev/null +++ b/app/assets/javascripts/Components/Switch/SwitchProps.tsx @@ -0,0 +1,11 @@ +import { ReactNode } from 'react' + +export type SwitchProps = { + checked?: boolean + onChange?: (checked: boolean) => void + className?: string + children?: ReactNode + role?: string + disabled?: boolean + tabIndex?: number +} diff --git a/app/assets/javascripts/Components/SyncResolutionMenu/SyncResolutionMenu.tsx b/app/assets/javascripts/Components/SyncResolutionMenu/SyncResolutionMenu.tsx index 0e3c45350..ecce71632 100644 --- a/app/assets/javascripts/Components/SyncResolutionMenu/SyncResolutionMenu.tsx +++ b/app/assets/javascripts/Components/SyncResolutionMenu/SyncResolutionMenu.tsx @@ -6,7 +6,7 @@ type Props = { close: () => void } -export class SyncResolutionMenu extends PureComponent { +class SyncResolutionMenu extends PureComponent { constructor(props: Props) { super(props, props.application) } @@ -55,3 +55,5 @@ export class SyncResolutionMenu extends PureComponent { ) } } + +export default SyncResolutionMenu diff --git a/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagHint.tsx b/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagHint.tsx index 093045639..504ffdb0d 100644 --- a/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagHint.tsx +++ b/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagHint.tsx @@ -1,14 +1,14 @@ import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { useRef, useEffect, useCallback } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' +import { useRef, useEffect, useCallback, FocusEventHandler, KeyboardEventHandler } from 'react' +import Icon from '@/Components/Icon/Icon' type Props = { appState: AppState closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void } -export const AutocompleteTagHint = observer(({ appState, closeOnBlur }: Props) => { +const AutocompleteTagHint = ({ appState, closeOnBlur }: Props) => { const { autocompleteTagHintFocused } = appState.noteTags const hintRef = useRef(null) @@ -24,16 +24,16 @@ export const AutocompleteTagHint = observer(({ appState, closeOnBlur }: Props) = appState.noteTags.setAutocompleteTagHintFocused(true) }, [appState]) - const onBlur = useCallback( - (event: FocusEvent) => { + const onBlur: FocusEventHandler = useCallback( + (event) => { closeOnBlur(event) appState.noteTags.setAutocompleteTagHintFocused(false) }, [appState, closeOnBlur], ) - const onKeyDown = useCallback( - (event: KeyboardEvent) => { + const onKeyDown: KeyboardEventHandler = useCallback( + (event) => { if (event.key === 'ArrowUp') { if (autocompleteTagResults.length > 0) { const lastTagResult = autocompleteTagResults[autocompleteTagResults.length - 1] @@ -75,4 +75,6 @@ export const AutocompleteTagHint = observer(({ appState, closeOnBlur }: Props) = ) -}) +} + +export default observer(AutocompleteTagHint) diff --git a/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagInput.tsx b/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagInput.tsx index 07215243b..9f58fa47c 100644 --- a/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagInput.tsx +++ b/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagInput.tsx @@ -1,9 +1,17 @@ -import { useEffect, useRef, useState } from 'preact/hooks' +import { + ChangeEventHandler, + FocusEventHandler, + FormEventHandler, + KeyboardEventHandler, + useEffect, + useRef, + useState, +} from 'react' import { Disclosure, DisclosurePanel } from '@reach/disclosure' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { AppState } from '@/UIModels/AppState' -import { AutocompleteTagResult } from './AutocompleteTagResult' -import { AutocompleteTagHint } from './AutocompleteTagHint' +import AutocompleteTagResult from './AutocompleteTagResult' +import AutocompleteTagHint from './AutocompleteTagHint' import { observer } from 'mobx-react-lite' import { SNTag } from '@standardnotes/snjs' @@ -11,7 +19,7 @@ type Props = { appState: AppState } -export const AutocompleteTagInput = observer(({ appState }: Props) => { +const AutocompleteTagInput = ({ appState }: Props) => { const { autocompleteInputFocused, autocompleteSearchQuery, @@ -41,8 +49,8 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => { } } - const onSearchQueryChange = (event: Event) => { - const query = (event.target as HTMLInputElement).value + const onSearchQueryChange: ChangeEventHandler = (event) => { + const query = event.target.value if (query === '') { appState.noteTags.clearAutocompleteSearch() @@ -52,14 +60,14 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => { } } - const onFormSubmit = async (event: Event) => { + const onFormSubmit: FormEventHandler = async (event) => { event.preventDefault() if (autocompleteSearchQuery !== '') { await appState.noteTags.createAndAddNewTag() } } - const onKeyDown = (event: KeyboardEvent) => { + const onKeyDown: KeyboardEventHandler = (event) => { switch (event.key) { case 'Backspace': case 'ArrowLeft': @@ -85,7 +93,7 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => { appState.noteTags.setAutocompleteInputFocused(true) } - const onBlur = (event: FocusEvent) => { + const onBlur: FocusEventHandler = (event) => { closeOnBlur(event) appState.noteTags.setAutocompleteInputFocused(false) } @@ -139,4 +147,6 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
    ) -}) +} + +export default observer(AutocompleteTagInput) diff --git a/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagResult.tsx b/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagResult.tsx index cce905368..64dd5f0e1 100644 --- a/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagResult.tsx +++ b/app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagResult.tsx @@ -2,8 +2,8 @@ import { AppState } from '@/UIModels/AppState' import { splitQueryInString } from '@/Utils/StringUtils' import { SNTag } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { useEffect, useRef } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' +import { FocusEventHandler, KeyboardEventHandler, useEffect, useRef } from 'react' +import Icon from '@/Components/Icon/Icon' type Props = { appState: AppState @@ -11,7 +11,7 @@ type Props = { closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void } -export const AutocompleteTagResult = observer(({ appState, tagResult, closeOnBlur }: Props) => { +const AutocompleteTagResult = ({ appState, tagResult, closeOnBlur }: Props) => { const { autocompleteSearchQuery, autocompleteTagHintVisible, autocompleteTagResults, focusedTagResultUuid } = appState.noteTags @@ -26,7 +26,7 @@ export const AutocompleteTagResult = observer(({ appState, tagResult, closeOnBlu appState.noteTags.setAutocompleteInputFocused(true) } - const onKeyDown = (event: KeyboardEvent) => { + const onKeyDown: KeyboardEventHandler = (event) => { const tagResultIndex = appState.noteTags.getTagIndex(tagResult, autocompleteTagResults) switch (event.key) { case 'ArrowUp': @@ -54,7 +54,7 @@ export const AutocompleteTagResult = observer(({ appState, tagResult, closeOnBlu appState.noteTags.setFocusedTagResultUuid(tagResult.uuid) } - const onBlur = (event: FocusEvent) => { + const onBlur: FocusEventHandler = (event) => { closeOnBlur(event) appState.noteTags.setFocusedTagResultUuid(undefined) } @@ -97,4 +97,6 @@ export const AutocompleteTagResult = observer(({ appState, tagResult, closeOnBlu ) -}) +} + +export default observer(AutocompleteTagResult) diff --git a/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx b/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx index a1c43c378..bcefc5b66 100644 --- a/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx +++ b/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx @@ -1,8 +1,9 @@ -import { Icon } from '@/Components/Icon/Icon' +import Icon from '@/Components/Icon/Icon' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { FeaturesState } from '@/UIModels/AppState/FeaturesState' import { TagsState } from '@/UIModels/AppState/TagsState' import { observer } from 'mobx-react-lite' +import { FunctionComponent } from 'react' import { useDrop } from 'react-dnd' import { DropItem, DropProps, ItemTypes } from './DragNDrop' @@ -11,7 +12,7 @@ type Props = { featuresState: FeaturesState } -export const RootTagDropZone: React.FC = observer(({ tagsState }) => { +const RootTagDropZone: FunctionComponent = ({ tagsState }) => { const premiumModal = usePremiumModal() const [{ isOver, canDrop }, dropRef] = useDrop( @@ -40,4 +41,6 @@ export const RootTagDropZone: React.FC = observer(({ tagsState }) => {

    ) -}) +} + +export default observer(RootTagDropZone) diff --git a/app/assets/javascripts/Components/Tags/SmartViewsList.tsx b/app/assets/javascripts/Components/Tags/SmartViewsList.tsx index 381dadba5..d524fdf9f 100644 --- a/app/assets/javascripts/Components/Tags/SmartViewsList.tsx +++ b/app/assets/javascripts/Components/Tags/SmartViewsList.tsx @@ -1,18 +1,13 @@ import { AppState } from '@/UIModels/AppState' -import { isStateDealloced } from '@/UIModels/AppState/AbstractState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { SmartViewsListItem } from './SmartViewsListItem' +import { FunctionComponent } from 'react' +import SmartViewsListItem from './SmartViewsListItem' type Props = { appState: AppState } -export const SmartViewsList: FunctionComponent = observer(({ appState }: Props) => { - if (isStateDealloced(appState)) { - return null - } - +const SmartViewsList: FunctionComponent = ({ appState }: Props) => { const allViews = appState.tags.smartViews return ( @@ -22,4 +17,6 @@ export const SmartViewsList: FunctionComponent = observer(({ appState }: })} ) -}) +} + +export default observer(SmartViewsList) diff --git a/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx b/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx index 3d2f2a1ed..0c0feea47 100644 --- a/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx +++ b/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx @@ -1,11 +1,18 @@ -import { Icon } from '@/Components/Icon/Icon' +import Icon from '@/Components/Icon/Icon' import { FeaturesState } from '@/UIModels/AppState/FeaturesState' import { TagsState } from '@/UIModels/AppState/TagsState' import '@reach/tooltip/styles.css' import { SmartView, SystemViewId, IconType, isSystemView } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' +import { + FormEventHandler, + FunctionComponent, + KeyboardEventHandler, + useCallback, + useEffect, + useRef, + useState, +} from 'react' type Props = { view: SmartView @@ -36,7 +43,7 @@ const smartViewIconType = (view: SmartView): IconType => { return 'hashtag' } -export const SmartViewsListItem: FunctionComponent = observer(({ view, tagsState }) => { +const SmartViewsListItem: FunctionComponent = ({ view, tagsState }) => { const [title, setTitle] = useState(view.title || '') const inputRef = useRef(null) @@ -57,16 +64,16 @@ export const SmartViewsListItem: FunctionComponent = observer(({ view, ta setTitle(view.title) }, [tagsState, view, title, setTitle]) - const onInput = useCallback( - (e: Event) => { + const onInput: FormEventHandler = useCallback( + (e) => { const value = (e.target as HTMLInputElement).value setTitle(value) }, [setTitle], ) - const onKeyUp = useCallback( - (e: KeyboardEvent) => { + const onKeyUp: KeyboardEventHandler = useCallback( + (e) => { if (e.code === 'Enter') { inputRef.current?.blur() e.preventDefault() @@ -149,4 +156,6 @@ export const SmartViewsListItem: FunctionComponent = observer(({ view, ta
    ) -}) +} + +export default observer(SmartViewsListItem) diff --git a/app/assets/javascripts/Components/Tags/SmartViewsSection.tsx b/app/assets/javascripts/Components/Tags/SmartViewsSection.tsx index 872056e0f..60c2e3a44 100644 --- a/app/assets/javascripts/Components/Tags/SmartViewsSection.tsx +++ b/app/assets/javascripts/Components/Tags/SmartViewsSection.tsx @@ -1,16 +1,18 @@ import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { SmartViewsList } from './SmartViewsList' +import { FunctionComponent } from 'react' +import SmartViewsList from './SmartViewsList' type Props = { appState: AppState } -export const SmartViewsSection: FunctionComponent = observer(({ appState }) => { +const SmartViewsSection: FunctionComponent = ({ appState }) => { return (
    ) -}) +} + +export default observer(SmartViewsSection) diff --git a/app/assets/javascripts/Components/Tags/TagContextMenu.tsx b/app/assets/javascripts/Components/Tags/TagContextMenu.tsx index fc7f6b1f1..02804f9f7 100644 --- a/app/assets/javascripts/Components/Tags/TagContextMenu.tsx +++ b/app/assets/javascripts/Components/Tags/TagContextMenu.tsx @@ -1,30 +1,25 @@ import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useRef } from 'preact/hooks' -import { Icon } from '@/Components/Icon/Icon' -import { Menu } from '@/Components/Menu/Menu' -import { MenuItem, MenuItemType } from '@/Components/Menu/MenuItem' +import { useCallback, useEffect, useRef } from 'react' +import Icon from '@/Components/Icon/Icon' +import Menu from '@/Components/Menu/Menu' +import MenuItem from '@/Components/Menu/MenuItem' +import { MenuItemType } from '@/Components/Menu/MenuItemType' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { SNTag } from '@standardnotes/snjs' import { isStateDealloced } from '@/UIModels/AppState/AbstractState' -type Props = { +type WrapperProps = { appState: AppState } -export const TagsContextMenu: FunctionComponent = observer(({ appState }: Props) => { - if (isStateDealloced(appState)) { - return null - } +type ContextMenuProps = WrapperProps & { + selectedTag: SNTag +} +const TagsContextMenu = observer(({ appState, selectedTag }: ContextMenuProps) => { const premiumModal = usePremiumModal() - const selectedTag = appState.tags.selected - - if (!selectedTag || !(selectedTag instanceof SNTag)) { - return null - } const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = appState.tags @@ -101,3 +96,21 @@ export const TagsContextMenu: FunctionComponent = observer(({ appState }:
    ) : null }) + +TagsContextMenu.displayName = 'TagsContextMenu' + +const TagsContextMenuWrapper = ({ appState }: WrapperProps) => { + if (isStateDealloced(appState)) { + return null + } + + const selectedTag = appState.tags.selected + + if (!selectedTag || !(selectedTag instanceof SNTag)) { + return null + } + + return +} + +export default observer(TagsContextMenuWrapper) diff --git a/app/assets/javascripts/Components/Tags/TagsList.tsx b/app/assets/javascripts/Components/Tags/TagsList.tsx index 2bb8db491..3dca17030 100644 --- a/app/assets/javascripts/Components/Tags/TagsList.tsx +++ b/app/assets/javascripts/Components/Tags/TagsList.tsx @@ -1,25 +1,19 @@ import { AppState } from '@/UIModels/AppState' -import { isStateDealloced } from '@/UIModels/AppState/AbstractState' import { isMobile } from '@/Utils' import { SNTag } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback } from 'preact/hooks' +import { FunctionComponent, useCallback } from 'react' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import { TouchBackend } from 'react-dnd-touch-backend' -import { RootTagDropZone } from './RootTagDropZone' +import RootTagDropZone from './RootTagDropZone' import { TagsListItem } from './TagsListItem' type Props = { appState: AppState } -export const TagsList: FunctionComponent = observer(({ appState }: Props) => { - if (isStateDealloced(appState)) { - return null - } - +const TagsList: FunctionComponent = ({ appState }: Props) => { const tagsState = appState.tags const allTags = tagsState.allLocalRootTags @@ -68,4 +62,6 @@ export const TagsList: FunctionComponent = observer(({ appState }: Props) )} ) -}) +} + +export default observer(TagsList) diff --git a/app/assets/javascripts/Components/Tags/TagsListItem.tsx b/app/assets/javascripts/Components/Tags/TagsListItem.tsx index 40db65e0c..fd4bd92f4 100644 --- a/app/assets/javascripts/Components/Tags/TagsListItem.tsx +++ b/app/assets/javascripts/Components/Tags/TagsListItem.tsx @@ -1,4 +1,4 @@ -import { Icon } from '@/Components/Icon/Icon' +import Icon from '@/Components/Icon/Icon' import { TAG_FOLDERS_FEATURE_NAME } from '@/Constants' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { KeyboardKey } from '@/Services/IOService' @@ -8,8 +8,16 @@ import '@reach/tooltip/styles.css' import { SNTag } from '@standardnotes/snjs' import { computed } from 'mobx' import { observer } from 'mobx-react-lite' -import { FunctionComponent, JSX } from 'preact' -import { useCallback, useEffect, useRef, useState } from 'preact/hooks' +import { + FormEventHandler, + FunctionComponent, + KeyboardEventHandler, + MouseEventHandler, + useCallback, + useEffect, + useRef, + useState, +} from 'react' import { useDrag, useDrop } from 'react-dnd' import { DropItem, DropProps, ItemTypes } from './DragNDrop' @@ -29,7 +37,7 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, const [subtagTitle, setSubtagTitle] = useState('') const inputRef = useRef(null) const subtagInputRef = useRef(null) - const menuButtonRef = useRef(null) + const menuButtonRef = useRef(null) const isSelected = tagsState.selected === tag const isEditing = tagsState.editingTag === tag @@ -58,8 +66,8 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, setTitle(tag.title || '') }, [setTitle, tag]) - const toggleChildren = useCallback( - (e: MouseEvent) => { + const toggleChildren: MouseEventHandler = useCallback( + (e) => { e.stopPropagation() setShowChildren((x) => { tagsState.setExpanded(tag, !x) @@ -78,16 +86,16 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, setTitle(tag.title) }, [tagsState, tag, title, setTitle]) - const onInput = useCallback( - (e: JSX.TargetedEvent) => { + const onInput: FormEventHandler = useCallback( + (e) => { const value = (e.target as HTMLInputElement).value setTitle(value) }, [setTitle], ) - const onKeyDown = useCallback( - (e: KeyboardEvent) => { + const onKeyDown: KeyboardEventHandler = useCallback( + (e) => { if (e.key === KeyboardKey.Enter) { inputRef.current?.blur() e.preventDefault() @@ -102,7 +110,7 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, } }, [inputRef, isEditing]) - const onSubtagInput = useCallback((e: JSX.TargetedEvent) => { + const onSubtagInput = useCallback((e) => { const value = (e.target as HTMLInputElement).value setSubtagTitle(value) }, []) @@ -112,8 +120,8 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, setSubtagTitle('') }, [subtagTitle, tag, tagsState]) - const onSubtagKeyDown = useCallback( - (e: KeyboardEvent) => { + const onSubtagKeyDown: KeyboardEventHandler = useCallback( + (e) => { if (e.key === KeyboardKey.Enter) { e.preventDefault() subtagInputRef.current?.blur() @@ -189,7 +197,7 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, style={{ paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`, }} - onContextMenu={(e: MouseEvent) => { + onContextMenu={(e) => { e.preventDefault() onContextMenu(tag, e.clientX, e.clientY) }} @@ -197,14 +205,15 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features,
    {hasAtLeastOneFolder && (
    - +
    )}
    @@ -222,7 +231,8 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, ref={inputRef} />
    - +
    {noteCounts.get()}
    @@ -282,3 +292,5 @@ export const TagsListItem: FunctionComponent = observer(({ tag, features, ) }) + +TagsListItem.displayName = 'TagsListItem' diff --git a/app/assets/javascripts/Components/Tags/TagsSection.tsx b/app/assets/javascripts/Components/Tags/TagsSection.tsx index 5d3b158bd..acf67966c 100644 --- a/app/assets/javascripts/Components/Tags/TagsSection.tsx +++ b/app/assets/javascripts/Components/Tags/TagsSection.tsx @@ -1,17 +1,16 @@ -import { TagsList } from '@/Components/Tags/TagsList' +import TagsList from '@/Components/Tags/TagsList' import { AppState } from '@/UIModels/AppState' import { ApplicationEvent } from '@/__mocks__/@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback, useEffect, useState } from 'preact/hooks' -import { TagsSectionAddButton } from './TagsSectionAddButton' -import { TagsSectionTitle } from './TagsSectionTitle' +import { FunctionComponent, useCallback, useEffect, useState } from 'react' +import TagsSectionAddButton from './TagsSectionAddButton' +import TagsSectionTitle from './TagsSectionTitle' type Props = { appState: AppState } -export const TagsSection: FunctionComponent = observer(({ appState }) => { +const TagsSection: FunctionComponent = ({ appState }) => { const [hasMigration, setHasMigration] = useState(false) const checkIfMigrationNeeded = useCallback(() => { @@ -63,4 +62,6 @@ export const TagsSection: FunctionComponent = observer(({ appState }) => ) -}) +} + +export default observer(TagsSection) diff --git a/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx b/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx index 01a60517d..1b54363b0 100644 --- a/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx +++ b/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx @@ -1,15 +1,15 @@ -import { IconButton } from '@/Components/Button/IconButton' +import IconButton from '@/Components/Button/IconButton' import { FeaturesState } from '@/UIModels/AppState/FeaturesState' import { TagsState } from '@/UIModels/AppState/TagsState' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' type Props = { tags: TagsState features: FeaturesState } -export const TagsSectionAddButton: FunctionComponent = observer(({ tags }) => { +const TagsSectionAddButton: FunctionComponent = ({ tags }) => { return ( = observer(({ tags } onClick={() => tags.createNewTemplate()} /> ) -}) +} + +export default observer(TagsSectionAddButton) diff --git a/app/assets/javascripts/Components/Tags/TagsSectionTitle.tsx b/app/assets/javascripts/Components/Tags/TagsSectionTitle.tsx index 8dbaec464..211ed9d53 100644 --- a/app/assets/javascripts/Components/Tags/TagsSectionTitle.tsx +++ b/app/assets/javascripts/Components/Tags/TagsSectionTitle.tsx @@ -3,8 +3,7 @@ import { usePremiumModal } from '@/Hooks/usePremiumModal' import { FeaturesState } from '@/UIModels/AppState/FeaturesState' import { Tooltip } from '@reach/tooltip' import { observer } from 'mobx-react-lite' -import { FunctionComponent } from 'preact' -import { useCallback } from 'preact/hooks' +import { FunctionComponent, useCallback } from 'react' type Props = { features: FeaturesState @@ -12,7 +11,7 @@ type Props = { onClickMigration: () => void } -export const TagsSectionTitle: FunctionComponent = observer(({ features, hasMigration, onClickMigration }) => { +const TagsSectionTitle: FunctionComponent = ({ features, hasMigration, onClickMigration }) => { const entitledToFolders = features.hasFolders const modal = usePremiumModal() @@ -47,4 +46,6 @@ export const TagsSectionTitle: FunctionComponent = observer(({ features,
    ) -}) +} + +export default observer(TagsSectionTitle) diff --git a/app/assets/javascripts/Components/TitleBar/Title.tsx b/app/assets/javascripts/Components/TitleBar/Title.tsx index 178766141..c6c5b09d1 100644 --- a/app/assets/javascripts/Components/TitleBar/Title.tsx +++ b/app/assets/javascripts/Components/TitleBar/Title.tsx @@ -1,5 +1,11 @@ -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' -export const Title: FunctionComponent<{ className?: string }> = ({ children, className }) => { +type Props = { + className?: string +} + +const Title: FunctionComponent = ({ children, className }) => { return
    {children}
    } + +export default Title diff --git a/app/assets/javascripts/Components/TitleBar/TitleBar.tsx b/app/assets/javascripts/Components/TitleBar/TitleBar.tsx index 725513eab..55a6adb5b 100644 --- a/app/assets/javascripts/Components/TitleBar/TitleBar.tsx +++ b/app/assets/javascripts/Components/TitleBar/TitleBar.tsx @@ -1,5 +1,11 @@ -import { FunctionComponent } from 'preact' +import { FunctionComponent } from 'react' -export const TitleBar: FunctionComponent<{ className?: string }> = ({ children, className }) => ( +type Props = { + className?: string +} + +const TitleBar: FunctionComponent = ({ children, className }) => (
    {children}
    ) + +export default TitleBar diff --git a/app/assets/javascripts/Hooks/useBeforeUnload.tsx b/app/assets/javascripts/Hooks/useBeforeUnload.tsx index 1fcc2391e..32b722354 100644 --- a/app/assets/javascripts/Hooks/useBeforeUnload.tsx +++ b/app/assets/javascripts/Hooks/useBeforeUnload.tsx @@ -1,4 +1,4 @@ -import { useEffect } from '@node_modules/preact/hooks' +import { useEffect } from 'react' export const useBeforeUnload = (): void => { useEffect(() => { diff --git a/app/assets/javascripts/Hooks/useCloseOnBlur.ts b/app/assets/javascripts/Hooks/useCloseOnBlur.ts index 0396859c5..80da10296 100644 --- a/app/assets/javascripts/Hooks/useCloseOnBlur.ts +++ b/app/assets/javascripts/Hooks/useCloseOnBlur.ts @@ -1,4 +1,4 @@ -import { StateUpdater, useCallback, useState } from 'preact/hooks' +import { Dispatch, SetStateAction, useCallback, useState } from 'react' /** * @returns a callback that will close a dropdown if none of its children has @@ -8,7 +8,7 @@ import { StateUpdater, useCallback, useState } from 'preact/hooks' export function useCloseOnBlur( container: { current?: HTMLDivElement | null }, setOpen: (open: boolean) => void, -): [(event: { relatedTarget: EventTarget | null }) => void, StateUpdater] { +): [(event: { relatedTarget: EventTarget | null }) => void, Dispatch>] { const [locked, setLocked] = useState(false) return [ useCallback( diff --git a/app/assets/javascripts/Hooks/useCloseOnClickOutside.ts b/app/assets/javascripts/Hooks/useCloseOnClickOutside.ts index b9b86c562..261e6f455 100644 --- a/app/assets/javascripts/Hooks/useCloseOnClickOutside.ts +++ b/app/assets/javascripts/Hooks/useCloseOnClickOutside.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from 'preact/hooks' +import { useCallback, useEffect } from 'react' export function useCloseOnClickOutside(container: { current: HTMLDivElement | null }, callback: () => void): void { const closeOnClickOutside = useCallback( diff --git a/app/assets/javascripts/Hooks/useListKeyboardNavigation.ts b/app/assets/javascripts/Hooks/useListKeyboardNavigation.ts index 7f9945b3c..491437215 100644 --- a/app/assets/javascripts/Hooks/useListKeyboardNavigation.ts +++ b/app/assets/javascripts/Hooks/useListKeyboardNavigation.ts @@ -1,8 +1,8 @@ import { KeyboardKey } from '@/Services/IOService' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' -import { useCallback, useState, useEffect, Ref } from 'preact/hooks' +import { useCallback, useState, useEffect, RefObject } from 'react' -export const useListKeyboardNavigation = (container: Ref, initialFocus = 0) => { +export const useListKeyboardNavigation = (container: RefObject, initialFocus = 0) => { const [listItems, setListItems] = useState() const [focusedItemIndex, setFocusedItemIndex] = useState(initialFocus) diff --git a/app/assets/javascripts/Hooks/usePremiumModal.tsx b/app/assets/javascripts/Hooks/usePremiumModal.tsx index 6a0405499..8e170a800 100644 --- a/app/assets/javascripts/Hooks/usePremiumModal.tsx +++ b/app/assets/javascripts/Hooks/usePremiumModal.tsx @@ -1,10 +1,8 @@ import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' import { observer } from 'mobx-react-lite' -import { ComponentChildren, FunctionalComponent, createContext } from 'preact' -import { useCallback, useContext } from 'preact/hooks' - -import { PremiumFeaturesModal } from '@/Components/PremiumFeaturesModal/PremiumFeaturesModal' +import { FunctionComponent, createContext, useCallback, useContext, ReactNode } from 'react' +import PremiumFeaturesModal from '@/Components/PremiumFeaturesModal/PremiumFeaturesModal' type PremiumModalContextData = { activate: (featureName: string) => void @@ -27,50 +25,51 @@ export const usePremiumModal = (): PremiumModalContextData => { interface Props { application: WebApplication appState: AppState - children: ComponentChildren | ComponentChildren[] + children: ReactNode } -export const PremiumModalProvider: FunctionalComponent = observer( - ({ application, appState, children }: Props) => { - const dealloced = !appState || appState.dealloced == undefined - if (dealloced) { - return null - } +const PremiumModalProvider: FunctionComponent = observer(({ application, appState, children }: Props) => { + const featureName = appState.features.premiumAlertFeatureName || '' - const featureName = appState.features.premiumAlertFeatureName || '' + const showModal = !!featureName - const showModal = !!featureName + const hasSubscription = Boolean( + appState.subscription.userSubscription && + !appState.subscription.isUserSubscriptionExpired && + !appState.subscription.isUserSubscriptionCanceled, + ) - const hasSubscription = Boolean( - appState.subscription.userSubscription && - !appState.subscription.isUserSubscriptionExpired && - !appState.subscription.isUserSubscriptionCanceled, - ) + const activate = useCallback( + (feature: string) => { + appState.features.showPremiumAlert(feature).catch(console.error) + }, + [appState], + ) - const activate = useCallback( - (feature: string) => { - appState.features.showPremiumAlert(feature).catch(console.error) - }, - [appState], - ) + const close = useCallback(() => { + appState.features.closePremiumAlert() + }, [appState]) - const close = useCallback(() => { - appState.features.closePremiumAlert() - }, [appState]) + return ( + <> + {showModal && ( + + )} + {children} + + ) +}) - return ( - <> - {showModal && ( - - )} - {children} - - ) - }, -) +PremiumModalProvider.displayName = 'PremiumModalProvider' + +const PremiumModalProviderWithDeallocateHandling: FunctionComponent = ({ application, appState, children }) => { + return +} + +export default observer(PremiumModalProviderWithDeallocateHandling) diff --git a/app/assets/javascripts/UIModels/Application.ts b/app/assets/javascripts/UIModels/Application.ts index c3308e24e..33d24c6cb 100644 --- a/app/assets/javascripts/UIModels/Application.ts +++ b/app/assets/javascripts/UIModels/Application.ts @@ -18,6 +18,7 @@ import { isDesktopDevice, DeinitMode, } from '@standardnotes/snjs' +import { makeObservable, observable } from 'mobx' type WebServices = { appState: AppState @@ -62,6 +63,10 @@ export class WebApplication extends SNApplication { supportsFileNavigation: window.enabledUnfinishedFeatures, }) + makeObservable(this, { + dealloced: observable, + }) + deviceInterface.setApplication(this) this.noteControllerGroup = new NoteGroupController(this) this.iconsController = new IconsController() diff --git a/app/assets/javascripts/Utils/FormatLastSyncDate.ts b/app/assets/javascripts/Utils/FormatLastSyncDate.ts new file mode 100644 index 000000000..0d10d18c4 --- /dev/null +++ b/app/assets/javascripts/Utils/FormatLastSyncDate.ts @@ -0,0 +1,5 @@ +import { dateToLocalizedString } from '@standardnotes/snjs/' + +export const formatLastSyncDate = (lastUpdatedDate: Date) => { + return dateToLocalizedString(lastUpdatedDate) +} diff --git a/app/assets/javascripts/Utils/PreactUtils.ts b/app/assets/javascripts/Utils/PreactUtils.ts deleted file mode 100644 index 20bf725be..000000000 --- a/app/assets/javascripts/Utils/PreactUtils.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { render } from 'preact' - -/** - * Source: https://stackoverflow.com/questions/50946950/how-to-destroy-root-preact-node - * For some reason importing `import { unmountComponentAtNode } from 'preact/compat'` inside of app/index.tsx - * results in the app failing to compile. - */ -export function unmountComponentAtRoot(root: HTMLElement) { - render(null, root) -} diff --git a/app/assets/javascripts/jest.config.js b/app/assets/javascripts/jest.config.js index f7dfda989..9125ac632 100644 --- a/app/assets/javascripts/jest.config.js +++ b/app/assets/javascripts/jest.config.js @@ -11,8 +11,6 @@ module.exports = { ...pathsToModuleNameMapper(pathsFromTsconfig, { prefix: '', }), - '^react$': ['preact/compat'], - '^react-dom$': 'preact', '\\.(css|less|scss|sass)$': 'identity-obj-proxy', }, globals: { diff --git a/app/assets/javascripts/tsconfig.json b/app/assets/javascripts/tsconfig.json index 8a5ac4bce..555d51e32 100644 --- a/app/assets/javascripts/tsconfig.json +++ b/app/assets/javascripts/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../../node_modules/@standardnotes/config/src/tsconfig.json", "compilerOptions": { + "skipLibCheck": true, "target": "ES2019", "module": "commonjs", "moduleResolution": "node", @@ -15,15 +16,29 @@ "declarationDir": "../../../dist/@types", "baseUrl": ".", "jsx": "react-jsx", - "jsxImportSource": "preact", - "typeRoots": ["./@types", "../../../node_modules/@types"], + "typeRoots": [ + "./@types", + "../../../node_modules/@types" + ], "paths": { - "%/*": ["../templates/*"], - "@/*": ["./*"], - "@Controllers/*": ["./controllers/*"], - "@Views/*": ["./views/*"], - "@Services/*": ["Services/*"], - "@node_modules/*": ["../../../node_modules/*"] + "%/*": [ + "../templates/*" + ], + "@/*": [ + "./*" + ], + "@Controllers/*": [ + "./controllers/*" + ], + "@Views/*": [ + "./views/*" + ], + "@Services/*": [ + "Services/*" + ], + "@node_modules/*": [ + "../../../node_modules/*" + ] } } } \ No newline at end of file diff --git a/package.json b/package.json index 590f6d6ef..fad70660c 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@standardnotes/config": "^2.4.3", "@types/jest": "^27.4.1", "@types/react": "^17.0.42", + "@types/react-dom": "^18.0.5", "@types/wicg-file-system-access": "^2020.9.5", "@typescript-eslint/eslint-plugin": "^5.20.0", "@typescript-eslint/parser": "^5.20.0", @@ -74,16 +75,17 @@ "@standardnotes/icons": "^1.1.8", "@standardnotes/services": "^1.13.3", "@standardnotes/sncrypto-web": "1.10.1", - "@standardnotes/snjs": "^2.114.2", + "@standardnotes/snjs": "^2.114.5", "@standardnotes/stylekit": "5.29.2", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", - "preact": "^10.7.1", "qrcode.react": "^3.0.1", + "react": "^18.1.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dnd-touch-backend": "^16.0.1" + "react-dnd-touch-backend": "^16.0.1", + "react-dom": "^18.1.0" }, "lint-staged": { "app/**/*.{js,ts,jsx,tsx}": "eslint --cache --fix", diff --git a/webpack.config.js b/webpack.config.js index 12185cdf3..52a1622d3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -50,9 +50,6 @@ module.exports = (env) => { '@Views': path.resolve(__dirname, 'app/assets/javascripts/views'), '@Services': path.resolve(__dirname, 'app/assets/javascripts/services'), '@node_modules': path.resolve(__dirname, 'node_modules'), - react: 'preact/compat', - 'react-dom/test-utils': 'preact/test-utils', - 'react-dom': 'preact/compat', }, }, module: { diff --git a/yarn.lock b/yarn.lock index 9de3df1c2..3b8b4d600 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2358,6 +2358,14 @@ "@standardnotes/common" "^1.22.0" jsonwebtoken "^8.5.1" +"@standardnotes/auth@^3.19.1": + version "3.19.1" + resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.19.1.tgz#09b1bf2a7c22bfb351b3f5af5a193566bfe4410c" + integrity sha512-ozzCan/uSdN8oQ2YSid5kVGts+13f63eKMyuiGesS1a/l0ezKWJHaFa3ulXETobhjJEF60yNQIjQ7sfdv+4L9A== + dependencies: + "@standardnotes/common" "^1.22.0" + jsonwebtoken "^8.5.1" + "@standardnotes/common@^1.22.0": version "1.22.0" resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.22.0.tgz#397604fb4b92901bac276940a2647509b70a7ad2" @@ -2379,22 +2387,22 @@ eslint-plugin-prettier "^4.0.0" prettier "^2.6.2" -"@standardnotes/domain-events@^2.28.13": - version "2.28.13" - resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.28.13.tgz#ee8434544ad175e6bfd7e9e59d6fea148dcaee1e" - integrity sha512-gKYfo1Yv0TKcG1CZJtjg2my3NyLddfWjdniPXCyFxZ7kHVMa2wGUjoxNpds8WQwSk9ktSlQV/8b7hv1Cjb0eXw== +"@standardnotes/domain-events@^2.28.14": + version "2.28.14" + resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.28.14.tgz#58de063de3ba93d016bdb7169b978d43b9c43df8" + integrity sha512-rMGQie66EK09AqRmxXOcVQSILt0TXdOGeVTRkKjZzGpAM+Y8YpOfG0/WOkBsB4pyMWBrSJvMA1aQtl7buslDrw== dependencies: - "@standardnotes/auth" "^3.19.0" - "@standardnotes/features" "^1.44.4" + "@standardnotes/auth" "^3.19.1" + "@standardnotes/features" "^1.44.5" -"@standardnotes/encryption@^1.8.3": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.8.3.tgz#ae46c995f034e1b5f3fa04672fd65e37c09037f5" - integrity sha512-oPaUakSQtOyEVrJxz/O4CQNnQYDeIZN9FBjzoIsrdg1rpl7to+5lUYo0vufc4fVLXCXVKg3UQiS9pYotIEo7og== +"@standardnotes/encryption@^1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.8.5.tgz#48ea926e9c707e9d27e4c7698c634b5bfa85d662" + integrity sha512-Ih6HCnreQNkR8YTmcem2PvKtxTUadDYx20Tp4UF7TSpeaUL4ETLipamJ6aqYasKYeuf4uuoiQb7LLYiad37NyQ== dependencies: - "@standardnotes/models" "^1.10.2" - "@standardnotes/responses" "^1.6.27" - "@standardnotes/services" "^1.13.3" + "@standardnotes/models" "^1.11.0" + "@standardnotes/responses" "^1.6.28" + "@standardnotes/services" "^1.13.5" "@standardnotes/features@^1.44.4": version "1.44.4" @@ -2404,7 +2412,15 @@ "@standardnotes/auth" "^3.19.0" "@standardnotes/common" "^1.22.0" -"@standardnotes/filepicker@1.16.2", "@standardnotes/filepicker@^1.16.2": +"@standardnotes/features@^1.44.5": + version "1.44.5" + resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.44.5.tgz#c1dd2d8b46387765c7bef76ee916b0698c1153b5" + integrity sha512-yMh9W6KP/HfqEJEGh469Vh6PlH/U0GSif4amt5paBIdhvZt2Rmqv81wt+ac9kPp7HkDxZhr8+Ly68OVznCeVjw== + dependencies: + "@standardnotes/auth" "^3.19.1" + "@standardnotes/common" "^1.22.0" + +"@standardnotes/filepicker@1.16.2": version "1.16.2" resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.2.tgz#d6fda94b5578f30e6b4f792c874e3eb11ce58453" integrity sha512-B290nRn2aJzVX1VXMTHhu66tbj1oZFaksTSLQxiEWeLQ76AFAQ3/5frVH/6Y8ahIvBn3XYyotOOyXeL9HkBu8g== @@ -2413,16 +2429,25 @@ "@standardnotes/services" "^1.13.3" "@standardnotes/utils" "^1.6.10" -"@standardnotes/files@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.3.2.tgz#efa676cd80a3178300f5e2f62c0fb9b21a625ec5" - integrity sha512-53jGUeYwS/EcIcJAXms+4bPAYVp6sI1kZmdRkS1+3T7+lsBB7wLth3wKIsNEmGLXHzOR0JwsjHND4VQABymUcA== +"@standardnotes/filepicker@^1.16.4": + version "1.16.4" + resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.4.tgz#742023dab908c477e28bf472183cae8309639d95" + integrity sha512-a8RHKtLgTG3WmgXvRafMNj5utGKaIZvgQhqyRfNt0EuIK1uSvdvt3jLA8VGeehP1JE0gc3XfZKCmE7vgNz8wDg== dependencies: - "@standardnotes/encryption" "^1.8.3" - "@standardnotes/filepicker" "^1.16.2" - "@standardnotes/models" "^1.10.2" - "@standardnotes/responses" "^1.6.27" - "@standardnotes/services" "^1.13.3" + "@standardnotes/common" "^1.22.0" + "@standardnotes/services" "^1.13.5" + "@standardnotes/utils" "^1.6.10" + +"@standardnotes/files@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.3.4.tgz#0b8c820b2aad2d24d5b837935999979e09a2a6bb" + integrity sha512-kSb8jhvnnLX43FlCL3JzMZxT3KjI+9I37DyZi9FR/s+8gQ6TyuCtk4JjCZxn1i+cRbdKyzeVPi2T24cvQEVs/g== + dependencies: + "@standardnotes/encryption" "^1.8.5" + "@standardnotes/filepicker" "^1.16.4" + "@standardnotes/models" "^1.11.0" + "@standardnotes/responses" "^1.6.28" + "@standardnotes/services" "^1.13.5" "@standardnotes/utils" "^1.6.10" "@standardnotes/icons@^1.1.8": @@ -2439,6 +2464,15 @@ "@standardnotes/responses" "^1.6.27" "@standardnotes/utils" "^1.6.10" +"@standardnotes/models@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.11.0.tgz#35647d0f8b1bb0a431298bd040c91343f4ffc2b1" + integrity sha512-5T4WBQeNfWpX9F5NUhI68ah5nAk+0+Umz3YphRjpa8MrmDiA32VH37LG43+Tp17vSxQeG2shVAduC15QmGch3g== + dependencies: + "@standardnotes/features" "^1.44.5" + "@standardnotes/responses" "^1.6.28" + "@standardnotes/utils" "^1.6.10" + "@standardnotes/responses@^1.6.27": version "1.6.27" resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.27.tgz#3a440090e5cee09f2980df5cc57a60f76adff735" @@ -2448,6 +2482,15 @@ "@standardnotes/common" "^1.22.0" "@standardnotes/features" "^1.44.4" +"@standardnotes/responses@^1.6.28": + version "1.6.28" + resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.28.tgz#846e56968d104bac360aa9080627123326f70af9" + integrity sha512-oaxoH1Qw9VrIF7Fgak5NGayGDQZl5lOuuxyXh8ngCOP7Qpgrr/5whm5ZnKTMYwD8kE9/Vi22G9oxVTz1JOErkA== + dependencies: + "@standardnotes/auth" "^3.19.1" + "@standardnotes/common" "^1.22.0" + "@standardnotes/features" "^1.44.5" + "@standardnotes/services@^1.13.3": version "1.13.3" resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.3.tgz#b857a6ed42b15d40c2e9d08b41739f5fb6b0d3e0" @@ -2459,6 +2502,17 @@ "@standardnotes/responses" "^1.6.27" "@standardnotes/utils" "^1.6.10" +"@standardnotes/services@^1.13.5": + version "1.13.5" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.5.tgz#644533f1a30b6d7459c295152c222a725c9a4f33" + integrity sha512-YDG8JgCI29Sa3s8f90rxeuakG7/MDuKSM8OytKKl16WDioKcKEysPlqo4blmj3ntK5mTR7aV/CGh4J+gNak+jw== + dependencies: + "@standardnotes/auth" "^3.19.1" + "@standardnotes/common" "^1.22.0" + "@standardnotes/models" "^1.11.0" + "@standardnotes/responses" "^1.6.28" + "@standardnotes/utils" "^1.6.10" + "@standardnotes/settings@^1.14.3": version "1.14.3" resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.14.3.tgz#021085e8c383a9893a2c49daa74cc0754ccd67b5" @@ -2478,21 +2532,21 @@ buffer "^6.0.3" libsodium-wrappers "^0.7.9" -"@standardnotes/snjs@^2.114.2": - version "2.114.2" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.114.2.tgz#c1560c538127cff6b008348cef24ff1767d117ba" - integrity sha512-0qAzkfQDe1yeO+la4wNxzQI2N7F7RaFBpQrVCwkBxnq3s+DDXK+BnmUWxDNL54cchxLCTdqhayIoDFmsG0j8xg== +"@standardnotes/snjs@^2.114.5": + version "2.114.5" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.114.5.tgz#1fee7e7c7558d591cfa99bc47a514eb08c67357d" + integrity sha512-SEriP3YsKoIn8idPbKNUnTocQtLt8jXngZ1O1DQrC0fL/aUxHeaVUinSjYyr8WXQqQatoA2Jngcouv3zpvmAkQ== dependencies: - "@standardnotes/auth" "^3.19.0" + "@standardnotes/auth" "^3.19.1" "@standardnotes/common" "^1.22.0" - "@standardnotes/domain-events" "^2.28.13" - "@standardnotes/encryption" "^1.8.3" - "@standardnotes/features" "^1.44.4" - "@standardnotes/filepicker" "^1.16.2" - "@standardnotes/files" "^1.3.2" - "@standardnotes/models" "^1.10.2" - "@standardnotes/responses" "^1.6.27" - "@standardnotes/services" "^1.13.3" + "@standardnotes/domain-events" "^2.28.14" + "@standardnotes/encryption" "^1.8.5" + "@standardnotes/features" "^1.44.5" + "@standardnotes/filepicker" "^1.16.4" + "@standardnotes/files" "^1.3.4" + "@standardnotes/models" "^1.11.0" + "@standardnotes/responses" "^1.6.28" + "@standardnotes/services" "^1.13.5" "@standardnotes/settings" "^1.14.3" "@standardnotes/sncrypto-common" "^1.9.0" "@standardnotes/utils" "^1.6.10" @@ -2740,6 +2794,22 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/react-dom@^18.0.5": + version "18.0.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.5.tgz#330b2d472c22f796e5531446939eacef8378444a" + integrity sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" + integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/react@^17.0.42": version "17.0.42" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.42.tgz#8242b9219bf8a911c47f248e327206fea3f4ee5a" @@ -7155,7 +7225,7 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -loose-envify@^1.0.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -8291,11 +8361,6 @@ postcss@^8.4.7: picocolors "^1.0.0" source-map-js "^1.0.2" -preact@^10.7.1: - version "10.7.1" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.7.1.tgz#bdd2b2dce91a5842c3b9b34dfe050e5401068c9e" - integrity sha512-MufnRFz39aIhs9AMFisonjzTud1PK1bY+jcJLo6m2T9Uh8AqjD77w11eAAawmjUogoGOnipECq7e/1RClIKsxg== - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8532,6 +8597,14 @@ react-dnd@^16.0.1: fast-deep-equal "^3.1.3" hoist-non-react-statics "^3.3.2" +react-dom@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f" + integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.22.0" + react-focus-lock@^2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.5.2.tgz#f1e4db5e25cd8789351f2bd5ebe91e9dcb9c2922" @@ -8582,6 +8655,13 @@ react-style-singleton@^2.1.0: invariant "^2.2.4" tslib "^1.0.0" +react@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890" + integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ== + dependencies: + loose-envify "^1.1.0" + read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" @@ -8947,6 +9027,13 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +scheduler@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8" + integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ== + dependencies: + loose-envify "^1.1.0" + schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" From 834459d3a84dce6b64094bb4e4115bd882ea0932 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Mon, 30 May 2022 13:38:24 +0530 Subject: [PATCH 25/39] fix: react-related fixes (#1050) * fix: no 'onChange' error * fix: "document.body is null" --- app/assets/javascripts/App.tsx | 13 ++++++----- .../WorkspaceSwitcher/WorkspaceMenuItem.tsx | 22 +++++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/App.tsx b/app/assets/javascripts/App.tsx index 9ca95133b..710f35274 100644 --- a/app/assets/javascripts/App.tsx +++ b/app/assets/javascripts/App.tsx @@ -28,7 +28,7 @@ import { StartApplication } from './Device/StartApplication' import { ApplicationGroup } from './UIModels/ApplicationGroup' import { WebOrDesktopDevice } from './Device/WebOrDesktopDevice' import { WebApplication } from './UIModels/Application' -import { createRoot } from 'react-dom/client' +import { createRoot, Root } from 'react-dom/client' let keyCount = 0 const getKey = () => { @@ -37,11 +37,6 @@ const getKey = () => { const RootId = 'app-group-root' -const rootElement = document.createElement('div') -rootElement.id = RootId -const appendedRootNode = document.body.appendChild(rootElement) -const root = createRoot(appendedRootNode) - const startApplication: StartApplication = async function startApplication( defaultSyncServerHost: string, device: WebOrDesktopDevice, @@ -50,6 +45,7 @@ const startApplication: StartApplication = async function startApplication( ) { SNLog.onLog = console.log SNLog.onError = console.error + let root: Root const onDestroy = () => { const rootElement = document.getElementById(RootId) as HTMLElement @@ -59,6 +55,11 @@ const startApplication: StartApplication = async function startApplication( } const renderApp = () => { + const rootElement = document.createElement('div') + rootElement.id = RootId + const appendedRootNode = document.body.appendChild(rootElement) + root = createRoot(appendedRootNode) + root.render( = ({ hideOptions, }) => { const [isRenaming, setIsRenaming] = useState(false) + const [inputValue, setInputValue] = useState(descriptor.label) const inputRef = useRef(null) useEffect(() => { @@ -37,20 +39,21 @@ const WorkspaceMenuItem: FunctionComponent = ({ } }, [isRenaming]) + const handleChange: ChangeEventHandler = useCallback((event) => { + setInputValue(event.target.value) + }, []) + const handleInputKeyDown: KeyboardEventHandler = useCallback((event) => { if (event.key === KeyboardKey.Enter) { inputRef.current?.blur() } }, []) - const handleInputBlur: FocusEventHandler = useCallback( - (event) => { - const name = event.target.value - renameDescriptor(name) - setIsRenaming(false) - }, - [renameDescriptor], - ) + const handleInputBlur: FocusEventHandler = useCallback(() => { + renameDescriptor(inputValue) + setIsRenaming(false) + setInputValue('') + }, [inputValue, renameDescriptor]) return ( = ({ From 7edd65cfe83b9896d77283cabd9f149f147771b3 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Mon, 30 May 2022 15:16:24 +0530 Subject: [PATCH 26/39] fix: note tags list styling (#1052) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index fad70660c..35cd85db2 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@standardnotes/services": "^1.13.3", "@standardnotes/sncrypto-web": "1.10.1", "@standardnotes/snjs": "^2.114.5", - "@standardnotes/stylekit": "5.29.2", + "@standardnotes/stylekit": "5.29.3", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index 3b8b4d600..5274ec156 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2551,10 +2551,10 @@ "@standardnotes/sncrypto-common" "^1.9.0" "@standardnotes/utils" "^1.6.10" -"@standardnotes/stylekit@5.29.2": - version "5.29.2" - resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.29.2.tgz#44c7f3ecef86126a853028c5a71851b85f470392" - integrity sha512-Y8sW65YH/2dCOadMJ1x+WvvwHcYxpUL/fvpQQ2/RKfM4EPtKZnqvSDoIMIQBNKt4+0ho6/BQkoPmUthpbGrLoQ== +"@standardnotes/stylekit@5.29.3": + version "5.29.3" + resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.29.3.tgz#8db39196d99ee4039eff5bf0d1bd408c4892e39b" + integrity sha512-BMJE+YEbC4xOvSXlUx23FAnoNtrVLW2Thcf2wkkF9lpc8ZbQJAF5Np7mcmtgBRFyR8apbz2jEVFaq7pYPOeVKg== dependencies: "@nanostores/react" "^0.2.0" "@reach/listbox" "^0.17.0" From 501da8ecc675e4720d5def57d04ae91c50173778 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Mon, 30 May 2022 16:18:26 +0530 Subject: [PATCH 27/39] feat: upload prompt on add button click in FilesView (#1053) --- .../ContentListView/ContentListView.tsx | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/Components/ContentListView/ContentListView.tsx b/app/assets/javascripts/Components/ContentListView/ContentListView.tsx index cb793be26..27c419f00 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentListView.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentListView.tsx @@ -2,7 +2,7 @@ import { KeyboardKey, KeyboardModifier } from '@/Services/IOService' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' import { PANEL_NAME_NOTES } from '@/Constants' -import { PrefKey } from '@standardnotes/snjs' +import { PrefKey, SystemViewId } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { ChangeEventHandler, @@ -10,6 +10,7 @@ import { KeyboardEventHandler, useCallback, useEffect, + useMemo, useRef, useState, } from 'react' @@ -54,6 +55,19 @@ const ContentListView: FunctionComponent = ({ application, appState }) => const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(displayOptionsMenuRef, setShowDisplayOptionsMenu) + const isFilesSmartView = useMemo( + () => appState.tags.selected?.uuid === SystemViewId.Files, + [appState.tags.selected?.uuid], + ) + + const addNewItem = useCallback(() => { + if (isFilesSmartView) { + void appState.files.uploadNewFile() + } else { + void createNewNote() + } + }, [appState.files, createNewNote, isFilesSmartView]) + useEffect(() => { /** * In the browser we're not allowed to override cmd/ctrl + n, so we have to @@ -65,7 +79,7 @@ const ContentListView: FunctionComponent = ({ application, appState }) => modifiers: [KeyboardModifier.Meta, KeyboardModifier.Ctrl], onKeyDown: (event) => { event.preventDefault() - void createNewNote() + addNewItem() }, }) @@ -104,7 +118,7 @@ const ContentListView: FunctionComponent = ({ application, appState }) => previousNoteKeyObserver() searchKeyObserver() } - }, [application.io, createNewNote, searchBarElement, selectNextItem, selectPreviousItem]) + }, [addNewItem, application.io, createNewNote, searchBarElement, selectNextItem, selectPreviousItem]) const onNoteFilterTextChange: ChangeEventHandler = useCallback( (e) => { @@ -142,6 +156,11 @@ const ContentListView: FunctionComponent = ({ application, appState }) => setShowDisplayOptionsMenu(!showDisplayOptionsMenu) }, [showDisplayOptionsMenu]) + const addButtonLabel = useMemo( + () => (isFilesSmartView ? 'Upload file' : 'Create a new note in the selected tag'), + [isFilesSmartView], + ) + return (
    = ({ application, appState }) =>
    {panelTitle}
    - v{appState.version} + v{viewControllerManager.version}
    {user ? ( <> diff --git a/app/assets/javascripts/Components/AccountMenu/MenuPaneSelector.tsx b/app/assets/javascripts/Components/AccountMenu/MenuPaneSelector.tsx index a456769dc..4544ac6e2 100644 --- a/app/assets/javascripts/Components/AccountMenu/MenuPaneSelector.tsx +++ b/app/assets/javascripts/Components/AccountMenu/MenuPaneSelector.tsx @@ -1,6 +1,6 @@ -import { WebApplication } from '@/UIModels/Application' -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ApplicationGroup } from '@/Application/ApplicationGroup' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { FunctionComponent, useState } from 'react' import { AccountMenuPane } from './AccountMenuPane' @@ -10,7 +10,7 @@ import GeneralAccountMenu from './GeneralAccountMenu' import SignInPane from './SignIn' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager application: WebApplication mainApplicationGroup: ApplicationGroup menuPane: AccountMenuPane @@ -20,7 +20,7 @@ type Props = { const MenuPaneSelector: FunctionComponent = ({ application, - appState, + viewControllerManager, menuPane, setMenuPane, closeMenu, @@ -33,7 +33,7 @@ const MenuPaneSelector: FunctionComponent = ({ case AccountMenuPane.GeneralMenu: return ( = ({ /> ) case AccountMenuPane.SignIn: - return + return ( + + ) case AccountMenuPane.Register: return ( = ({ case AccountMenuPane.ConfirmPassword: return ( void } -const SignInPane: FunctionComponent = ({ application, appState, setMenuPane }) => { - const { notesAndTagsCount } = appState.accountMenu +const SignInPane: FunctionComponent = ({ application, viewControllerManager, setMenuPane }) => { + const { notesAndTagsCount } = viewControllerManager.accountMenuController const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [error, setError] = useState('') @@ -86,7 +86,7 @@ const SignInPane: FunctionComponent = ({ application, appState, setMenuPa if (res.error) { throw new Error(res.error.message) } - appState.accountMenu.closeAccountMenu() + viewControllerManager.accountMenuController.closeAccountMenu() }) .catch((err) => { console.error(err) @@ -97,7 +97,7 @@ const SignInPane: FunctionComponent = ({ application, appState, setMenuPa .finally(() => { setIsSigningIn(false) }) - }, [appState, application, email, isEphemeral, isStrictSignin, password, shouldMergeLocal]) + }, [viewControllerManager, application, email, isEphemeral, isStrictSignin, password, shouldMergeLocal]) const onPrivateWorkspaceChange = useCallback( (newIsPrivateWorkspace: boolean, privateWorkspaceIdentifier?: string) => { @@ -201,7 +201,7 @@ const SignInPane: FunctionComponent = ({ application, appState, setMenuPa
    { - const { server } = appState.accountMenu +const User = ({ viewControllerManager, application }: Props) => { + const { server } = viewControllerManager.accountMenuController const user = application.getUser() as UserType return (
    - {appState.sync.errorMessage && ( + {viewControllerManager.syncStatusController.errorMessage && (
    Sync Unreachable
    - Hmm...we can't seem to sync your account. The reason: {appState.sync.errorMessage} + Hmm...we can't seem to sync your account. The reason:{' '} + {viewControllerManager.syncStatusController.errorMessage}
    = ({ mainApplicationGroup, - appState, + viewControllerManager, isOpen, hideWorkspaceOptions = false, }: Props) => { @@ -42,7 +42,7 @@ const WorkspaceSwitcherMenu: FunctionComponent = ({ }, [mainApplicationGroup]) const signoutAll = useCallback(async () => { - const confirmed = await appState.application.alertService.confirm( + const confirmed = await viewControllerManager.application.alertService.confirm( 'Are you sure you want to sign out of all workspaces on this device?', undefined, 'Sign out all', @@ -52,11 +52,11 @@ const WorkspaceSwitcherMenu: FunctionComponent = ({ return } mainApplicationGroup.signOutAllWorkspaces().catch(console.error) - }, [mainApplicationGroup, appState]) + }, [mainApplicationGroup, viewControllerManager]) const destroyWorkspace = useCallback(() => { - appState.accountMenu.setSigningOut(true) - }, [appState]) + viewControllerManager.accountMenuController.setSigningOut(true) + }, [viewControllerManager]) return ( diff --git a/app/assets/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx b/app/assets/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx index 95a788196..43907697d 100644 --- a/app/assets/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx +++ b/app/assets/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx @@ -1,6 +1,6 @@ import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' -import { AppState } from '@/UIModels/AppState' +import { ApplicationGroup } from '@/Application/ApplicationGroup' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { observer } from 'mobx-react-lite' import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' @@ -9,10 +9,10 @@ import WorkspaceSwitcherMenu from './WorkspaceSwitcherMenu' type Props = { mainApplicationGroup: ApplicationGroup - appState: AppState + viewControllerManager: ViewControllerManager } -const WorkspaceSwitcherOption: FunctionComponent = ({ mainApplicationGroup, appState }) => { +const WorkspaceSwitcherOption: FunctionComponent = ({ mainApplicationGroup, viewControllerManager }) => { const buttonRef = useRef(null) const menuRef = useRef(null) const [isOpen, setIsOpen] = useState(false) @@ -58,7 +58,11 @@ const WorkspaceSwitcherOption: FunctionComponent = ({ mainApplicationGrou {isOpen && (
    - +
    )} diff --git a/app/assets/javascripts/Components/ApplicationGroupView/ApplicationGroupView.tsx b/app/assets/javascripts/Components/ApplicationGroupView/ApplicationGroupView.tsx index 467debdaf..475c4d3e0 100644 --- a/app/assets/javascripts/Components/ApplicationGroupView/ApplicationGroupView.tsx +++ b/app/assets/javascripts/Components/ApplicationGroupView/ApplicationGroupView.tsx @@ -1,5 +1,5 @@ -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' -import { WebApplication } from '@/UIModels/Application' +import { ApplicationGroup } from '@/Application/ApplicationGroup' +import { WebApplication } from '@/Application/Application' import { Component } from 'react' import ApplicationView from '@/Components/ApplicationView/ApplicationView' import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice' diff --git a/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx b/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx index 1f2e8d1c0..0fe3edc89 100644 --- a/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx +++ b/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx @@ -1,10 +1,10 @@ -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' +import { ApplicationGroup } from '@/Application/ApplicationGroup' import { getPlatformString, getWindowUrlParams } from '@/Utils' -import { AppStateEvent, PanelResizedData } from '@/UIModels/AppState' +import { ViewControllerManagerEvent } from '@/Services/ViewControllerManager' import { ApplicationEvent, Challenge, removeFromArray } from '@standardnotes/snjs' import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants' import { alertDialog } from '@/Services/AlertService' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import Navigation from '@/Components/Navigation/Navigation' import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView' import Footer from '@/Components/Footer/Footer' @@ -23,6 +23,7 @@ import FilePreviewModalWrapper from '@/Components/Files/FilePreviewModal' import ContentListView from '@/Components/ContentListView/ContentListView' import FileContextMenuWrapper from '@/Components/FileContextMenu/FileContextMenu' import PermissionsModalWrapper from '@/Components/PermissionsModal/PermissionsModalWrapper' +import { PanelResizedData } from '@/Typings/PanelResizedData' type Props = { application: WebApplication @@ -36,7 +37,7 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio const [needsUnlock, setNeedsUnlock] = useState(true) const [challenges, setChallenges] = useState([]) - const appState = application.getAppState() + const viewControllerManager = application.getViewControllerManager() useEffect(() => { const desktopService = application.getDesktopService() @@ -119,8 +120,8 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio }, [application, onAppLaunch, onAppStart]) useEffect(() => { - const removeObserver = application.getAppState().addObserver(async (eventName, data) => { - if (eventName === AppStateEvent.PanelResized) { + const removeObserver = application.getViewControllerManager().addObserver(async (eventName, data) => { + if (eventName === ViewControllerManagerEvent.PanelResized) { const { panel, collapsed } = data as PanelResizedData let appClass = '' if (panel === PANEL_NAME_NOTES && collapsed) { @@ -130,7 +131,7 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio appClass += ' collapsed-navigation' } setAppClass(appClass) - } else if (eventName === AppStateEvent.WindowDidFocus) { + } else if (eventName === ViewControllerManagerEvent.WindowDidFocus) { if (!(await application.isLocked())) { application.sync.sync().catch(console.error) } @@ -155,7 +156,7 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio = ({ application, mainApplicatio })} ) - }, [appState, challenges, mainApplicationGroup, removeChallenge, application]) + }, [viewControllerManager, challenges, mainApplicationGroup, removeChallenge, application]) if (!renderAppContents) { return renderChallenges() } return ( - +
    - +
    <>
    - - - + + + {renderChallenges()} <> - - - - + + + + - +
    diff --git a/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx b/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx index a945f5e89..8c3e17a85 100644 --- a/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx +++ b/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx @@ -1,5 +1,5 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import VisuallyHidden from '@reach/visually-hidden' @@ -19,13 +19,17 @@ import { isHandlingFileDrag } from '@/Utils/DragTypeCheck' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager onClickPreprocessing?: () => Promise } -const AttachedFilesButton: FunctionComponent = ({ application, appState, onClickPreprocessing }: Props) => { +const AttachedFilesButton: FunctionComponent = ({ + application, + viewControllerManager, + onClickPreprocessing, +}: Props) => { const premiumModal = usePremiumModal() - const note: SNNote | undefined = appState.notes.firstSelectedNote + const note: SNNote | undefined = viewControllerManager.notesController.firstSelectedNote const [open, setOpen] = useState(false) const [position, setPosition] = useState({ @@ -39,12 +43,12 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, const [closeOnBlur, keepMenuOpen] = useCloseOnBlur(containerRef, setOpen) useEffect(() => { - if (appState.filePreviewModal.isOpen) { + if (viewControllerManager.filePreviewModalController.isOpen) { keepMenuOpen(true) } else { keepMenuOpen(false) } - }, [appState.filePreviewModal.isOpen, keepMenuOpen]) + }, [viewControllerManager.filePreviewModalController.isOpen, keepMenuOpen]) const [currentTab, setCurrentTab] = useState(PopoverTabs.AttachedFiles) const [allFiles, setAllFiles] = useState([]) @@ -90,10 +94,10 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, }, [onClickPreprocessing, open]) const prospectivelyShowFilesPremiumModal = useCallback(() => { - if (!appState.features.hasFiles) { + if (!viewControllerManager.featuresController.hasFiles) { premiumModal.activate('Files') } - }, [appState.features.hasFiles, premiumModal]) + }, [viewControllerManager.featuresController.hasFiles, premiumModal]) const toggleAttachedFilesMenuWithEntitlementCheck = useCallback(async () => { prospectivelyShowFilesPremiumModal() @@ -121,7 +125,7 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, } const downloadFile = async (file: FileItem) => { - appState.files.downloadFile(file).catch(console.error) + viewControllerManager.filesController.downloadFile(file).catch(console.error) } const attachFileToNote = useCallback( @@ -213,7 +217,7 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, case PopoverFileItemActionType.PreviewFile: { keepMenuOpen(true) const otherFiles = currentTab === PopoverTabs.AllFiles ? allFiles : attachedFiles - appState.filePreviewModal.activate( + viewControllerManager.filePreviewModalController.activate( file, otherFiles.filter((file) => !file.protected), ) @@ -305,7 +309,7 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, setIsDraggingFiles(false) - if (!appState.features.hasFiles) { + if (!viewControllerManager.featuresController.hasFiles) { prospectivelyShowFilesPremiumModal() return } @@ -320,7 +324,7 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, return } - const uploadedFiles = await appState.files.uploadNewFile(fileOrHandle) + const uploadedFiles = await viewControllerManager.filesController.uploadNewFile(fileOrHandle) if (!uploadedFiles) { return @@ -338,8 +342,8 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, } }, [ - appState.files, - appState.features.hasFiles, + viewControllerManager.filesController, + viewControllerManager.featuresController.hasFiles, attachFileToNote, currentTab, application, @@ -396,7 +400,7 @@ const AttachedFilesButton: FunctionComponent = ({ application, appState, {open && ( void @@ -25,7 +25,7 @@ type Props = { const AttachedFilesPopover: FunctionComponent = ({ application, - appState, + viewControllerManager, allFiles, attachedFiles, closeOnBlur, @@ -45,7 +45,7 @@ const AttachedFilesPopover: FunctionComponent = ({ : filesList const handleAttachFilesClick = async () => { - const uploadedFiles = await appState.files.uploadNewFile() + const uploadedFiles = await viewControllerManager.filesController.uploadNewFile() if (!uploadedFiles) { return } diff --git a/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx b/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx index 9b305f863..7622a159a 100644 --- a/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx +++ b/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { DialogContent, DialogOverlay } from '@reach/dialog' import { ButtonType, @@ -14,13 +14,13 @@ import Button from '@/Components/Button/Button' import Icon from '@/Components/Icon/Icon' import ChallengeModalPrompt from './ChallengePrompt' import LockscreenWorkspaceSwitcher from './LockscreenWorkspaceSwitcher' -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' -import { AppState } from '@/UIModels/AppState' +import { ApplicationGroup } from '@/Application/ApplicationGroup' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { ChallengeModalValues } from './ChallengeModalValues' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager mainApplicationGroup: ApplicationGroup challenge: Challenge onDismiss?: (challenge: Challenge) => void @@ -44,7 +44,7 @@ const validateValues = (values: ChallengeModalValues, prompts: ChallengePrompt[] const ChallengeModal: FunctionComponent = ({ application, - appState, + viewControllerManager, mainApplicationGroup, challenge, onDismiss, @@ -255,7 +255,10 @@ const ChallengeModal: FunctionComponent = ({ )} {shouldShowWorkspaceSwitcher && ( - + )} diff --git a/app/assets/javascripts/Components/ChallengeModal/LockscreenWorkspaceSwitcher.tsx b/app/assets/javascripts/Components/ChallengeModal/LockscreenWorkspaceSwitcher.tsx index 6bd8b77ec..a534cf018 100644 --- a/app/assets/javascripts/Components/ChallengeModal/LockscreenWorkspaceSwitcher.tsx +++ b/app/assets/javascripts/Components/ChallengeModal/LockscreenWorkspaceSwitcher.tsx @@ -1,5 +1,5 @@ -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' -import { AppState } from '@/UIModels/AppState' +import { ApplicationGroup } from '@/Application/ApplicationGroup' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' import WorkspaceSwitcherMenu from '@/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu' @@ -9,10 +9,10 @@ import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside' type Props = { mainApplicationGroup: ApplicationGroup - appState: AppState + viewControllerManager: ViewControllerManager } -const LockscreenWorkspaceSwitcher: FunctionComponent = ({ mainApplicationGroup, appState }) => { +const LockscreenWorkspaceSwitcher: FunctionComponent = ({ mainApplicationGroup, viewControllerManager }) => { const buttonRef = useRef(null) const menuRef = useRef(null) const containerRef = useRef(null) @@ -55,7 +55,7 @@ const LockscreenWorkspaceSwitcher: FunctionComponent = ({ mainApplication
    diff --git a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx index c7e2411cb..f5edbec27 100644 --- a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx +++ b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx @@ -1,5 +1,5 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import VisuallyHidden from '@reach/visually-hidden' @@ -11,12 +11,16 @@ import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager onClickPreprocessing?: () => Promise } -const ChangeEditorButton: FunctionComponent = ({ application, appState, onClickPreprocessing }: Props) => { - const note = appState.notes.firstSelectedNote +const ChangeEditorButton: FunctionComponent = ({ + application, + viewControllerManager, + onClickPreprocessing, +}: Props) => { + const note = viewControllerManager.notesController.firstSelectedNote const [isOpen, setIsOpen] = useState(false) const [isVisible, setIsVisible] = useState(false) const [position, setPosition] = useState({ diff --git a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx index dae1095b3..491bba8cf 100644 --- a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx +++ b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx @@ -4,7 +4,7 @@ import MenuItem from '@/Components/Menu/MenuItem' import { MenuItemType } from '@/Components/Menu/MenuItemType' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { STRING_EDIT_LOCKED_ATTEMPT } from '@/Strings' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ComponentArea, ItemMutator, @@ -90,7 +90,7 @@ const ChangeEditorMenu: FunctionComponent = ({ const transactions: TransactionalMutation[] = [] - await application.getAppState().contentListView.insertCurrentIfTemplate() + await application.getViewControllerManager().contentListController.insertCurrentIfTemplate() if (note.locked) { application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error) diff --git a/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts b/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts index 0ad8f50cb..0c2e962e4 100644 --- a/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts +++ b/app/assets/javascripts/Components/ChangeEditor/createEditorMenuGroups.ts @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ContentType, FeatureStatus, diff --git a/app/assets/javascripts/Components/ComponentView/ComponentView.tsx b/app/assets/javascripts/Components/ComponentView/ComponentView.tsx index 33ff00cae..2471ed0f5 100644 --- a/app/assets/javascripts/Components/ComponentView/ComponentView.tsx +++ b/app/assets/javascripts/Components/ComponentView/ComponentView.tsx @@ -7,7 +7,7 @@ import { ComponentViewerEvent, ComponentViewerError, } from '@standardnotes/snjs' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' import { observer } from 'mobx-react-lite' import OfflineRestricted from '@/Components/ComponentView/OfflineRestricted' @@ -15,12 +15,12 @@ import UrlMissing from '@/Components/ComponentView/UrlMissing' import IsDeprecated from '@/Components/ComponentView/IsDeprecated' import IsExpired from '@/Components/ComponentView/IsExpired' import IssueOnLoading from '@/Components/ComponentView/IssueOnLoading' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { openSubscriptionDashboard } from '@/Utils/ManageSubscription' interface IProps { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager componentViewer: ComponentViewer requestReload?: (viewer: ComponentViewer, force?: boolean) => void onLoad?: (component: SNComponent) => void @@ -151,7 +151,7 @@ const ComponentView: FunctionComponent = ({ application, onLoad, compone application.io.handleComponentKeyUp(data.keyboardModifier) break case ComponentAction.Click: - application.getAppState().notes.setContextMenuOpen(false) + application.getViewControllerManager().notesController.setContextMenuOpen(false) break default: return diff --git a/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx b/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx index 98e99bc33..9a6c94b55 100644 --- a/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx +++ b/app/assets/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx @@ -1,31 +1,31 @@ import { FunctionComponent, useEffect, useRef, useState } from 'react' import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog' import { STRING_SIGN_OUT_CONFIRMATION } from '@/Strings' -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' -import { ApplicationGroup } from '@/UIModels/ApplicationGroup' +import { ApplicationGroup } from '@/Application/ApplicationGroup' import { isDesktopApplication } from '@/Utils' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager applicationGroup: ApplicationGroup } -const ConfirmSignoutModal: FunctionComponent = ({ application, appState, applicationGroup }) => { +const ConfirmSignoutModal: FunctionComponent = ({ application, viewControllerManager, applicationGroup }) => { const [deleteLocalBackups, setDeleteLocalBackups] = useState(false) const cancelRef = useRef(null) function closeDialog() { - appState.accountMenu.setSigningOut(false) + viewControllerManager.accountMenuController.setSigningOut(false) } const [localBackupsCount, setLocalBackupsCount] = useState(0) useEffect(() => { application.desktopDevice?.localBackupsCount().then(setLocalBackupsCount).catch(console.error) - }, [appState.accountMenu.signingOut, application.desktopDevice]) + }, [viewControllerManager.accountMenuController.signingOut, application.desktopDevice]) const workspaces = applicationGroup.getDescriptors() const showWorkspaceWarning = workspaces.length > 1 && isDesktopApplication() @@ -112,7 +112,7 @@ const ConfirmSignoutModal: FunctionComponent = ({ application, appState, ConfirmSignoutModal.displayName = 'ConfirmSignoutModal' const ConfirmSignoutContainer = (props: Props) => { - if (!props.appState.accountMenu.signingOut) { + if (!props.viewControllerManager.accountMenuController.signingOut) { return null } return diff --git a/app/assets/javascripts/Components/ContentListView/ContentList.tsx b/app/assets/javascripts/Components/ContentListView/ContentList.tsx index e084539bd..dac21cb3b 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentList.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentList.tsx @@ -1,6 +1,6 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { KeyboardKey } from '@/Services/IOService' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { UuidString } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { FunctionComponent, KeyboardEventHandler, UIEventHandler, useCallback } from 'react' @@ -10,16 +10,23 @@ import ContentListItem from './ContentListItem' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager items: ListableContentItem[] selectedItems: Record paginate: () => void } -const ContentList: FunctionComponent = ({ application, appState, items, selectedItems, paginate }) => { - const { selectPreviousItem, selectNextItem } = appState.contentListView - const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = appState.contentListView.webDisplayOptions - const { sortBy } = appState.contentListView.displayOptions +const ContentList: FunctionComponent = ({ + application, + viewControllerManager, + items, + selectedItems, + paginate, +}) => { + const { selectPreviousItem, selectNextItem } = viewControllerManager.contentListController + const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = + viewControllerManager.contentListController.webDisplayOptions + const { sortBy } = viewControllerManager.contentListController.displayOptions const onScroll: UIEventHandler = useCallback( (e) => { @@ -57,7 +64,7 @@ const ContentList: FunctionComponent = ({ application, appState, items, s = (props) => { return [] } - const selectedTag = props.appState.tags.selected + const selectedTag = props.viewControllerManager.navigationController.selected if (!selectedTag) { return [] } - const tags = props.appState.getItemTags(props.item) + const tags = props.viewControllerManager.getItemTags(props.item) const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1 if (isNavigatingOnlyTag) { diff --git a/app/assets/javascripts/Components/ContentListView/ContentListOptionsMenu.tsx b/app/assets/javascripts/Components/ContentListView/ContentListOptionsMenu.tsx index 0e21af840..ce6243c9b 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentListOptionsMenu.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentListOptionsMenu.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { CollectionSort, CollectionSortProperty, PrefKey, SystemViewId } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { FunctionComponent, useCallback, useState } from 'react' @@ -7,11 +7,11 @@ import Menu from '@/Components/Menu/Menu' import MenuItem from '@/Components/Menu/MenuItem' import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator' import { MenuItemType } from '@/Components/Menu/MenuItemType' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void closeDisplayOptionsMenu: () => void isOpen: boolean @@ -21,7 +21,7 @@ const ContentListOptionsMenu: FunctionComponent = ({ closeDisplayOptionsMenu, closeOnBlur, application, - appState, + viewControllerManager, isOpen, }) => { const [sortBy, setSortBy] = useState(() => application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)) @@ -174,7 +174,7 @@ const ContentListOptionsMenu: FunctionComponent = ({
    View
    - {appState.tags.selectedUuid !== SystemViewId.Files && ( + {viewControllerManager.navigationController.selectedUuid !== SystemViewId.Files && ( = ({ application, appState }) => { +const ContentListView: FunctionComponent = ({ application, viewControllerManager }) => { const itemsViewPanelRef = useRef(null) const displayOptionsMenuRef = useRef(null) @@ -46,9 +46,9 @@ const ContentListView: FunctionComponent = ({ application, appState }) => paginate, panelWidth, createNewNote, - } = appState.contentListView + } = viewControllerManager.contentListController - const { selectedItems } = appState.selectedItems + const { selectedItems } = viewControllerManager.selectionController const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false) const [focusedSearch, setFocusedSearch] = useState(false) @@ -56,17 +56,17 @@ const ContentListView: FunctionComponent = ({ application, appState }) => const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(displayOptionsMenuRef, setShowDisplayOptionsMenu) const isFilesSmartView = useMemo( - () => appState.tags.selected?.uuid === SystemViewId.Files, - [appState.tags.selected?.uuid], + () => viewControllerManager.navigationController.selected?.uuid === SystemViewId.Files, + [viewControllerManager.navigationController.selected?.uuid], ) const addNewItem = useCallback(() => { if (isFilesSmartView) { - void appState.files.uploadNewFile() + void viewControllerManager.filesController.uploadNewFile() } else { void createNewNote() } - }, [appState.files, createNewNote, isFilesSmartView]) + }, [viewControllerManager.filesController, createNewNote, isFilesSmartView]) useEffect(() => { /** @@ -142,15 +142,15 @@ const ContentListView: FunctionComponent = ({ application, appState }) => const panelResizeFinishCallback: ResizeFinishCallback = useCallback( (width, _lastLeft, _isMaxWidth, isCollapsed) => { application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error) - appState.noteTags.reloadTagsContainerMaxWidth() - appState.panelDidResize(PANEL_NAME_NOTES, isCollapsed) + viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth() + viewControllerManager.panelDidResize(PANEL_NAME_NOTES, isCollapsed) }, - [appState, application], + [viewControllerManager, application], ) const panelWidthEventCallback = useCallback(() => { - appState.noteTags.reloadTagsContainerMaxWidth() - }, [appState]) + viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth() + }, [viewControllerManager]) const toggleDisplayOptionsMenu = useCallback(() => { setShowDisplayOptionsMenu(!showDisplayOptionsMenu) @@ -208,11 +208,11 @@ const ContentListView: FunctionComponent = ({ application, appState }) => {(focusedSearch || noteFilterText) && (
    - +
    )}
    - +
    @@ -235,7 +235,7 @@ const ContentListView: FunctionComponent = ({ application, appState }) => {showDisplayOptionsMenu && ( = ({ application, appState }) => items={renderedItems} selectedItems={selectedItems} application={application} - appState={appState} + viewControllerManager={viewControllerManager} paginate={paginate} /> ) : null} diff --git a/app/assets/javascripts/Components/ContentListView/FileListItem.tsx b/app/assets/javascripts/Components/ContentListView/FileListItem.tsx index e8358298f..b1c32c664 100644 --- a/app/assets/javascripts/Components/ContentListView/FileListItem.tsx +++ b/app/assets/javascripts/Components/ContentListView/FileListItem.tsx @@ -10,7 +10,7 @@ import { DisplayableListItemProps } from './Types/DisplayableListItemProps' const FileListItem: FunctionComponent = ({ application, - appState, + viewControllerManager, hideDate, hideIcon, hideTags, @@ -21,32 +21,40 @@ const FileListItem: FunctionComponent = ({ }) => { const openFileContextMenu = useCallback( (posX: number, posY: number) => { - appState.files.setFileContextMenuLocation({ + viewControllerManager.filesController.setFileContextMenuLocation({ x: posX, y: posY, }) - appState.files.setShowFileContextMenu(true) + viewControllerManager.filesController.setShowFileContextMenu(true) }, - [appState.files], + [viewControllerManager.filesController], ) const openContextMenu = useCallback( async (posX: number, posY: number) => { - const { didSelect } = await appState.selectedItems.selectItem(item.uuid) + const { didSelect } = await viewControllerManager.selectionController.selectItem(item.uuid) if (didSelect) { openFileContextMenu(posX, posY) } }, - [appState.selectedItems, item.uuid, openFileContextMenu], + [viewControllerManager.selectionController, item.uuid, openFileContextMenu], ) const onClick = useCallback(() => { - void appState.selectedItems.selectItem(item.uuid, true).then(({ didSelect }) => { - if (didSelect && appState.selectedItems.selectedItemsCount < 2) { - appState.filePreviewModal.activate(item as FileItem, appState.files.allFiles) + void viewControllerManager.selectionController.selectItem(item.uuid, true).then(({ didSelect }) => { + if (didSelect && viewControllerManager.selectionController.selectedItemsCount < 2) { + viewControllerManager.filePreviewModalController.activate( + item as FileItem, + viewControllerManager.filesController.allFiles, + ) } }) - }, [appState.filePreviewModal, appState.files.allFiles, appState.selectedItems, item]) + }, [ + viewControllerManager.filePreviewModalController, + viewControllerManager.filesController.allFiles, + viewControllerManager.selectionController, + item, + ]) const IconComponent = () => getFileIconComponent( diff --git a/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx b/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx index b6fa0e960..d1575459f 100644 --- a/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx +++ b/app/assets/javascripts/Components/ContentListView/NoteListItem.tsx @@ -11,7 +11,7 @@ import { DisplayableListItemProps } from './Types/DisplayableListItemProps' const NoteListItem: FunctionComponent = ({ application, - appState, + viewControllerManager, hideDate, hideIcon, hideTags, @@ -27,16 +27,16 @@ const NoteListItem: FunctionComponent = ({ const hasFiles = application.items.getFilesForNote(item as SNNote).length > 0 const openNoteContextMenu = (posX: number, posY: number) => { - appState.notes.setContextMenuClickLocation({ + viewControllerManager.notesController.setContextMenuClickLocation({ x: posX, y: posY, }) - appState.notes.reloadContextMenuLayout() - appState.notes.setContextMenuOpen(true) + viewControllerManager.notesController.reloadContextMenuLayout() + viewControllerManager.notesController.setContextMenuOpen(true) } const openContextMenu = async (posX: number, posY: number) => { - const { didSelect } = await appState.selectedItems.selectItem(item.uuid, true) + const { didSelect } = await viewControllerManager.selectionController.selectItem(item.uuid, true) if (didSelect) { openNoteContextMenu(posX, posY) } @@ -49,7 +49,7 @@ const NoteListItem: FunctionComponent = ({ }`} id={item.uuid} onClick={() => { - void appState.selectedItems.selectItem(item.uuid, true) + void viewControllerManager.selectionController.selectItem(item.uuid, true) }} onContextMenu={(event) => { event.preventDefault() diff --git a/app/assets/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts b/app/assets/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts index 64abf5983..8b1b2ded4 100644 --- a/app/assets/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts +++ b/app/assets/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts @@ -1,11 +1,11 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { SortableItem } from '@standardnotes/snjs' import { ListableContentItem } from './ListableContentItem' export type AbstractListItemProps = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager hideDate: boolean hideIcon: boolean hideTags: boolean diff --git a/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx b/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx index 770c4cc12..bdc99e4e5 100644 --- a/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx +++ b/app/assets/javascripts/Components/DeallocateHandler/DeallocateHandler.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'react' diff --git a/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx b/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx index 1243073b0..a0c1dbdad 100644 --- a/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx +++ b/app/assets/javascripts/Components/FileContextMenu/FileContextMenu.tsx @@ -1,7 +1,7 @@ import { MAX_MENU_SIZE_MULTIPLIER, MENU_MARGIN_FROM_APP_BORDER } from '@/Constants' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' import { PopoverFileItemAction } from '../AttachedFilesPopover/PopoverFileItemAction' @@ -9,11 +9,12 @@ import { PopoverTabs } from '../AttachedFilesPopover/PopoverTabs' import FileMenuOptions from './FileMenuOptions' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager } -const FileContextMenu: FunctionComponent = observer(({ appState }) => { - const { selectedFiles, showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = appState.files +const FileContextMenu: FunctionComponent = observer(({ viewControllerManager }) => { + const { selectedFiles, showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = + viewControllerManager.filesController const [contextMenuStyle, setContextMenuStyle] = useState({ top: 0, @@ -23,7 +24,7 @@ const FileContextMenu: FunctionComponent = observer(({ appState }) => { const [contextMenuMaxHeight, setContextMenuMaxHeight] = useState('auto') const contextMenuRef = useRef(null) const [closeOnBlur] = useCloseOnBlur(contextMenuRef, (open: boolean) => setShowFileContextMenu(open)) - useCloseOnClickOutside(contextMenuRef, () => appState.files.setShowFileContextMenu(false)) + useCloseOnClickOutside(contextMenuRef, () => viewControllerManager.filesController.setShowFileContextMenu(false)) const selectedFile = selectedFiles[0] @@ -87,10 +88,13 @@ const FileContextMenu: FunctionComponent = observer(({ appState }) => { const handleFileAction = useCallback( async (action: PopoverFileItemAction) => { - const { didHandleAction } = await appState.files.handleFileAction(action, PopoverTabs.AllFiles) + const { didHandleAction } = await viewControllerManager.filesController.handleFileAction( + action, + PopoverTabs.AllFiles, + ) return didHandleAction }, - [appState.files], + [viewControllerManager.filesController], ) return ( @@ -116,8 +120,8 @@ const FileContextMenu: FunctionComponent = observer(({ appState }) => { FileContextMenu.displayName = 'FileContextMenu' -const FileContextMenuWrapper: FunctionComponent = ({ appState }) => { - const { selectedFiles, showFileContextMenu } = appState.files +const FileContextMenuWrapper: FunctionComponent = ({ viewControllerManager }) => { + const { selectedFiles, showFileContextMenu } = viewControllerManager.filesController const selectedFile = selectedFiles[0] @@ -125,7 +129,7 @@ const FileContextMenuWrapper: FunctionComponent = ({ appState }) => { return null } - return + return } export default observer(FileContextMenuWrapper) diff --git a/app/assets/javascripts/Components/Files/FilePreviewModal.tsx b/app/assets/javascripts/Components/Files/FilePreviewModal.tsx index 503f9d76d..348926298 100644 --- a/app/assets/javascripts/Components/Files/FilePreviewModal.tsx +++ b/app/assets/javascripts/Components/Files/FilePreviewModal.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { concatenateUint8Arrays } from '@/Utils/ConcatenateUint8Arrays' import { DialogContent, DialogOverlay } from '@reach/dialog' import { addToast, ToastType } from '@standardnotes/stylekit' @@ -12,16 +12,16 @@ import { isFileTypePreviewable } from './isFilePreviewable' import PreviewComponent from './PreviewComponent' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' import { KeyboardKey } from '@/Services/IOService' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager } -const FilePreviewModal: FunctionComponent = observer(({ application, appState }) => { - const { currentFile, setCurrentFile, otherFiles, dismiss } = appState.filePreviewModal +const FilePreviewModal: FunctionComponent = observer(({ application, viewControllerManager }) => { + const { currentFile, setCurrentFile, otherFiles, dismiss } = viewControllerManager.filePreviewModalController if (!currentFile) { return null @@ -221,7 +221,10 @@ const FilePreviewModal: FunctionComponent = observer(({ application, appS
    {ref && ( diff --git a/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx b/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx index 2792fc803..1dbf56cfb 100644 --- a/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx +++ b/app/assets/javascripts/Components/NoAccountWarning/NoAccountWarning.tsx @@ -1,22 +1,22 @@ import Icon from '@/Components/Icon/Icon' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { MouseEventHandler, useCallback } from 'react' -type Props = { appState: AppState } +type Props = { viewControllerManager: ViewControllerManager } -const NoAccountWarning = observer(({ appState }: Props) => { +const NoAccountWarning = observer(({ viewControllerManager }: Props) => { const showAccountMenu: MouseEventHandler = useCallback( (event) => { event.stopPropagation() - appState.accountMenu.setShow(true) + viewControllerManager.accountMenuController.setShow(true) }, - [appState], + [viewControllerManager], ) const hideWarning = useCallback(() => { - appState.noAccountWarning.hide() - }, [appState]) + viewControllerManager.noAccountWarningController.hide() + }, [viewControllerManager]) return (
    @@ -40,10 +40,10 @@ const NoAccountWarning = observer(({ appState }: Props) => { NoAccountWarning.displayName = 'NoAccountWarning' -const NoAccountWarningWrapper = ({ appState }: Props) => { - const canShow = appState.noAccountWarning.show +const NoAccountWarningWrapper = ({ viewControllerManager }: Props) => { + const canShow = viewControllerManager.noAccountWarningController.show - return canShow ? : null + return canShow ? : null } export default observer(NoAccountWarningWrapper) diff --git a/app/assets/javascripts/Components/NoteGroupView/NoteGroupView.tsx b/app/assets/javascripts/Components/NoteGroupView/NoteGroupView.tsx index fc1c982b6..a4082e660 100644 --- a/app/assets/javascripts/Components/NoteGroupView/NoteGroupView.tsx +++ b/app/assets/javascripts/Components/NoteGroupView/NoteGroupView.tsx @@ -1,6 +1,6 @@ import { NoteViewController } from '@standardnotes/snjs' import { PureComponent } from '@/Components/Abstract/PureComponent' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import MultipleSelectedNotes from '@/Components/MultipleSelectedNotes/MultipleSelectedNotes' import NoteView from '@/Components/NoteView/NoteView' import { ElementIds } from '@/ElementIDs' @@ -37,9 +37,9 @@ class NoteGroupView extends PureComponent { }) this.autorun(() => { - if (this.appState && this.appState.notes) { + if (this.viewControllerManager && this.viewControllerManager.notesController) { this.setState({ - showMultipleSelectedNotes: this.appState.notes.selectedNotesCount > 1, + showMultipleSelectedNotes: this.viewControllerManager.notesController.selectedNotesCount > 1, }) } }) @@ -56,7 +56,7 @@ class NoteGroupView extends PureComponent { return (
    {this.state.showMultipleSelectedNotes && ( - + )} {!this.state.showMultipleSelectedNotes && ( diff --git a/app/assets/javascripts/Components/NoteTags/NoteTag.tsx b/app/assets/javascripts/Components/NoteTags/NoteTag.tsx index f6f94467c..54844a37e 100644 --- a/app/assets/javascripts/Components/NoteTags/NoteTag.tsx +++ b/app/assets/javascripts/Components/NoteTags/NoteTag.tsx @@ -8,17 +8,17 @@ import { useRef, useState, } from 'react' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { SNTag } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager tag: SNTag } -const NoteTag = ({ appState, tag }: Props) => { - const noteTags = appState.noteTags +const NoteTag = ({ viewControllerManager, tag }: Props) => { + const noteTags = viewControllerManager.noteTagsController const { autocompleteInputFocused, focusedTagUuid, tags } = noteTags @@ -33,9 +33,9 @@ const NoteTag = ({ appState, tag }: Props) => { const longTitle = noteTags.getLongTitle(tag) const deleteTag = useCallback(() => { - appState.noteTags.focusPreviousTag(tag) - appState.noteTags.removeTagFromActiveNote(tag).catch(console.error) - }, [appState, tag]) + viewControllerManager.noteTagsController.focusPreviousTag(tag) + viewControllerManager.noteTagsController.removeTagFromActiveNote(tag).catch(console.error) + }, [viewControllerManager, tag]) const onDeleteTagClick: MouseEventHandler = useCallback( (event) => { @@ -49,28 +49,28 @@ const NoteTag = ({ appState, tag }: Props) => { (event) => { if (tagClicked && event.target !== deleteTagRef.current) { setTagClicked(false) - void appState.tags.setSelectedTag(tag) + void viewControllerManager.navigationController.setSelectedTag(tag) } else { setTagClicked(true) } }, - [appState, tagClicked, tag], + [viewControllerManager, tagClicked, tag], ) const onFocus = useCallback(() => { - appState.noteTags.setFocusedTagUuid(tag.uuid) + viewControllerManager.noteTagsController.setFocusedTagUuid(tag.uuid) setShowDeleteButton(true) - }, [appState, tag]) + }, [viewControllerManager, tag]) const onBlur: FocusEventHandler = useCallback( (event) => { const relatedTarget = event.relatedTarget as Node if (relatedTarget !== deleteTagRef.current) { - appState.noteTags.setFocusedTagUuid(undefined) + viewControllerManager.noteTagsController.setFocusedTagUuid(undefined) setShowDeleteButton(false) } }, - [appState], + [viewControllerManager], ) const getTabIndex = useCallback(() => { @@ -85,33 +85,33 @@ const NoteTag = ({ appState, tag }: Props) => { const onKeyDown: KeyboardEventHandler = useCallback( (event) => { - const tagIndex = appState.noteTags.getTagIndex(tag, tags) + const tagIndex = viewControllerManager.noteTagsController.getTagIndex(tag, tags) switch (event.key) { case 'Backspace': deleteTag() break case 'ArrowLeft': - appState.noteTags.focusPreviousTag(tag) + viewControllerManager.noteTagsController.focusPreviousTag(tag) break case 'ArrowRight': if (tagIndex === tags.length - 1) { - appState.noteTags.setAutocompleteInputFocused(true) + viewControllerManager.noteTagsController.setAutocompleteInputFocused(true) } else { - appState.noteTags.focusNextTag(tag) + viewControllerManager.noteTagsController.focusNextTag(tag) } break default: return } }, - [appState, deleteTag, tag, tags], + [viewControllerManager, deleteTag, tag, tags], ) useEffect(() => { if (focusedTagUuid === tag.uuid) { tagRef.current?.focus() } - }, [appState, focusedTagUuid, tag]) + }, [viewControllerManager, focusedTagUuid, tag]) return (
    - +
    )} @@ -993,7 +993,7 @@ class NoteView extends PureComponent { onLoad={this.onEditorComponentLoad} requestReload={this.editorComponentViewerRequestsReload} application={this.application} - appState={this.appState} + viewControllerManager={this.viewControllerManager} />
    )} @@ -1068,7 +1068,7 @@ class NoteView extends PureComponent { key={viewer.identifier} componentViewer={viewer} application={this.application} - appState={this.appState} + viewControllerManager={this.viewControllerManager} />
    ) diff --git a/app/assets/javascripts/Components/NoteView/NoteViewProps.ts b/app/assets/javascripts/Components/NoteView/NoteViewProps.ts index e5041db0d..114ad3d0c 100644 --- a/app/assets/javascripts/Components/NoteView/NoteViewProps.ts +++ b/app/assets/javascripts/Components/NoteView/NoteViewProps.ts @@ -1,6 +1,6 @@ import { NoteViewController } from '@standardnotes/snjs' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' export interface NoteViewProps { application: WebApplication diff --git a/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx b/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx index 33f0a1be6..edb2f4df7 100644 --- a/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx +++ b/app/assets/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx @@ -1,27 +1,29 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside' import { observer } from 'mobx-react-lite' import NotesOptions from '@/Components/NotesOptions/NotesOptions' import { useCallback, useEffect, useRef } from 'react' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager } -const NotesContextMenu = ({ application, appState }: Props) => { - const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = appState.notes +const NotesContextMenu = ({ application, viewControllerManager }: Props) => { + const { contextMenuOpen, contextMenuPosition, contextMenuMaxHeight } = viewControllerManager.notesController const contextMenuRef = useRef(null) - const [closeOnBlur] = useCloseOnBlur(contextMenuRef, (open: boolean) => appState.notes.setContextMenuOpen(open)) + const [closeOnBlur] = useCloseOnBlur(contextMenuRef, (open: boolean) => + viewControllerManager.notesController.setContextMenuOpen(open), + ) - useCloseOnClickOutside(contextMenuRef, () => appState.notes.setContextMenuOpen(false)) + useCloseOnClickOutside(contextMenuRef, () => viewControllerManager.notesController.setContextMenuOpen(false)) const reloadContextMenuLayout = useCallback(() => { - appState.notes.reloadContextMenuLayout() - }, [appState]) + viewControllerManager.notesController.reloadContextMenuLayout() + }, [viewControllerManager]) useEffect(() => { window.addEventListener('resize', reloadContextMenuLayout) @@ -39,7 +41,7 @@ const NotesContextMenu = ({ application, appState }: Props) => { maxHeight: contextMenuMaxHeight, }} > - +
    ) : null } diff --git a/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx b/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx index 1eb63d3f7..764e46c20 100644 --- a/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx +++ b/app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx @@ -1,4 +1,4 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { observer } from 'mobx-react-lite' @@ -7,10 +7,10 @@ import Icon from '@/Components/Icon/Icon' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager } -const AddTagOption: FunctionComponent = ({ appState }) => { +const AddTagOption: FunctionComponent = ({ viewControllerManager }) => { const menuContainerRef = useRef(null) const menuRef = useRef(null) const menuButtonRef = useRef(null) @@ -84,22 +84,22 @@ const AddTagOption: FunctionComponent = ({ appState }) => { }} className="sn-dropdown min-w-80 flex flex-col py-2 max-h-120 max-w-xs fixed overflow-y-auto" > - {appState.tags.tags.map((tag) => ( + {viewControllerManager.navigationController.tags.map((tag) => ( ))} diff --git a/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx b/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx index 4d6ab456e..3cfcd5c65 100644 --- a/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx +++ b/app/assets/javascripts/Components/NotesOptions/ChangeEditorOption.tsx @@ -1,6 +1,6 @@ import { KeyboardKey } from '@/Services/IOService' -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { SNNote } from '@standardnotes/snjs' import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react' @@ -10,7 +10,7 @@ import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuSty import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' type ChangeEditorOptionProps = { - appState: AppState + viewControllerManager: ViewControllerManager application: WebApplication note: SNNote } diff --git a/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx b/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx index 985bebb3c..c21c628b1 100644 --- a/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx +++ b/app/assets/javascripts/Components/NotesOptions/ListedActionsOption.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { Action, ListedAccount, SNNote } from '@standardnotes/snjs' diff --git a/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx b/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx index 0fbb9af06..46cd4b00f 100644 --- a/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx +++ b/app/assets/javascripts/Components/NotesOptions/NotesOptions.tsx @@ -1,4 +1,4 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import Icon from '@/Components/Icon/Icon' import Switch from '@/Components/Switch/Switch' import { observer } from 'mobx-react-lite' @@ -121,15 +121,15 @@ const NoteAttributes: FunctionComponent<{ } const SpellcheckOptions: FunctionComponent<{ - appState: AppState + viewControllerManager: ViewControllerManager note: SNNote -}> = ({ appState, note }) => { - const editor = appState.application.componentManager.editorForNote(note) +}> = ({ viewControllerManager, note }) => { + const editor = viewControllerManager.application.componentManager.editorForNote(note) const spellcheckControllable = Boolean(!editor || editor.package_info.spellcheckControl) const noteSpellcheck = !spellcheckControllable ? true : note - ? appState.notes.getSpellcheckStateForNote(note) + ? viewControllerManager.notesController.getSpellcheckStateForNote(note) : undefined return ( @@ -137,7 +137,7 @@ const SpellcheckOptions: FunctionComponent<{ @@ -425,7 +427,7 @@ const NotesOptions = ({ application, appState, closeOnBlur }: NotesOptionsProps)
    - +
    diff --git a/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx b/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx index 23b0a50bc..09874408f 100644 --- a/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx +++ b/app/assets/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx @@ -1,4 +1,4 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import Icon from '@/Components/Icon/Icon' import VisuallyHidden from '@reach/visually-hidden' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' @@ -6,16 +6,16 @@ import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure import { useRef, useState } from 'react' import { observer } from 'mobx-react-lite' import NotesOptions from './NotesOptions' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager onClickPreprocessing?: () => Promise } -const NotesOptionsPanel = ({ application, appState, onClickPreprocessing }: Props) => { +const NotesOptionsPanel = ({ application, viewControllerManager, onClickPreprocessing }: Props) => { const [open, setOpen] = useState(false) const [position, setPosition] = useState({ top: 0, @@ -79,7 +79,13 @@ const NotesOptionsPanel = ({ application, appState, onClickPreprocessing }: Prop onBlur={closeOnBlur} tabIndex={FOCUSABLE_BUT_NOT_TABBABLE} > - {open && } + {open && ( + + )} ) diff --git a/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts b/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts index 2a969165c..6f10ede6a 100644 --- a/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts +++ b/app/assets/javascripts/Components/NotesOptions/NotesOptionsProps.ts @@ -1,8 +1,8 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' export type NotesOptionsProps = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void } diff --git a/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx b/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx index c10ce1f16..8cb39fae3 100644 --- a/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx +++ b/app/assets/javascripts/Components/OtherSessionsSignOut/OtherSessionsSignOut.tsx @@ -1,20 +1,20 @@ import { useCallback, useRef } from 'react' import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog' -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager } -const ConfirmOtherSessionsSignOut = observer(({ application, appState }: Props) => { +const ConfirmOtherSessionsSignOut = observer(({ application, viewControllerManager }: Props) => { const cancelRef = useRef(null) const closeDialog = useCallback(() => { - appState.accountMenu.setOtherSessionsSignOut(false) - }, [appState]) + viewControllerManager.accountMenuController.setOtherSessionsSignOut(false) + }, [viewControllerManager]) return ( @@ -62,7 +62,7 @@ const ConfirmOtherSessionsSignOut = observer(({ application, appState }: Props) ConfirmOtherSessionsSignOut.displayName = 'ConfirmOtherSessionsSignOut' const OtherSessionsSignOutContainer = (props: Props) => { - if (!props.appState.accountMenu.otherSessionsSignOut) { + if (!props.viewControllerManager.accountMenuController.otherSessionsSignOut) { return null } return diff --git a/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx b/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx index 2cb0d1180..b44e5d254 100644 --- a/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx +++ b/app/assets/javascripts/Components/PasswordWizard/PasswordWizard.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ChangeEventHandler, createRef } from 'react' import { PureComponent } from '@/Components/Abstract/PureComponent' diff --git a/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx b/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx index 0682c8960..9df4bc06e 100644 --- a/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx +++ b/app/assets/javascripts/Components/PermissionsModal/PermissionsModal.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { SNComponent } from '@standardnotes/snjs' import { Component } from 'react' diff --git a/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx b/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx index 308074fff..462af2d47 100644 --- a/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx +++ b/app/assets/javascripts/Components/PermissionsModal/PermissionsModalWrapper.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ApplicationEvent, PermissionDialog } from '@standardnotes/snjs' import { FunctionComponent, useCallback, useEffect, useState } from 'react' import PermissionsModal from './PermissionsModal' diff --git a/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx b/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx index 6738391a5..8ec22bd1e 100644 --- a/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx +++ b/app/assets/javascripts/Components/PinNoteButton/PinNoteButton.tsx @@ -1,17 +1,21 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import VisuallyHidden from '@reach/visually-hidden' import { observer } from 'mobx-react-lite' import { FunctionComponent, useCallback } from 'react' import Icon from '@/Components/Icon/Icon' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager className?: string onClickPreprocessing?: () => Promise } -const PinNoteButton: FunctionComponent = ({ appState, className = '', onClickPreprocessing }: Props) => { - const notes = appState.notes.selectedNotes +const PinNoteButton: FunctionComponent = ({ + viewControllerManager, + className = '', + onClickPreprocessing, +}: Props) => { + const notes = viewControllerManager.notesController.selectedNotes const pinned = notes.some((note) => note.pinned) const togglePinned = useCallback(async () => { @@ -19,11 +23,11 @@ const PinNoteButton: FunctionComponent = ({ appState, className = '', onC await onClickPreprocessing() } if (!pinned) { - appState.notes.setPinSelectedNotes(true) + viewControllerManager.notesController.setPinSelectedNotes(true) } else { - appState.notes.setPinSelectedNotes(false) + viewControllerManager.notesController.setPinSelectedNotes(false) } - }, [appState, onClickPreprocessing, pinned]) + }, [viewControllerManager, onClickPreprocessing, pinned]) return (
    @@ -45,23 +49,23 @@ const SignOutView: FunctionComponent = observer(({ application, appState dangerStyle={true} label="Sign out workspace" onClick={() => { - appState.accountMenu.setSigningOut(true) + viewControllerManager.accountMenuController.setSigningOut(true) }} /> - + ) }) SignOutView.displayName = 'SignOutView' -const SignOutWrapper: FunctionComponent = ({ application, appState }) => { +const SignOutWrapper: FunctionComponent = ({ application, viewControllerManager }) => { if (!application.hasAccount()) { - return + return } - return + return } export default observer(SignOutWrapper) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/NoSubscription.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/NoSubscription.tsx index 45440773a..dd86e8ec4 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/NoSubscription.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/NoSubscription.tsx @@ -1,7 +1,7 @@ import { FunctionComponent, useState } from 'react' import { LinkButton, Text } from '@/Components/Preferences/PreferencesComponents/Content' import Button from '@/Components/Button/Button' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { loadPurchaseFlowUrl } from '@/Components/PurchaseFlow/PurchaseFlowFunctions' type Props = { diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/StatusText.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/StatusText.tsx index 38825fca8..19ccf4648 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/StatusText.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/StatusText.tsx @@ -1,8 +1,8 @@ -import { SubscriptionState } from '@/UIModels/AppState/SubscriptionState' +import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController' import { observer } from 'mobx-react-lite' import { Text } from '@/Components/Preferences/PreferencesComponents/Content' -type Props = { subscriptionState: SubscriptionState } +type Props = { subscriptionState: SubscriptionController } const StatusText = ({ subscriptionState }: Props) => { const { diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/Subscription.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/Subscription.tsx index 38cb941ca..d7e025ddb 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/Subscription.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/Subscription.tsx @@ -1,20 +1,20 @@ import { Title } from '@/Components/Preferences/PreferencesComponents/Content' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import SubscriptionInformation from './SubscriptionInformation' import NoSubscription from './NoSubscription' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'react' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup' import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager } -const Subscription: FunctionComponent = ({ application, appState }: Props) => { - const subscriptionState = appState.subscription +const Subscription: FunctionComponent = ({ application, viewControllerManager }: Props) => { + const subscriptionState = viewControllerManager.subscriptionController const { userSubscription } = subscriptionState const now = new Date().getTime() diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/SubscriptionInformation.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/SubscriptionInformation.tsx index 7344ab22d..bef7da4e6 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/SubscriptionInformation.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/SubscriptionInformation.tsx @@ -1,12 +1,12 @@ import { observer } from 'mobx-react-lite' -import { SubscriptionState } from '@/UIModels/AppState/SubscriptionState' +import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController' import Button from '@/Components/Button/Button' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { openSubscriptionDashboard } from '@/Utils/ManageSubscription' import StatusText from './StatusText' type Props = { - subscriptionState: SubscriptionState + subscriptionState: SubscriptionController application: WebApplication } diff --git a/app/assets/javascripts/Components/Preferences/Panes/Account/Sync.tsx b/app/assets/javascripts/Components/Preferences/Panes/Account/Sync.tsx index cb3a1977f..a8145d89a 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Account/Sync.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Account/Sync.tsx @@ -3,7 +3,7 @@ import Button from '@/Components/Button/Button' import { SyncQueueStrategy } from '@standardnotes/snjs' import { STRING_GENERIC_SYNC_ERROR } from '@/Strings' import { observer } from 'mobx-react-lite' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FunctionComponent, useState } from 'react' import { formatLastSyncDate } from '@/Utils/FormatLastSyncDate' import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Appearance.tsx b/app/assets/javascripts/Components/Preferences/Panes/Appearance.tsx index ea60a5dc3..1b4037f67 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Appearance.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Appearance.tsx @@ -3,7 +3,7 @@ import { DropdownItem } from '@/Components/Dropdown/DropdownItem' import { usePremiumModal } from '@/Hooks/usePremiumModal' import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import Switch from '@/Components/Switch/Switch' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ContentType, FeatureIdentifier, FeatureStatus, PrefKey, GetFeatures, SNTheme } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { FunctionComponent, useEffect, useState } from 'react' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx index 0671fa0b5..96faa910c 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Backups.tsx @@ -1,5 +1,5 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { FunctionComponent } from 'react' import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane' import CloudLink from './CloudBackups/CloudBackups' @@ -9,14 +9,14 @@ import FileBackupsCrossPlatform from './Files/FileBackupsCrossPlatform' import { observer } from 'mobx-react-lite' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager application: WebApplication } -const Backups: FunctionComponent = ({ application, appState }) => { +const Backups: FunctionComponent = ({ application, viewControllerManager }) => { return ( - + diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx index b7d99e38d..b79bcb2f7 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackupProvider.tsx @@ -15,7 +15,7 @@ import { GoogleDriveBackupFrequency, OneDriveBackupFrequency, } from '@standardnotes/snjs' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import Button from '@/Components/Button/Button' import { isDev, openInNewTab } from '@/Utils' import { Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx index f7794647e..5dedd7c02 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/CloudBackups/CloudBackups.tsx @@ -1,6 +1,6 @@ import CloudBackupProvider from './CloudBackupProvider' import { useCallback, useEffect, useState, FunctionComponent, Fragment } from 'react' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import { diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx index 1067dafe9..c76522731 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/DataBackups.tsx @@ -12,8 +12,8 @@ import { } from '@/Strings' import { BackupFile } from '@standardnotes/snjs' import { ChangeEventHandler, MouseEventHandler, useCallback, useEffect, useRef, useState } from 'react' -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' import Button from '@/Components/Button/Button' @@ -23,10 +23,10 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager } -const DataBackups = ({ application, appState }: Props) => { +const DataBackups = ({ application, viewControllerManager }: Props) => { const fileInputRef = useRef(null) const [isImportDataLoading, setIsImportDataLoading] = useState(false) const { @@ -35,7 +35,7 @@ const DataBackups = ({ application, appState }: Props) => { setIsBackupEncrypted, setIsEncryptionEnabled, setEncryptionStatusString, - } = appState.accountMenu + } = viewControllerManager.accountMenuController const refreshEncryptionStatus = useCallback(() => { const hasUser = application.hasAccount() diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/EmailBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/EmailBackups.tsx index fc9526874..488b1400e 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/EmailBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/EmailBackups.tsx @@ -1,7 +1,7 @@ import { convertStringifiedBooleanToBoolean, isDesktopApplication } from '@/Utils' import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/Strings' import { useCallback, useEffect, useState } from 'react' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { observer } from 'mobx-react-lite' import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' import Dropdown from '@/Components/Dropdown/Dropdown' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx index 0b9e64fc3..f406a2263 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx @@ -6,7 +6,7 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import Icon from '@/Components/Icon/Icon' import { StreamingFileApi } from '@standardnotes/filepicker' import { isHandlingBackupDrag } from '@/Utils/DragTypeCheck' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import EncryptionStatusItem from '../../Security/EncryptionStatusItem' import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx index 2118645c5..9e95dde16 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx @@ -1,7 +1,7 @@ import { Subtitle, Title, Text } from '@/Components/Preferences/PreferencesComponents/Content' import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup' import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { useMemo } from 'react' import BackupsDropZone from './BackupsDropZone' import FileBackupsDesktop from './FileBackupsDesktop' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx index 64c625a81..2a57d4106 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { observer } from 'mobx-react-lite' import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' import { useCallback, useEffect, useState } from 'react' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionItemProps.tsx b/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionItemProps.tsx index aef23ebd2..c8419a7f6 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionItemProps.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionItemProps.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { AnyExtension } from './AnyExtension' export interface ExtensionItemProps { diff --git a/app/assets/javascripts/Components/Preferences/Panes/Extensions/Extensions.tsx b/app/assets/javascripts/Components/Preferences/Panes/Extensions/Extensions.tsx index e9f872ce2..97c700e33 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Extensions/Extensions.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Extensions/Extensions.tsx @@ -1,7 +1,7 @@ import { ButtonType, ContentType, SNComponent } from '@standardnotes/snjs' import Button from '@/Components/Button/Button' import DecoratedInput from '@/Components/Input/DecoratedInput' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FunctionComponent, useEffect, useRef, useState } from 'react' import { Title } from '@/Components/Preferences/PreferencesComponents/Content' import { observer } from 'mobx-react-lite' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions.ts b/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions.ts index 3d64aa9d2..19d03c77a 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions.ts +++ b/app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions.ts @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ClientDisplayableError, FeatureDescription } from '@standardnotes/snjs' import { makeAutoObservable, observable } from 'mobx' import { AnyExtension } from './AnyExtension' diff --git a/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx b/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx index 0fb52173c..4c8aef6a9 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx @@ -2,7 +2,7 @@ import Dropdown from '@/Components/Dropdown/Dropdown' import { DropdownItem } from '@/Components/Dropdown/DropdownItem' import { FeatureIdentifier, PrefKey, ComponentArea, ComponentMutator, SNComponent } from '@standardnotes/snjs' import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FunctionComponent, useEffect, useState } from 'react' import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import Switch from '@/Components/Switch/Switch' @@ -57,7 +57,7 @@ const Defaults: FunctionComponent = ({ application }) => { const toggleSpellcheck = () => { setSpellcheck(!spellcheck) - application.getAppState().toggleGlobalSpellcheck().catch(console.error) + application.getViewControllerManager().toggleGlobalSpellcheck().catch(console.error) } useEffect(() => { diff --git a/app/assets/javascripts/Components/Preferences/Panes/General/General.tsx b/app/assets/javascripts/Components/Preferences/Panes/General/General.tsx index dec99befc..9f530e480 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/General/General.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/General/General.tsx @@ -1,5 +1,5 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { FunctionComponent } from 'react' import { ExtensionsLatestVersions } from '@/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions' import { observer } from 'mobx-react-lite' @@ -10,17 +10,21 @@ import Advanced from '@/Components/Preferences/Panes/Account/Advanced' import PreferencesPane from '../../PreferencesComponents/PreferencesPane' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager application: WebApplication extensionsLatestVersions: ExtensionsLatestVersions } -const General: FunctionComponent = ({ appState, application, extensionsLatestVersions }) => ( +const General: FunctionComponent = ({ viewControllerManager, application, extensionsLatestVersions }) => ( - + ) diff --git a/app/assets/javascripts/Components/Preferences/Panes/General/Labs.tsx b/app/assets/javascripts/Components/Preferences/Panes/General/Labs.tsx index df48fdacf..a361246bb 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/General/Labs.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/General/Labs.tsx @@ -1,6 +1,6 @@ import Switch from '@/Components/Switch/Switch' import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FeatureIdentifier, FeatureStatus, FindNativeFeature } from '@standardnotes/snjs' import { Fragment, FunctionComponent, useCallback, useEffect, useState } from 'react' import { usePremiumModal } from '@/Hooks/usePremiumModal' diff --git a/app/assets/javascripts/Components/Preferences/Panes/General/Tools.tsx b/app/assets/javascripts/Components/Preferences/Panes/General/Tools.tsx index 39b8a95e3..0370720b1 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/General/Tools.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/General/Tools.tsx @@ -1,7 +1,7 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import Switch from '@/Components/Switch/Switch' import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { PrefKey } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { FunctionComponent, useState } from 'react' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Listed/Listed.tsx b/app/assets/javascripts/Components/Preferences/Panes/Listed/Listed.tsx index c4c9f4385..66c1d2858 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Listed/Listed.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Listed/Listed.tsx @@ -1,6 +1,6 @@ import { Title, Subtitle, Text } from '@/Components/Preferences/PreferencesComponents/Content' import { observer } from 'mobx-react-lite' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ButtonType, ListedAccount } from '@standardnotes/snjs' import { useCallback, useEffect, useState } from 'react' import ListedAccountItem from './ListedAccountItem' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx b/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx index ff8364a0e..eaeab7bac 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx @@ -1,6 +1,6 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import { LinkButton, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ListedAccount, ListedAccountInfo } from '@standardnotes/snjs' import { FunctionComponent, useEffect, useState } from 'react' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx index 944c0dbcc..45b000a66 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx @@ -1,5 +1,5 @@ import { STRING_E2E_ENABLED, STRING_ENC_NOT_ENABLED, STRING_LOCAL_ENC_ENABLED } from '@/Strings' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'react' import { Title, Text } from '../../PreferencesComponents/Content' @@ -7,10 +7,10 @@ import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' import EncryptionEnabled from './EncryptionEnabled' -type Props = { appState: AppState } +type Props = { viewControllerManager: ViewControllerManager } -const Encryption: FunctionComponent = ({ appState }) => { - const app = appState.application +const Encryption: FunctionComponent = ({ viewControllerManager }) => { + const app = viewControllerManager.application const hasUser = app.hasAccount() const hasPasscode = app.hasPasscode() const isEncryptionEnabled = app.isEncryptionAvailable() @@ -27,7 +27,7 @@ const Encryption: FunctionComponent = ({ appState }) => { Encryption {encryptionStatusString} - {isEncryptionEnabled && } + {isEncryptionEnabled && } ) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/EncryptionEnabled.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/EncryptionEnabled.tsx index add683ae8..a504e133e 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/EncryptionEnabled.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/EncryptionEnabled.tsx @@ -1,16 +1,16 @@ import Icon from '@/Components/Icon/Icon' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'react' import EncryptionStatusItem from './EncryptionStatusItem' import { formatCount } from './formatCount' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager } -const EncryptionEnabled: FunctionComponent = ({ appState }) => { - const count = appState.accountMenu.structuredNotesAndTagsCount +const EncryptionEnabled: FunctionComponent = ({ viewControllerManager }) => { + const count = viewControllerManager.accountMenuController.structuredNotesAndTagsCount const notes = formatCount(count.notes, 'notes') const tags = formatCount(count.tags, 'tags') const archived = formatCount(count.archived, 'archived notes') diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx index d0d7c83ec..7298d0c99 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx @@ -1,4 +1,4 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { observer } from 'mobx-react-lite' import { Fragment, FunctionComponent, useState } from 'react' import { Text, Title, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content' @@ -13,10 +13,10 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' -type Props = { appState: AppState } +type Props = { viewControllerManager: ViewControllerManager } -const ErroredItems: FunctionComponent = ({ appState }: Props) => { - const app = appState.application +const ErroredItems: FunctionComponent = ({ viewControllerManager }: Props) => { + const app = viewControllerManager.application const [erroredItems, setErroredItems] = useState(app.items.invalidItems) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/PasscodeLock.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/PasscodeLock.tsx index 69ba76c63..eb7b6e73c 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/PasscodeLock.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/PasscodeLock.tsx @@ -8,13 +8,13 @@ import { StringUtils, Strings, } from '@/Strings' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { preventRefreshing } from '@/Utils' import { alertDialog } from '@/Services/AlertService' import { ChangeEventHandler, FormEvent, useCallback, useEffect, useRef, useState } from 'react' import { ApplicationEvent } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { Title, Text } from '@/Components/Preferences/PreferencesComponents/Content' import Button from '@/Components/Button/Button' import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' @@ -22,14 +22,15 @@ import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' type Props = { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager } -const PasscodeLock = ({ application, appState }: Props) => { +const PasscodeLock = ({ application, viewControllerManager }: Props) => { const keyStorageInfo = StringUtils.keyStorageInfo(application) const passcodeAutoLockOptions = application.getAutolockService().getAutoLockIntervalOptions() - const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } = appState.accountMenu + const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } = + viewControllerManager.accountMenuController const passcodeInputRef = useRef(null) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/Privacy.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/Privacy.tsx index 7d4d270f9..6949235c4 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/Privacy.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/Privacy.tsx @@ -1,7 +1,7 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import Switch from '@/Components/Switch/Switch' import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { MuteSignInEmailsOption, LogSessionUserAgentOption, SettingName } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { FunctionComponent, useCallback, useEffect, useState } from 'react' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/Protections.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/Protections.tsx index 2668e7ecb..5a9668b2e 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/Protections.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/Protections.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { FunctionComponent, useCallback, useState, useEffect } from 'react' import { ApplicationEvent } from '@standardnotes/snjs' import { isSameDay } from '@/Utils' diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/Security.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/Security.tsx index 9c256c1f8..b59e025da 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -1,5 +1,5 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' import { FunctionComponent } from 'react' import TwoFactorAuthWrapper from '../TwoFactorAuth/TwoFactorAuthWrapper' import { MfaProps } from '../TwoFactorAuth/MfaProps' @@ -11,17 +11,19 @@ import ErroredItems from './ErroredItems' import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane' interface SecurityProps extends MfaProps { - appState: AppState + viewControllerManager: ViewControllerManager application: WebApplication } const Security: FunctionComponent = (props) => ( - - {props.application.items.invalidItems.length > 0 && } + + {props.application.items.invalidItems.length > 0 && ( + + )} - + {props.application.getUser() && } ) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/securityPrefsHasBubble.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/securityPrefsHasBubble.tsx index d6dda7577..8b5a423c6 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/securityPrefsHasBubble.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/securityPrefsHasBubble.tsx @@ -1,4 +1,4 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' export const securityPrefsHasBubble = (application: WebApplication): boolean => { return application.items.invalidItems.length > 0 diff --git a/app/assets/javascripts/Components/Preferences/PreferencesMenu.ts b/app/assets/javascripts/Components/Preferences/PreferencesMenu.ts index d7454c5db..188fa27ca 100644 --- a/app/assets/javascripts/Components/Preferences/PreferencesMenu.ts +++ b/app/assets/javascripts/Components/Preferences/PreferencesMenu.ts @@ -1,6 +1,6 @@ import { action, makeAutoObservable, observable } from 'mobx' import { IconType } from '@standardnotes/snjs' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { ExtensionsLatestVersions } from './Panes/Extensions/ExtensionsLatestVersions' import { securityPrefsHasBubble } from './Panes/Security/securityPrefsHasBubble' diff --git a/app/assets/javascripts/Components/Preferences/PreferencesProps.tsx b/app/assets/javascripts/Components/Preferences/PreferencesProps.tsx index dc84b3e24..1e4949fd6 100644 --- a/app/assets/javascripts/Components/Preferences/PreferencesProps.tsx +++ b/app/assets/javascripts/Components/Preferences/PreferencesProps.tsx @@ -1,9 +1,9 @@ -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { MfaProps } from './Panes/TwoFactorAuth/MfaProps' -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' export interface PreferencesProps extends MfaProps { application: WebApplication - appState: AppState + viewControllerManager: ViewControllerManager closePreferences: () => void } diff --git a/app/assets/javascripts/Components/Preferences/PreferencesView.tsx b/app/assets/javascripts/Components/Preferences/PreferencesView.tsx index f3be2eaac..06d7b94f9 100644 --- a/app/assets/javascripts/Components/Preferences/PreferencesView.tsx +++ b/app/assets/javascripts/Components/Preferences/PreferencesView.tsx @@ -9,12 +9,12 @@ import { PreferencesProps } from './PreferencesProps' const PreferencesView: FunctionComponent = (props) => { const menu = useMemo( - () => new PreferencesMenu(props.application, props.appState.enableUnfinishedFeatures), - [props.appState.enableUnfinishedFeatures, props.application], + () => new PreferencesMenu(props.application, props.viewControllerManager.enableUnfinishedFeatures), + [props.viewControllerManager.enableUnfinishedFeatures, props.application], ) useEffect(() => { - menu.selectPane(props.appState.preferences.currentPane) + menu.selectPane(props.viewControllerManager.preferencesController.currentPane) const removeEscKeyObserver = props.application.io.addKeyObserver({ key: 'Escape', onKeyDown: (event) => { diff --git a/app/assets/javascripts/Components/Preferences/PreferencesViewWrapper.tsx b/app/assets/javascripts/Components/Preferences/PreferencesViewWrapper.tsx index f42c2c7b9..8a13b78cd 100644 --- a/app/assets/javascripts/Components/Preferences/PreferencesViewWrapper.tsx +++ b/app/assets/javascripts/Components/Preferences/PreferencesViewWrapper.tsx @@ -3,16 +3,19 @@ import { observer } from 'mobx-react-lite' import PreferencesView from './PreferencesView' import { PreferencesViewWrapperProps } from './PreferencesViewWrapperProps' -const PreferencesViewWrapper: FunctionComponent = ({ appState, application }) => { - if (!appState.preferences?.isOpen) { +const PreferencesViewWrapper: FunctionComponent = ({ + viewControllerManager, + application, +}) => { + if (!viewControllerManager.preferencesController?.isOpen) { return null } return ( appState.preferences.closePreferences()} + closePreferences={() => viewControllerManager.preferencesController.closePreferences()} application={application} - appState={appState} + viewControllerManager={viewControllerManager} mfaProvider={application} userProvider={application} /> diff --git a/app/assets/javascripts/Components/Preferences/PreferencesViewWrapperProps.tsx b/app/assets/javascripts/Components/Preferences/PreferencesViewWrapperProps.tsx index 95ad152f2..a7dd58638 100644 --- a/app/assets/javascripts/Components/Preferences/PreferencesViewWrapperProps.tsx +++ b/app/assets/javascripts/Components/Preferences/PreferencesViewWrapperProps.tsx @@ -1,7 +1,7 @@ -import { WebApplication } from '@/UIModels/Application' -import { AppState } from '@/UIModels/AppState' +import { WebApplication } from '@/Application/Application' +import { ViewControllerManager } from '@/Services/ViewControllerManager' export interface PreferencesViewWrapperProps { - appState: AppState + viewControllerManager: ViewControllerManager application: WebApplication } diff --git a/app/assets/javascripts/Components/PremiumFeaturesModal/PremiumFeaturesModal.tsx b/app/assets/javascripts/Components/PremiumFeaturesModal/PremiumFeaturesModal.tsx index dd934247e..5eabb0e8a 100644 --- a/app/assets/javascripts/Components/PremiumFeaturesModal/PremiumFeaturesModal.tsx +++ b/app/assets/javascripts/Components/PremiumFeaturesModal/PremiumFeaturesModal.tsx @@ -2,7 +2,7 @@ import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/al import { FunctionComponent, useCallback, useRef } from 'react' import Icon from '@/Components/Icon/Icon' import { PremiumIllustration } from '@standardnotes/icons' -import { WebApplication } from '@/UIModels/Application' +import { WebApplication } from '@/Application/Application' import { openSubscriptionDashboard } from '@/Utils/ManageSubscription' type Props = { diff --git a/app/assets/javascripts/Components/ProtectedNoteOverlay/ProtectedNoteOverlay.tsx b/app/assets/javascripts/Components/ProtectedNoteOverlay/ProtectedNoteOverlay.tsx index 52399f8c1..f8b7a682a 100644 --- a/app/assets/javascripts/Components/ProtectedNoteOverlay/ProtectedNoteOverlay.tsx +++ b/app/assets/javascripts/Components/ProtectedNoteOverlay/ProtectedNoteOverlay.tsx @@ -1,12 +1,12 @@ -import { AppState } from '@/UIModels/AppState' +import { ViewControllerManager } from '@/Services/ViewControllerManager' type Props = { - appState: AppState + viewControllerManager: ViewControllerManager onViewNote: () => void hasProtectionSources: boolean } -const ProtectedNoteOverlay = ({ appState, onViewNote, hasProtectionSources }: Props) => { +const ProtectedNoteOverlay = ({ viewControllerManager, onViewNote, hasProtectionSources }: Props) => { const instructionText = hasProtectionSources ? 'Authenticate to view this note.' : 'Add a passcode or create an account to require authentication to view this note.' @@ -20,7 +20,7 @@ const ProtectedNoteOverlay = ({ appState, onViewNote, hasProtectionSources }: Pr
    - v{viewControllerManager.version} + v{application.version} {user ? ( <> diff --git a/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx b/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx index fb492862f..20d441181 100644 --- a/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx +++ b/app/assets/javascripts/Components/ApplicationView/ApplicationView.tsx @@ -1,10 +1,10 @@ import { ApplicationGroup } from '@/Application/ApplicationGroup' import { getPlatformString, getWindowUrlParams } from '@/Utils' -import { ViewControllerManagerEvent } from '@/Services/ViewControllerManager' import { ApplicationEvent, Challenge, removeFromArray } from '@standardnotes/snjs' import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants' import { alertDialog } from '@/Services/AlertService' import { WebApplication } from '@/Application/Application' +import { WebAppEvent } from '@/Application/WebAppEvent' import Navigation from '@/Components/Navigation/Navigation' import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView' import Footer from '@/Components/Footer/Footer' @@ -120,8 +120,8 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio }, [application, onAppLaunch, onAppStart]) useEffect(() => { - const removeObserver = application.getViewControllerManager().addObserver(async (eventName, data) => { - if (eventName === ViewControllerManagerEvent.PanelResized) { + const removeObserver = application.addWebEventObserver(async (eventName, data) => { + if (eventName === WebAppEvent.PanelResized) { const { panel, collapsed } = data as PanelResizedData let appClass = '' if (panel === PANEL_NAME_NOTES && collapsed) { @@ -131,7 +131,7 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio appClass += ' collapsed-navigation' } setAppClass(appClass) - } else if (eventName === ViewControllerManagerEvent.WindowDidFocus) { + } else if (eventName === WebAppEvent.WindowDidFocus) { if (!(await application.isLocked())) { application.sync.sync().catch(console.error) } diff --git a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx index 6adcdfec1..2bc3d8a72 100644 --- a/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx +++ b/app/assets/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx @@ -90,7 +90,7 @@ const ChangeEditorMenu: FunctionComponent = ({ const transactions: TransactionalMutation[] = [] - await application.getViewControllerManager().contentListController.insertCurrentIfTemplate() + await application.getViewControllerManager().itemListController.insertCurrentIfTemplate() if (note.locked) { application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error) diff --git a/app/assets/javascripts/Components/ContentListView/ContentList.tsx b/app/assets/javascripts/Components/ContentListView/ContentList.tsx index 33f8b2066..8e5709e75 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentList.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentList.tsx @@ -23,10 +23,10 @@ const ContentList: FunctionComponent = ({ selectedItems, paginate, }) => { - const { selectPreviousItem, selectNextItem } = viewControllerManager.contentListController + const { selectPreviousItem, selectNextItem } = viewControllerManager.itemListController const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = - viewControllerManager.contentListController.webDisplayOptions - const { sortBy } = viewControllerManager.contentListController.displayOptions + viewControllerManager.itemListController.webDisplayOptions + const { sortBy } = viewControllerManager.itemListController.displayOptions const onScroll: UIEventHandler = useCallback( (e) => { diff --git a/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx b/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx index 2c4d301c0..ab4ebcbaf 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentListItem.tsx @@ -15,7 +15,7 @@ const ContentListItem: FunctionComponent = (props) => { return [] } - const tags = props.viewControllerManager.getItemTags(props.item) + const tags = props.application.getItemTags(props.item) const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1 if (isNavigatingOnlyTag) { diff --git a/app/assets/javascripts/Components/ContentListView/ContentListView.tsx b/app/assets/javascripts/Components/ContentListView/ContentListView.tsx index 1385f550e..53bdb6884 100644 --- a/app/assets/javascripts/Components/ContentListView/ContentListView.tsx +++ b/app/assets/javascripts/Components/ContentListView/ContentListView.tsx @@ -46,7 +46,7 @@ const ContentListView: FunctionComponent = ({ application, viewController paginate, panelWidth, createNewNote, - } = viewControllerManager.contentListController + } = viewControllerManager.itemListController const { selectedItems } = viewControllerManager.selectionController @@ -143,7 +143,7 @@ const ContentListView: FunctionComponent = ({ application, viewController (width, _lastLeft, _isMaxWidth, isCollapsed) => { application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error) viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth() - viewControllerManager.panelDidResize(PANEL_NAME_NOTES, isCollapsed) + application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, isCollapsed) }, [viewControllerManager, application], ) diff --git a/app/assets/javascripts/Components/Footer/Footer.tsx b/app/assets/javascripts/Components/Footer/Footer.tsx index 9967719bb..f6d5a969b 100644 --- a/app/assets/javascripts/Components/Footer/Footer.tsx +++ b/app/assets/javascripts/Components/Footer/Footer.tsx @@ -1,4 +1,5 @@ -import { WebAppEvent, WebApplication } from '@/Application/Application' +import { WebApplication } from '@/Application/Application' +import { WebAppEvent } from '@/Application/WebAppEvent' import { ApplicationGroup } from '@/Application/ApplicationGroup' import { PureComponent } from '@/Components/Abstract/PureComponent' import { destroyAllObjectProperties, preventRefreshing } from '@/Utils' @@ -12,7 +13,6 @@ import { } from '@/Constants/Strings' import { alertDialog, confirmDialog } from '@/Services/AlertService' import AccountMenu from '@/Components/AccountMenu/AccountMenu' -import { ViewControllerManagerEvent } from '@/Services/ViewControllerManager' import Icon from '@/Components/Icon/Icon' import QuickSettingsMenu from '@/Components/QuickSettingsMenu/QuickSettingsMenu' import SyncResolutionMenu from '@/Components/SyncResolutionMenu/SyncResolutionMenu' @@ -64,9 +64,34 @@ class Footer extends PureComponent { showQuickSettingsMenu: false, } - this.webEventListenerDestroyer = props.application.addWebEventObserver((event) => { - if (event === WebAppEvent.NewUpdateAvailable) { - this.onNewUpdateAvailable() + this.webEventListenerDestroyer = props.application.addWebEventObserver((event, data) => { + const statusService = this.application.status + + switch (event) { + case WebAppEvent.NewUpdateAvailable: + this.onNewUpdateAvailable() + break + case WebAppEvent.EditorFocused: + if ((data as any).eventSource === EditorEventSource.UserInteraction) { + this.closeAccountMenu() + } + break + case WebAppEvent.BeganBackupDownload: + statusService.setMessage('Saving local backup…') + break + case WebAppEvent.EndedBackupDownload: { + const successMessage = 'Successfully saved backup.' + const errorMessage = 'Unable to save local backup.' + statusService.setMessage((data as any).success ? successMessage : errorMessage) + + const twoSeconds = 2000 + setTimeout(() => { + if (statusService.message === successMessage || statusService.message === errorMessage) { + statusService.setMessage('') + } + }, twoSeconds) + break + } } }) } @@ -133,33 +158,6 @@ class Footer extends PureComponent { }) } - override onViewControllerManagerEvent(eventName: ViewControllerManagerEvent, data: any) { - const statusService = this.application.status - switch (eventName) { - case ViewControllerManagerEvent.EditorFocused: - if (data.eventSource === EditorEventSource.UserInteraction) { - this.closeAccountMenu() - } - break - case ViewControllerManagerEvent.BeganBackupDownload: - statusService.setMessage('Saving local backup…') - break - case ViewControllerManagerEvent.EndedBackupDownload: { - const successMessage = 'Successfully saved backup.' - const errorMessage = 'Unable to save local backup.' - statusService.setMessage(data.success ? successMessage : errorMessage) - - const twoSeconds = 2000 - setTimeout(() => { - if (statusService.message === successMessage || statusService.message === errorMessage) { - statusService.setMessage('') - } - }, twoSeconds) - break - } - } - } - override async onAppKeyChange() { super.onAppKeyChange().catch(console.error) this.reloadPasscodeStatus().catch(console.error) diff --git a/app/assets/javascripts/Components/Navigation/Navigation.tsx b/app/assets/javascripts/Components/Navigation/Navigation.tsx index 1ca094af9..e01a43df6 100644 --- a/app/assets/javascripts/Components/Navigation/Navigation.tsx +++ b/app/assets/javascripts/Components/Navigation/Navigation.tsx @@ -33,7 +33,7 @@ const Navigation: FunctionComponent = ({ application }) => { (width, _lastLeft, _isMaxWidth, isCollapsed) => { application.setPreference(PrefKey.TagsPanelWidth, width).catch(console.error) viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth() - viewControllerManager.panelDidResize(PANEL_NAME_NAVIGATION, isCollapsed) + application.publishPanelDidResizeEvent(PANEL_NAME_NAVIGATION, isCollapsed) }, [application, viewControllerManager], ) diff --git a/app/assets/javascripts/Components/NoteView/NoteView.tsx b/app/assets/javascripts/Components/NoteView/NoteView.tsx index 5e1d95299..6e04192e9 100644 --- a/app/assets/javascripts/Components/NoteView/NoteView.tsx +++ b/app/assets/javascripts/Components/NoteView/NoteView.tsx @@ -35,6 +35,7 @@ import { } from './TransactionFunctions' import { reloadFont } from './FontFunctions' import { NoteViewProps } from './NoteViewProps' +import { WebAppEvent } from '@/Application/WebAppEvent' const MINIMUM_STATUS_DURATION = 400 const TEXTAREA_DEBOUNCE = 100 @@ -588,7 +589,7 @@ class NoteView extends PureComponent { onContentFocus = () => { if (this.lastEditorFocusEventSource) { - this.application.getViewControllerManager().editorDidFocus(this.lastEditorFocusEventSource) + this.application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: this.lastEditorFocusEventSource }) } this.lastEditorFocusEventSource = undefined } diff --git a/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx b/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx index 2801073d9..4f74ca10d 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/General/Defaults.tsx @@ -57,7 +57,7 @@ const Defaults: FunctionComponent = ({ application }) => { const toggleSpellcheck = () => { setSpellcheck(!spellcheck) - application.getViewControllerManager().toggleGlobalSpellcheck().catch(console.error) + application.toggleGlobalSpellcheck().catch(console.error) } useEffect(() => { diff --git a/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx b/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx index 84987a8b4..13e2e5513 100644 --- a/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx +++ b/app/assets/javascripts/Components/Tags/RootTagDropZone.tsx @@ -1,14 +1,14 @@ import Icon from '@/Components/Icon/Icon' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { FeaturesController } from '@/Controllers/FeaturesController' -import { TagsController } from '@/Controllers/Navigation/TagsController' +import { NavigationController } from '@/Controllers/Navigation/NavigationController' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'react' import { useDrop } from 'react-dnd' import { DropItem, DropProps, ItemTypes } from './DragNDrop' type Props = { - tagsState: TagsController + tagsState: NavigationController featuresState: FeaturesController } diff --git a/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx b/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx index ab8a8ca2c..e895770df 100644 --- a/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx +++ b/app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx @@ -1,6 +1,6 @@ import Icon from '@/Components/Icon/Icon' import { FeaturesController } from '@/Controllers/FeaturesController' -import { TagsController } from '@/Controllers/Navigation/TagsController' +import { NavigationController } from '@/Controllers/Navigation/NavigationController' import '@reach/tooltip/styles.css' import { SmartView, SystemViewId, IconType, isSystemView } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' @@ -16,7 +16,7 @@ import { type Props = { view: SmartView - tagsState: TagsController + tagsState: NavigationController features: FeaturesController } diff --git a/app/assets/javascripts/Components/Tags/TagsListItem.tsx b/app/assets/javascripts/Components/Tags/TagsListItem.tsx index 7c524c511..bdacac921 100644 --- a/app/assets/javascripts/Components/Tags/TagsListItem.tsx +++ b/app/assets/javascripts/Components/Tags/TagsListItem.tsx @@ -3,7 +3,7 @@ import { TAG_FOLDERS_FEATURE_NAME } from '@/Constants/Constants' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { KeyboardKey } from '@/Services/IOService' import { FeaturesController } from '@/Controllers/FeaturesController' -import { TagsController } from '@/Controllers/Navigation/TagsController' +import { NavigationController } from '@/Controllers/Navigation/NavigationController' import '@reach/tooltip/styles.css' import { SNTag } from '@standardnotes/snjs' import { computed } from 'mobx' @@ -23,7 +23,7 @@ import { DropItem, DropProps, ItemTypes } from './DragNDrop' type Props = { tag: SNTag - tagsState: TagsController + tagsState: NavigationController features: FeaturesController level: number onContextMenu: (tag: SNTag, posX: number, posY: number) => void diff --git a/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx b/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx index ccac02cc5..405e0b903 100644 --- a/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx +++ b/app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx @@ -1,11 +1,11 @@ import IconButton from '@/Components/Button/IconButton' import { FeaturesController } from '@/Controllers/FeaturesController' -import { TagsController } from '@/Controllers/Navigation/TagsController' +import { NavigationController } from '@/Controllers/Navigation/NavigationController' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'react' type Props = { - tags: TagsController + tags: NavigationController features: FeaturesController } diff --git a/app/assets/javascripts/Controllers/Abstract/AbstractViewController.ts b/app/assets/javascripts/Controllers/Abstract/AbstractViewController.ts index e72335491..9dcc477a5 100644 --- a/app/assets/javascripts/Controllers/Abstract/AbstractViewController.ts +++ b/app/assets/javascripts/Controllers/Abstract/AbstractViewController.ts @@ -1,14 +1,27 @@ -import { DeinitSource } from '@standardnotes/snjs' +import { CrossControllerEvent } from '../CrossControllerEvent' +import { InternalEventBus, InternalEventPublishStrategy } from '@standardnotes/snjs' import { WebApplication } from '../../Application/Application' +import { Disposer } from '@/Types/Disposer' export abstract class AbstractViewController { dealloced = false + protected disposers: Disposer[] = [] - constructor(public application: WebApplication, public viewControllerManager?: AbstractViewController) {} + constructor(public application: WebApplication, protected eventBus: InternalEventBus) {} - deinit(_source: DeinitSource): void { + protected async publishEventSync(name: CrossControllerEvent): Promise { + await this.eventBus.publishSync({ type: name, payload: undefined }, InternalEventPublishStrategy.SEQUENCE) + } + + deinit(): void { this.dealloced = true ;(this.application as unknown) = undefined - ;(this.viewControllerManager as unknown) = undefined + ;(this.eventBus as unknown) = undefined + + for (const disposer of this.disposers) { + disposer() + } + + ;(this.disposers as unknown) = undefined } } diff --git a/app/assets/javascripts/Controllers/Abstract/IsControllerDealloced.ts b/app/assets/javascripts/Controllers/Abstract/IsControllerDealloced.ts index 5c4cbd58f..92f2c069f 100644 --- a/app/assets/javascripts/Controllers/Abstract/IsControllerDealloced.ts +++ b/app/assets/javascripts/Controllers/Abstract/IsControllerDealloced.ts @@ -1,5 +1,3 @@ -import { AbstractViewController } from './AbstractViewController' - -export function isControllerDealloced(state: AbstractViewController): boolean { - return state.dealloced == undefined || state.dealloced === true +export function isControllerDealloced(controller: { dealloced: boolean }): boolean { + return controller.dealloced == undefined || controller.dealloced === true } diff --git a/app/assets/javascripts/Controllers/AccountMenu/AccountMenuController.ts b/app/assets/javascripts/Controllers/AccountMenu/AccountMenuController.ts index f4bedc2dc..9e2128d21 100644 --- a/app/assets/javascripts/Controllers/AccountMenu/AccountMenuController.ts +++ b/app/assets/javascripts/Controllers/AccountMenu/AccountMenuController.ts @@ -1,6 +1,6 @@ import { destroyAllObjectProperties, isDev } from '@/Utils' import { action, computed, makeObservable, observable, runInAction } from 'mobx' -import { ApplicationEvent, ContentType, DeinitSource, SNNote, SNTag } from '@standardnotes/snjs' +import { ApplicationEvent, ContentType, InternalEventBus, SNNote, SNTag } from '@standardnotes/snjs' import { WebApplication } from '@/Application/Application' import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane' import { AbstractViewController } from '../Abstract/AbstractViewController' @@ -21,15 +21,16 @@ export class AccountMenuController extends AbstractViewController { shouldAnimateCloseMenu = false currentPane = AccountMenuPane.GeneralMenu - override deinit(source: DeinitSource) { - super.deinit(source) + override deinit() { + super.deinit() ;(this.notesAndTags as unknown) = undefined destroyAllObjectProperties(this) } - constructor(application: WebApplication, private appEventListeners: (() => void)[]) { - super(application) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) + makeObservable(this, { show: observable, signingOut: observable, @@ -60,12 +61,7 @@ export class AccountMenuController extends AbstractViewController { notesAndTagsCount: computed, }) - this.addAppLaunchedEventObserver() - this.streamNotesAndTags() - } - - addAppLaunchedEventObserver = (): void => { - this.appEventListeners.push( + this.disposers.push( this.application.addEventObserver(async () => { runInAction(() => { if (isDev && window.devAccountServer) { @@ -77,10 +73,8 @@ export class AccountMenuController extends AbstractViewController { }) }, ApplicationEvent.Launched), ) - } - streamNotesAndTags = (): void => { - this.appEventListeners.push( + this.disposers.push( this.application.streamItems([ContentType.Note, ContentType.Tag], () => { runInAction(() => { this.notesAndTags = this.application.items.getItems([ContentType.Note, ContentType.Tag]) diff --git a/app/assets/javascripts/Controllers/CrossControllerEvent.ts b/app/assets/javascripts/Controllers/CrossControllerEvent.ts new file mode 100644 index 000000000..1c89dd2f9 --- /dev/null +++ b/app/assets/javascripts/Controllers/CrossControllerEvent.ts @@ -0,0 +1,4 @@ +export enum CrossControllerEvent { + TagChanged = 'TagChanged', + ActiveEditorChanged = 'ActiveEditorChanged', +} diff --git a/app/assets/javascripts/Controllers/FeaturesController.ts b/app/assets/javascripts/Controllers/FeaturesController.ts index c1a7720f7..157cfab55 100644 --- a/app/assets/javascripts/Controllers/FeaturesController.ts +++ b/app/assets/javascripts/Controllers/FeaturesController.ts @@ -1,6 +1,6 @@ import { WebApplication } from '@/Application/Application' import { destroyAllObjectProperties } from '@/Utils' -import { ApplicationEvent, DeinitSource, FeatureIdentifier, FeatureStatus } from '@standardnotes/snjs' +import { ApplicationEvent, FeatureIdentifier, FeatureStatus, InternalEventBus } from '@standardnotes/snjs' import { action, makeObservable, observable, runInAction, when } from 'mobx' import { AbstractViewController } from './Abstract/AbstractViewController' @@ -10,8 +10,8 @@ export class FeaturesController extends AbstractViewController { hasFiles: boolean premiumAlertFeatureName: string | undefined - override deinit(source: DeinitSource) { - super.deinit(source) + override deinit() { + super.deinit() ;(this.showPremiumAlert as unknown) = undefined ;(this.closePremiumAlert as unknown) = undefined ;(this.hasFolders as unknown) = undefined @@ -22,8 +22,8 @@ export class FeaturesController extends AbstractViewController { destroyAllObjectProperties(this) } - constructor(application: WebApplication, appObservers: (() => void)[]) { - super(application) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) this.hasFolders = this.isEntitledToFolders() this.hasSmartViews = this.isEntitledToSmartViews() @@ -43,7 +43,7 @@ export class FeaturesController extends AbstractViewController { this.showPremiumAlert = this.showPremiumAlert.bind(this) this.closePremiumAlert = this.closePremiumAlert.bind(this) - appObservers.push( + this.disposers.push( application.addEventObserver(async (event) => { switch (event) { case ApplicationEvent.FeaturesUpdated: diff --git a/app/assets/javascripts/Controllers/FilesController.ts b/app/assets/javascripts/Controllers/FilesController.ts index aeb7c11d0..d09abd878 100644 --- a/app/assets/javascripts/Controllers/FilesController.ts +++ b/app/assets/javascripts/Controllers/FilesController.ts @@ -1,3 +1,4 @@ +import { FilePreviewModalController } from './FilePreviewModalController' import { PopoverFileItemAction, PopoverFileItemActionType, @@ -13,12 +14,13 @@ import { ClassicFileSaver, parseFileName, } from '@standardnotes/filepicker' -import { ChallengeReason, ClientDisplayableError, ContentType, FileItem } from '@standardnotes/snjs' +import { ChallengeReason, ClientDisplayableError, ContentType, FileItem, InternalEventBus } from '@standardnotes/snjs' import { addToast, dismissToast, ToastType, updateToast } from '@standardnotes/stylekit' import { action, computed, makeObservable, observable, reaction } from 'mobx' import { WebApplication } from '../Application/Application' import { AbstractViewController } from './Abstract/AbstractViewController' -import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager' +import { NotesController } from './NotesController' +import { SelectedItemsController } from './SelectedItemsController' const UnprotectedFileActions = [PopoverFileItemActionType.ToggleFileProtection] const NonMutatingFileActions = [PopoverFileItemActionType.DownloadFile, PopoverFileItemActionType.PreviewFile] @@ -31,12 +33,21 @@ export class FilesController extends AbstractViewController { showFileContextMenu = false fileContextMenuLocation: FileContextMenuLocation = { x: 0, y: 0 } + override deinit(): void { + super.deinit() + ;(this.notesController as unknown) = undefined + ;(this.selectionController as unknown) = undefined + ;(this.filePreviewModalController as unknown) = undefined + } + constructor( application: WebApplication, - override viewControllerManager: ViewControllerManager, - appObservers: (() => void)[], + private notesController: NotesController, + private selectionController: SelectedItemsController, + private filePreviewModalController: FilePreviewModalController, + eventBus: InternalEventBus, ) { - super(application, viewControllerManager) + super(application, eventBus) makeObservable(this, { allFiles: observable, @@ -52,13 +63,16 @@ export class FilesController extends AbstractViewController { setFileContextMenuLocation: action, }) - appObservers.push( + this.disposers.push( application.streamItems(ContentType.File, () => { this.reloadAllFiles() this.reloadAttachedFiles() }), + ) + + this.disposers.push( reaction( - () => viewControllerManager.notesController.selectedNotes, + () => notesController.selectedNotes, () => { this.reloadAttachedFiles() }, @@ -67,7 +81,7 @@ export class FilesController extends AbstractViewController { } get selectedFiles(): FileItem[] { - return this.viewControllerManager.selectionController.getSelectedItems(ContentType.File) + return this.selectionController.getSelectedItems(ContentType.File) } setShowFileContextMenu = (enabled: boolean) => { @@ -83,7 +97,7 @@ export class FilesController extends AbstractViewController { } reloadAttachedFiles = () => { - const note = this.viewControllerManager.notesController.firstSelectedNote + const note = this.notesController.firstSelectedNote if (note) { this.attachedFiles = this.application.items.getFilesForNote(note) } @@ -109,7 +123,7 @@ export class FilesController extends AbstractViewController { } attachFileToNote = async (file: FileItem) => { - const note = this.viewControllerManager.notesController.firstSelectedNote + const note = this.notesController.firstSelectedNote if (!note) { addToast({ type: ToastType.Error, @@ -122,7 +136,7 @@ export class FilesController extends AbstractViewController { } detachFileFromNote = async (file: FileItem) => { - const note = this.viewControllerManager.notesController.firstSelectedNote + const note = this.notesController.firstSelectedNote if (!note) { addToast({ type: ToastType.Error, @@ -197,7 +211,7 @@ export class FilesController extends AbstractViewController { await this.renameFile(file, action.payload.name) break case PopoverFileItemActionType.PreviewFile: - this.viewControllerManager.filePreviewModalController.activate( + this.filePreviewModalController.activate( file, currentTab === PopoverTabs.AllFiles ? this.allFiles : this.attachedFiles, ) diff --git a/app/assets/javascripts/Controllers/ItemList/ItemListController.ts b/app/assets/javascripts/Controllers/ItemList/ItemListController.ts index d6b468191..b909dae27 100644 --- a/app/assets/javascripts/Controllers/ItemList/ItemListController.ts +++ b/app/assets/javascripts/Controllers/ItemList/ItemListController.ts @@ -4,7 +4,6 @@ import { ApplicationEvent, CollectionSort, ContentType, - DeinitSource, findInArray, NoteViewController, PrefKey, @@ -13,13 +12,21 @@ import { SNTag, SystemViewId, DisplayOptions, + InternalEventBus, + InternalEventHandlerInterface, + InternalEventInterface, } from '@standardnotes/snjs' import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx' import { WebApplication } from '../../Application/Application' import { AbstractViewController } from '../Abstract/AbstractViewController' -import { ViewControllerManager } from '../../Services/ViewControllerManager/ViewControllerManager' -import { ViewControllerManagerEvent } from '../../Services/ViewControllerManager/ViewControllerManagerEvent' import { WebDisplayOptions } from './WebDisplayOptions' +import { NavigationController } from '../Navigation/NavigationController' +import { CrossControllerEvent } from '../CrossControllerEvent' +import { SearchOptionsController } from '../SearchOptionsController' +import { SelectedItemsController } from '../SelectedItemsController' +import { NotesController } from '../NotesController' +import { NoteTagsController } from '../NoteTagsController' +import { WebAppEvent } from '@/Application/WebAppEvent' const MinNoteCellHeight = 51.0 const DefaultListNumNotes = 20 @@ -27,7 +34,7 @@ const ElementIdSearchBar = 'search-bar' const ElementIdScrollContainer = 'notes-scrollable' const SupportsFileSelectionState = false -export class ItemListController extends AbstractViewController { +export class ItemListController extends AbstractViewController implements InternalEventHandlerInterface { completedFullSync = false noteFilterText = '' notes: SNNote[] = [] @@ -55,11 +62,16 @@ export class ItemListController extends AbstractViewController { } private reloadItemsPromise?: Promise - override deinit(source: DeinitSource) { - super.deinit(source) + override deinit() { + super.deinit() ;(this.noteFilterText as unknown) = undefined ;(this.notes as unknown) = undefined ;(this.renderedItems as unknown) = undefined + ;(this.navigationController as unknown) = undefined + ;(this.searchOptionsController as unknown) = undefined + ;(this.selectionController as unknown) = undefined + ;(this.notesController as unknown) = undefined + ;(this.noteTagsController as unknown) = undefined ;(window.onresize as unknown) = undefined destroyAllObjectProperties(this) @@ -67,18 +79,27 @@ export class ItemListController extends AbstractViewController { constructor( application: WebApplication, - override viewControllerManager: ViewControllerManager, - appObservers: (() => void)[], + private navigationController: NavigationController, + private searchOptionsController: SearchOptionsController, + private selectionController: SelectedItemsController, + private notesController: NotesController, + private noteTagsController: NoteTagsController, + eventBus: InternalEventBus, ) { - super(application, viewControllerManager) + super(application, eventBus) + + eventBus.addEventHandler(this, CrossControllerEvent.TagChanged) + eventBus.addEventHandler(this, CrossControllerEvent.ActiveEditorChanged) this.resetPagination() - appObservers.push( + this.disposers.push( application.streamItems(ContentType.Note, () => { void this.reloadItems() }), + ) + this.disposers.push( application.streamItems([ContentType.Tag], async ({ changed, inserted }) => { const tags = [...changed, ...inserted] @@ -87,28 +108,34 @@ export class ItemListController extends AbstractViewController { void this.reloadItems() - if ( - viewControllerManager.navigationController.selected && - findInArray(tags, 'uuid', viewControllerManager.navigationController.selected.uuid) - ) { + if (this.navigationController.selected && findInArray(tags, 'uuid', this.navigationController.selected.uuid)) { /** Tag title could have changed */ this.reloadPanelTitle() } }), + ) + + this.disposers.push( application.addEventObserver(async () => { void this.reloadPreferences() }, ApplicationEvent.PreferencesChanged), + ) + + this.disposers.push( application.addEventObserver(async () => { this.application.noteControllerGroup.closeAllNoteControllers() void this.selectFirstItem() this.setCompletedFullSync(false) }, ApplicationEvent.SignedIn), + ) + + this.disposers.push( application.addEventObserver(async () => { void this.reloadItems().then(() => { if ( this.notes.length === 0 && - viewControllerManager.navigationController.selected instanceof SmartView && - viewControllerManager.navigationController.selected.uuid === SystemViewId.AllNotes && + this.navigationController.selected instanceof SmartView && + this.navigationController.selected.uuid === SystemViewId.AllNotes && this.noteFilterText === '' && !this.getActiveNoteController() ) { @@ -117,28 +144,28 @@ export class ItemListController extends AbstractViewController { }) this.setCompletedFullSync(true) }, ApplicationEvent.CompletedFullSync), + ) + this.disposers.push( + application.addWebEventObserver((webEvent) => { + if (webEvent === WebAppEvent.EditorFocused) { + this.setShowDisplayOptionsMenu(false) + } + }), + ) + + this.disposers.push( reaction( () => [ - viewControllerManager.searchOptionsController.includeProtectedContents, - viewControllerManager.searchOptionsController.includeArchived, - viewControllerManager.searchOptionsController.includeTrashed, + this.searchOptionsController.includeProtectedContents, + this.searchOptionsController.includeArchived, + this.searchOptionsController.includeTrashed, ], () => { this.reloadNotesDisplayOptions() void this.reloadItems() }, ), - - viewControllerManager.addObserver(async (eventName) => { - if (eventName === ViewControllerManagerEvent.TagChanged) { - this.handleTagChange() - } else if (eventName === ViewControllerManagerEvent.ActiveEditorChanged) { - this.handleEditorChange().catch(console.error) - } else if (eventName === ViewControllerManagerEvent.EditorFocused) { - this.setShowDisplayOptionsMenu(false) - } - }), ) makeObservable(this, { @@ -170,6 +197,14 @@ export class ItemListController extends AbstractViewController { } } + async handleEvent(event: InternalEventInterface): Promise { + if (event.type === CrossControllerEvent.TagChanged) { + this.handleTagChange() + } else if (event.type === CrossControllerEvent.ActiveEditorChanged) { + this.handleEditorChange().catch(console.error) + } + } + public getActiveNoteController(): NoteViewController | undefined { return this.application.noteControllerGroup.activeNoteViewController } @@ -200,8 +235,8 @@ export class ItemListController extends AbstractViewController { if (this.isFiltering) { const resultCount = this.notes.length title = `${resultCount} search results` - } else if (this.viewControllerManager.navigationController.selected) { - title = `${this.viewControllerManager.navigationController.selected.title}` + } else if (this.navigationController.selected) { + title = `${this.navigationController.selected.title}` } this.panelTitle = title @@ -218,7 +253,7 @@ export class ItemListController extends AbstractViewController { } private async performReloadItems() { - const tag = this.viewControllerManager.navigationController.selected + const tag = this.navigationController.selected if (!tag) { return } @@ -241,17 +276,16 @@ export class ItemListController extends AbstractViewController { } private async recomputeSelectionAfterItemsReload() { - const viewControllerManager = this.viewControllerManager const activeController = this.getActiveNoteController() const activeNote = activeController?.note const isSearching = this.noteFilterText.length > 0 - const hasMultipleItemsSelected = viewControllerManager.selectionController.selectedItemsCount >= 2 + const hasMultipleItemsSelected = this.selectionController.selectedItemsCount >= 2 if (hasMultipleItemsSelected) { return } - const selectedItem = Object.values(viewControllerManager.selectionController.selectedItems)[0] + const selectedItem = Object.values(this.selectionController.selectedItems)[0] const isSelectedItemFile = this.items.includes(selectedItem) && selectedItem && selectedItem.content_type === ContentType.File @@ -280,25 +314,25 @@ export class ItemListController extends AbstractViewController { } const showTrashedNotes = - (viewControllerManager.navigationController.selected instanceof SmartView && - viewControllerManager.navigationController.selected?.uuid === SystemViewId.TrashedNotes) || - viewControllerManager?.searchOptionsController.includeTrashed + (this.navigationController.selected instanceof SmartView && + this.navigationController.selected?.uuid === SystemViewId.TrashedNotes) || + this.searchOptionsController.includeTrashed const showArchivedNotes = - (viewControllerManager.navigationController.selected instanceof SmartView && - viewControllerManager.navigationController.selected.uuid === SystemViewId.ArchivedNotes) || - viewControllerManager.searchOptionsController.includeArchived || + (this.navigationController.selected instanceof SmartView && + this.navigationController.selected.uuid === SystemViewId.ArchivedNotes) || + this.searchOptionsController.includeArchived || this.application.getPreference(PrefKey.NotesShowArchived, false) if ((activeNote.trashed && !showTrashedNotes) || (activeNote.archived && !showArchivedNotes)) { await this.selectNextItemOrCreateNewNote() - } else if (!this.viewControllerManager.selectionController.selectedItems[activeNote.uuid]) { - await this.viewControllerManager.selectionController.selectItem(activeNote.uuid).catch(console.error) + } else if (!this.selectionController.selectedItems[activeNote.uuid]) { + await this.selectionController.selectItem(activeNote.uuid).catch(console.error) } } reloadNotesDisplayOptions = () => { - const tag = this.viewControllerManager.navigationController.selected + const tag = this.navigationController.selected const searchText = this.noteFilterText.toLowerCase() const isSearching = searchText.length @@ -306,8 +340,8 @@ export class ItemListController extends AbstractViewController { let includeTrashed: boolean if (isSearching) { - includeArchived = this.viewControllerManager.searchOptionsController.includeArchived - includeTrashed = this.viewControllerManager.searchOptionsController.includeTrashed + includeArchived = this.searchOptionsController.includeArchived + includeTrashed = this.searchOptionsController.includeTrashed } else { includeArchived = this.displayOptions.includeArchived ?? false includeTrashed = this.displayOptions.includeTrashed ?? false @@ -324,7 +358,7 @@ export class ItemListController extends AbstractViewController { includeProtected: this.displayOptions.includeProtected, searchQuery: { query: searchText, - includeProtectedNoteText: this.viewControllerManager.searchOptionsController.includeProtectedContents, + includeProtectedNoteText: this.searchOptionsController.includeProtectedContents, }, } @@ -387,13 +421,10 @@ export class ItemListController extends AbstractViewController { } createNewNote = async () => { - this.viewControllerManager.notesController.unselectNotes() + this.notesController.unselectNotes() - if ( - this.viewControllerManager.navigationController.isInSmartView() && - !this.viewControllerManager.navigationController.isInHomeView() - ) { - await this.viewControllerManager.navigationController.selectHomeNavigationView() + if (this.navigationController.isInSmartView() && !this.navigationController.isInHomeView()) { + await this.navigationController.selectHomeNavigationView() } let title = `Note ${this.notes.length + 1}` @@ -401,16 +432,13 @@ export class ItemListController extends AbstractViewController { title = this.noteFilterText } - await this.viewControllerManager.notesController.createNewNoteController(title) + await this.notesController.createNewNoteController(title) - this.viewControllerManager.noteTagsController.reloadTagsForCurrentNote() + this.noteTagsController.reloadTagsForCurrentNote() } createPlaceholderNote = () => { - if ( - this.viewControllerManager.navigationController.isInSmartView() && - !this.viewControllerManager.navigationController.isInHomeView() - ) { + if (this.navigationController.isInSmartView() && !this.navigationController.isInHomeView()) { return } @@ -487,7 +515,7 @@ export class ItemListController extends AbstractViewController { }, { userTriggered = false, scrollIntoView = true }, ): Promise => { - await this.viewControllerManager.selectionController.selectItem(item.uuid, userTriggered) + await this.selectionController.selectItem(item.uuid, userTriggered) if (scrollIntoView) { const itemElement = document.getElementById(item.uuid) @@ -514,7 +542,7 @@ export class ItemListController extends AbstractViewController { const displayableItems = this.items const currentIndex = displayableItems.findIndex((candidate) => { - return candidate.uuid === this.viewControllerManager.selectionController.lastSelectedItem?.uuid + return candidate.uuid === this.selectionController.lastSelectedItem?.uuid }) let nextIndex = currentIndex + 1 @@ -554,11 +582,11 @@ export class ItemListController extends AbstractViewController { selectPreviousItem = () => { const displayableItems = this.items - if (!this.viewControllerManager.selectionController.lastSelectedItem) { + if (!this.selectionController.lastSelectedItem) { return } - const currentIndex = displayableItems.indexOf(this.viewControllerManager.selectionController.lastSelectedItem) + const currentIndex = displayableItems.indexOf(this.selectionController.lastSelectedItem) let previousIndex = currentIndex - 1 diff --git a/app/assets/javascripts/Controllers/Navigation/TagsController.ts b/app/assets/javascripts/Controllers/Navigation/NavigationController.ts similarity index 94% rename from app/assets/javascripts/Controllers/Navigation/TagsController.ts rename to app/assets/javascripts/Controllers/Navigation/NavigationController.ts index 6e4d254b2..e56740dbc 100644 --- a/app/assets/javascripts/Controllers/Navigation/TagsController.ts +++ b/app/assets/javascripts/Controllers/Navigation/NavigationController.ts @@ -11,20 +11,20 @@ import { UuidString, isSystemView, FindItem, - DeinitSource, SystemViewId, + InternalEventBus, + InternalEventPublishStrategy, } from '@standardnotes/snjs' import { action, computed, makeAutoObservable, makeObservable, observable, runInAction } from 'mobx' import { WebApplication } from '../../Application/Application' import { FeaturesController } from '../FeaturesController' import { AbstractViewController } from '../Abstract/AbstractViewController' import { destroyAllObjectProperties } from '@/Utils' -import { ViewControllerManager } from '../../Services/ViewControllerManager/ViewControllerManager' -import { ViewControllerManagerEvent } from '../../Services/ViewControllerManager/ViewControllerManagerEvent' import { isValidFutureSiblings, rootTags, tagSiblings } from './Utils' import { AnyTag } from './AnyTagType' +import { CrossControllerEvent } from '../CrossControllerEvent' -export class TagsController extends AbstractViewController { +export class NavigationController extends AbstractViewController { tags: SNTag[] = [] smartViews: SmartView[] = [] allNotesCount_ = 0 @@ -43,13 +43,8 @@ export class TagsController extends AbstractViewController { private readonly tagsCountsState: TagsCountsState - constructor( - application: WebApplication, - override viewControllerManager: ViewControllerManager, - appEventListeners: (() => void)[], - private features: FeaturesController, - ) { - super(application) + constructor(application: WebApplication, private featuresController: FeaturesController, eventBus: InternalEventBus) { + super(application, eventBus) this.tagsCountsState = new TagsCountsState(this.application) @@ -99,7 +94,7 @@ export class TagsController extends AbstractViewController { setContextMenuMaxHeight: action, }) - appEventListeners.push( + this.disposers.push( this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => { runInAction(() => { this.tags = this.application.items.getDisplayableTags() @@ -131,7 +126,7 @@ export class TagsController extends AbstractViewController { }), ) - appEventListeners.push( + this.disposers.push( this.application.items.addNoteCountChangeObserver((tagUuid) => { if (!tagUuid) { this.setAllNotesCount(this.application.items.allCountableNotesCount()) @@ -145,15 +140,16 @@ export class TagsController extends AbstractViewController { ) } - override deinit(source: DeinitSource) { - super.deinit(source) - ;(this.features as unknown) = undefined + override deinit() { + super.deinit() + ;(this.featuresController as unknown) = undefined ;(this.tags as unknown) = undefined ;(this.smartViews as unknown) = undefined ;(this.selected_ as unknown) = undefined ;(this.previouslySelected_ as unknown) = undefined ;(this.editing_ as unknown) = undefined ;(this.addingSubtagTo as unknown) = undefined + ;(this.featuresController as unknown) = undefined destroyAllObjectProperties(this) } @@ -369,10 +365,13 @@ export class TagsController extends AbstractViewController { return } - await this.viewControllerManager.notifyEvent(ViewControllerManagerEvent.TagChanged, { - tag, - previousTag: this.previouslySelected_, - }) + await this.eventBus.publishSync( + { + type: CrossControllerEvent.TagChanged, + payload: { tag, previousTag: this.previouslySelected_ }, + }, + InternalEventPublishStrategy.SEQUENCE, + ) } public async selectHomeNavigationView(): Promise { @@ -473,8 +472,8 @@ export class TagsController extends AbstractViewController { const isSmartViewTitle = this.application.items.isSmartViewTitle(newTitle) if (isSmartViewTitle) { - if (!this.features.hasSmartViews) { - await this.features.showPremiumAlert(SMART_TAGS_FEATURE_NAME) + if (!this.featuresController.hasSmartViews) { + await this.featuresController.showPremiumAlert(SMART_TAGS_FEATURE_NAME) return } } diff --git a/app/assets/javascripts/Controllers/NoAccountWarningController.ts b/app/assets/javascripts/Controllers/NoAccountWarningController.ts index ad73be5bf..03559f580 100644 --- a/app/assets/javascripts/Controllers/NoAccountWarningController.ts +++ b/app/assets/javascripts/Controllers/NoAccountWarningController.ts @@ -1,5 +1,5 @@ import { storage, StorageKey } from '@/Services/LocalStorage' -import { ApplicationEvent } from '@standardnotes/snjs' +import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs' import { runInAction, makeObservable, observable, action } from 'mobx' import { WebApplication } from '../Application/Application' import { AbstractViewController } from './Abstract/AbstractViewController' @@ -7,17 +7,20 @@ import { AbstractViewController } from './Abstract/AbstractViewController' export class NoAccountWarningController extends AbstractViewController { show: boolean - constructor(application: WebApplication, appObservers: (() => void)[]) { - super(application) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) this.show = application.hasAccount() ? false : storage.get(StorageKey.ShowNoAccountWarning) ?? true - appObservers.push( + this.disposers.push( application.addEventObserver(async () => { runInAction(() => { this.show = false }) }, ApplicationEvent.SignedIn), + ) + + this.disposers.push( application.addEventObserver(async () => { if (application.hasAccount()) { runInAction(() => { diff --git a/app/assets/javascripts/Controllers/NoteTagsController.ts b/app/assets/javascripts/Controllers/NoteTagsController.ts index 2cca0809a..8e7e9f236 100644 --- a/app/assets/javascripts/Controllers/NoteTagsController.ts +++ b/app/assets/javascripts/Controllers/NoteTagsController.ts @@ -1,10 +1,18 @@ import { ElementIds } from '@/Constants/ElementIDs' import { destroyAllObjectProperties } from '@/Utils' -import { ApplicationEvent, ContentType, DeinitSource, PrefKey, SNNote, SNTag, UuidString } from '@standardnotes/snjs' +import { + ApplicationEvent, + ContentType, + InternalEventBus, + PrefKey, + SNNote, + SNTag, + UuidString, +} from '@standardnotes/snjs' import { action, computed, makeObservable, observable } from 'mobx' import { WebApplication } from '../Application/Application' import { AbstractViewController } from './Abstract/AbstractViewController' -import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager' +import { ItemListController } from './ItemList/ItemListController' export class NoteTagsController extends AbstractViewController { autocompleteInputFocused = false @@ -16,21 +24,19 @@ export class NoteTagsController extends AbstractViewController { tags: SNTag[] = [] tagsContainerMaxWidth: number | 'auto' = 0 addNoteToParentFolders: boolean + private itemListController!: ItemListController - override deinit(source: DeinitSource) { - super.deinit(source) + override deinit() { + super.deinit() ;(this.tags as unknown) = undefined ;(this.autocompleteTagResults as unknown) = undefined + ;(this.itemListController as unknown) = undefined destroyAllObjectProperties(this) } - constructor( - application: WebApplication, - override viewControllerManager: ViewControllerManager, - appEventListeners: (() => void)[], - ) { - super(application, viewControllerManager) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) makeObservable(this, { autocompleteInputFocused: observable, @@ -55,13 +61,17 @@ export class NoteTagsController extends AbstractViewController { }) this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true) + } - appEventListeners.push( - application.streamItems(ContentType.Tag, () => { + public setServicestPostConstruction(itemListController: ItemListController) { + this.itemListController = itemListController + + this.disposers.push( + this.application.streamItems(ContentType.Tag, () => { this.reloadTagsForCurrentNote() }), - application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => { - this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true) + this.application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => { + this.addNoteToParentFolders = this.application.getPreference(PrefKey.NoteAddToParentFolders, true) }), ) } @@ -151,7 +161,7 @@ export class NoteTagsController extends AbstractViewController { searchActiveNoteAutocompleteTags(): void { const newResults = this.application.items.searchTags( this.autocompleteSearchQuery, - this.viewControllerManager.contentListController.activeControllerNote, + this.itemListController.activeControllerNote, ) this.setAutocompleteTagResults(newResults) } @@ -161,7 +171,7 @@ export class NoteTagsController extends AbstractViewController { } reloadTagsForCurrentNote(): void { - const activeNote = this.viewControllerManager.contentListController.activeControllerNote + const activeNote = this.itemListController.activeControllerNote if (activeNote) { const tags = this.application.items.getSortedTagsForNote(activeNote) @@ -177,7 +187,7 @@ export class NoteTagsController extends AbstractViewController { } async addTagToActiveNote(tag: SNTag): Promise { - const activeNote = this.viewControllerManager.contentListController.activeControllerNote + const activeNote = this.itemListController.activeControllerNote if (activeNote) { await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders) @@ -187,7 +197,7 @@ export class NoteTagsController extends AbstractViewController { } async removeTagFromActiveNote(tag: SNTag): Promise { - const activeNote = this.viewControllerManager.contentListController.activeControllerNote + const activeNote = this.itemListController.activeControllerNote if (activeNote) { await this.application.mutator.changeItem(tag, (mutator) => { diff --git a/app/assets/javascripts/Controllers/NotesController.ts b/app/assets/javascripts/Controllers/NotesController.ts index 363c2ea8d..f4af058a2 100644 --- a/app/assets/javascripts/Controllers/NotesController.ts +++ b/app/assets/javascripts/Controllers/NotesController.ts @@ -2,11 +2,15 @@ import { destroyAllObjectProperties } from '@/Utils' import { confirmDialog } from '@/Services/AlertService' import { StringEmptyTrash, Strings, StringUtils } from '@/Constants/Strings' import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants/Constants' -import { SNNote, NoteMutator, ContentType, SNTag, DeinitSource, TagMutator } from '@standardnotes/snjs' +import { SNNote, NoteMutator, ContentType, SNTag, TagMutator, InternalEventBus } from '@standardnotes/snjs' import { makeObservable, observable, action, computed, runInAction } from 'mobx' import { WebApplication } from '../Application/Application' -import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager' import { AbstractViewController } from './Abstract/AbstractViewController' +import { SelectedItemsController } from './SelectedItemsController' +import { ItemListController } from './ItemList/ItemListController' +import { NoteTagsController } from './NoteTagsController' +import { NavigationController } from './Navigation/NavigationController' +import { CrossControllerEvent } from './CrossControllerEvent' export class NotesController extends AbstractViewController { lastSelectedNote: SNNote | undefined @@ -19,22 +23,27 @@ export class NotesController extends AbstractViewController { contextMenuMaxHeight: number | 'auto' = 'auto' showProtectedWarning = false showRevisionHistoryModal = false + private itemListController!: ItemListController - override deinit(source: DeinitSource) { - super.deinit(source) + override deinit() { + super.deinit() ;(this.lastSelectedNote as unknown) = undefined - ;(this.onActiveEditorChanged as unknown) = undefined + ;(this.selectionController as unknown) = undefined + ;(this.noteTagsController as unknown) = undefined + ;(this.navigationController as unknown) = undefined + ;(this.itemListController as unknown) = undefined destroyAllObjectProperties(this) } constructor( application: WebApplication, - public override viewControllerManager: ViewControllerManager, - private onActiveEditorChanged: () => Promise, - appEventListeners: (() => void)[], + private selectionController: SelectedItemsController, + private noteTagsController: NoteTagsController, + private navigationController: NavigationController, + eventBus: InternalEventBus, ) { - super(application, viewControllerManager) + super(application, eventBus) makeObservable(this, { contextMenuOpen: observable, @@ -55,17 +64,21 @@ export class NotesController extends AbstractViewController { setShowRevisionHistoryModal: action, unselectNotes: action, }) + } - appEventListeners.push( - application.streamItems(ContentType.Note, ({ changed, inserted, removed }) => { + public setServicestPostConstruction(itemListController: ItemListController) { + this.itemListController = itemListController + + this.disposers.push( + this.application.streamItems(ContentType.Note, ({ changed, inserted, removed }) => { runInAction(() => { for (const removedNote of removed) { - this.viewControllerManager.selectionController.deselectItem(removedNote) + this.selectionController.deselectItem(removedNote) } for (const note of [...changed, ...inserted]) { - if (this.viewControllerManager.selectionController.isItemSelected(note)) { - this.viewControllerManager.selectionController.updateReferenceOfSelectedItem(note) + if (this.selectionController.isItemSelected(note)) { + this.selectionController.updateReferenceOfSelectedItem(note) } } }) @@ -80,7 +93,7 @@ export class NotesController extends AbstractViewController { for (const selectedId of selectedUuids) { if (!activeNoteUuids.includes(selectedId)) { - this.viewControllerManager.selectionController.deselectItem({ uuid: selectedId }) + this.selectionController.deselectItem({ uuid: selectedId }) } } }), @@ -88,7 +101,7 @@ export class NotesController extends AbstractViewController { } public get selectedNotes(): SNNote[] { - return this.viewControllerManager.selectionController.getSelectedItems(ContentType.Note) + return this.selectionController.getSelectedItems(ContentType.Note) } get firstSelectedNote(): SNNote | undefined { @@ -108,7 +121,7 @@ export class NotesController extends AbstractViewController { } async openNote(noteUuid: string): Promise { - if (this.viewControllerManager.contentListController.activeControllerNote?.uuid === noteUuid) { + if (this.itemListController.activeControllerNote?.uuid === noteUuid) { return } @@ -120,13 +133,13 @@ export class NotesController extends AbstractViewController { await this.application.noteControllerGroup.createNoteController(noteUuid) - this.viewControllerManager.noteTagsController.reloadTagsForCurrentNote() + this.noteTagsController.reloadTagsForCurrentNote() - await this.onActiveEditorChanged() + await this.publishEventSync(CrossControllerEvent.ActiveEditorChanged) } async createNewNoteController(title?: string) { - const selectedTag = this.viewControllerManager.navigationController.selected + const selectedTag = this.navigationController.selected const activeRegularTagUuid = selectedTag && selectedTag instanceof SNTag ? selectedTag.uuid : undefined @@ -262,7 +275,7 @@ export class NotesController extends AbstractViewController { if (permanently) { for (const note of this.getSelectedNotesList()) { await this.application.mutator.deleteItem(note) - this.viewControllerManager.selectionController.deselectItem(note) + this.selectionController.deselectItem(note) } } else { await this.changeSelectedNotes((mutator) => { @@ -294,7 +307,7 @@ export class NotesController extends AbstractViewController { }) runInAction(() => { - this.viewControllerManager.selectionController.setSelectedItems({}) + this.selectionController.setSelectedItems({}) this.contextMenuOpen = false }) } @@ -311,11 +324,11 @@ export class NotesController extends AbstractViewController { } unselectNotes(): void { - this.viewControllerManager.selectionController.setSelectedItems({}) + this.selectionController.setSelectedItems({}) } getSpellcheckStateForNote(note: SNNote) { - return note.spellcheck != undefined ? note.spellcheck : this.viewControllerManager.isGlobalSpellcheckEnabled() + return note.spellcheck != undefined ? note.spellcheck : this.application.isGlobalSpellcheckEnabled() } async toggleGlobalSpellcheckForNote(note: SNNote) { @@ -358,7 +371,7 @@ export class NotesController extends AbstractViewController { isTagInSelectedNotes(tag: SNTag): boolean { const selectedNotes = this.getSelectedNotesList() return selectedNotes.every((note) => - this.viewControllerManager.getItemTags(note).find((noteTag) => noteTag.uuid === tag.uuid), + this.application.getItemTags(note).find((noteTag) => noteTag.uuid === tag.uuid), ) } diff --git a/app/assets/javascripts/Controllers/PurchaseFlow/PurchaseFlowController.ts b/app/assets/javascripts/Controllers/PurchaseFlow/PurchaseFlowController.ts index 7ea79a5f7..87b058304 100644 --- a/app/assets/javascripts/Controllers/PurchaseFlow/PurchaseFlowController.ts +++ b/app/assets/javascripts/Controllers/PurchaseFlow/PurchaseFlowController.ts @@ -1,4 +1,5 @@ import { loadPurchaseFlowUrl } from '@/Components/PurchaseFlow/PurchaseFlowFunctions' +import { InternalEventBus } from '@standardnotes/snjs' import { action, makeObservable, observable } from 'mobx' import { WebApplication } from '../../Application/Application' import { AbstractViewController } from '../Abstract/AbstractViewController' @@ -8,8 +9,8 @@ export class PurchaseFlowController extends AbstractViewController { isOpen = false currentPane = PurchaseFlowPane.CreateAccount - constructor(application: WebApplication) { - super(application) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) makeObservable(this, { isOpen: observable, diff --git a/app/assets/javascripts/Controllers/SearchOptionsController.ts b/app/assets/javascripts/Controllers/SearchOptionsController.ts index 661cd521e..3f7d7914e 100644 --- a/app/assets/javascripts/Controllers/SearchOptionsController.ts +++ b/app/assets/javascripts/Controllers/SearchOptionsController.ts @@ -1,4 +1,4 @@ -import { ApplicationEvent } from '@standardnotes/snjs' +import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs' import { makeObservable, observable, action, runInAction } from 'mobx' import { WebApplication } from '../Application/Application' import { AbstractViewController } from './Abstract/AbstractViewController' @@ -8,8 +8,8 @@ export class SearchOptionsController extends AbstractViewController { includeArchived = false includeTrashed = false - constructor(application: WebApplication, appObservers: (() => void)[]) { - super(application) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) makeObservable(this, { includeProtectedContents: observable, @@ -22,7 +22,7 @@ export class SearchOptionsController extends AbstractViewController { refreshIncludeProtectedContents: action, }) - appObservers.push( + this.disposers.push( this.application.addEventObserver(async () => { this.refreshIncludeProtectedContents() }, ApplicationEvent.UnprotectedSessionBegan), diff --git a/app/assets/javascripts/Controllers/SelectedItemsController.ts b/app/assets/javascripts/Controllers/SelectedItemsController.ts index a46c56afb..599b671fd 100644 --- a/app/assets/javascripts/Controllers/SelectedItemsController.ts +++ b/app/assets/javascripts/Controllers/SelectedItemsController.ts @@ -1,22 +1,35 @@ import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem' -import { ChallengeReason, ContentType, KeyboardModifier, FileItem, SNNote, UuidString } from '@standardnotes/snjs' +import { + ChallengeReason, + ContentType, + KeyboardModifier, + FileItem, + SNNote, + UuidString, + InternalEventBus, +} from '@standardnotes/snjs' import { action, computed, makeObservable, observable, runInAction } from 'mobx' import { WebApplication } from '../Application/Application' import { AbstractViewController } from './Abstract/AbstractViewController' -import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager' +import { ItemListController } from './ItemList/ItemListController' +import { NotesController } from './NotesController' type SelectedItems = Record export class SelectedItemsController extends AbstractViewController { lastSelectedItem: ListableContentItem | undefined selectedItems: SelectedItems = {} + private itemListController!: ItemListController + private notesController!: NotesController - constructor( - application: WebApplication, - override viewControllerManager: ViewControllerManager, - appObservers: (() => void)[], - ) { - super(application) + override deinit(): void { + super.deinit() + ;(this.itemListController as unknown) = undefined + ;(this.notesController as unknown) = undefined + } + + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) makeObservable(this, { selectedItems: observable, @@ -26,9 +39,14 @@ export class SelectedItemsController extends AbstractViewController { selectItem: action, setSelectedItems: action, }) + } - appObservers.push( - application.streamItems( + public setServicestPostConstruction(itemListController: ItemListController, notesController: NotesController) { + this.itemListController = itemListController + this.notesController = notesController + + this.disposers.push( + this.application.streamItems( [ContentType.Note, ContentType.File], ({ changed, inserted, removed }) => { runInAction(() => { @@ -82,7 +100,7 @@ export class SelectedItemsController extends AbstractViewController { } private selectItemsRange = async (selectedItem: ListableContentItem): Promise => { - const items = this.viewControllerManager.contentListController.renderedItems + const items = this.itemListController.renderedItems const lastSelectedItemIndex = items.findIndex((item) => item.uuid == this.lastSelectedItem?.uuid) const selectedItemIndex = items.findIndex((item) => item.uuid == selectedItem.uuid) @@ -171,7 +189,7 @@ export class SelectedItemsController extends AbstractViewController { if (this.selectedItemsCount === 1) { const item = Object.values(this.selectedItems)[0] if (item.content_type === ContentType.Note) { - await this.viewControllerManager.notesController.openNote(item.uuid) + await this.notesController.openNote(item.uuid) } } diff --git a/app/assets/javascripts/Controllers/Subscription/SubscriptionController.ts b/app/assets/javascripts/Controllers/Subscription/SubscriptionController.ts index 82d1caa6a..c10768d85 100644 --- a/app/assets/javascripts/Controllers/Subscription/SubscriptionController.ts +++ b/app/assets/javascripts/Controllers/Subscription/SubscriptionController.ts @@ -3,7 +3,7 @@ import { ApplicationEvent, ClientDisplayableError, convertTimestampToMilliseconds, - DeinitSource, + InternalEventBus, } from '@standardnotes/snjs' import { action, computed, makeObservable, observable } from 'mobx' import { WebApplication } from '../../Application/Application' @@ -15,16 +15,16 @@ export class SubscriptionController extends AbstractViewController { userSubscription: Subscription | undefined = undefined availableSubscriptions: AvailableSubscriptions | undefined = undefined - override deinit(source: DeinitSource) { - super.deinit(source) + override deinit() { + super.deinit() ;(this.userSubscription as unknown) = undefined ;(this.availableSubscriptions as unknown) = undefined destroyAllObjectProperties(this) } - constructor(application: WebApplication, appObservers: (() => void)[]) { - super(application) + constructor(application: WebApplication, eventBus: InternalEventBus) { + super(application, eventBus) makeObservable(this, { userSubscription: observable, @@ -39,15 +39,21 @@ export class SubscriptionController extends AbstractViewController { setAvailableSubscriptions: action, }) - appObservers.push( + this.disposers.push( application.addEventObserver(async () => { if (application.hasAccount()) { this.getSubscriptionInfo().catch(console.error) } }, ApplicationEvent.Launched), + ) + + this.disposers.push( application.addEventObserver(async () => { this.getSubscriptionInfo().catch(console.error) }, ApplicationEvent.SignedIn), + ) + + this.disposers.push( application.addEventObserver(async () => { this.getSubscriptionInfo().catch(console.error) }, ApplicationEvent.UserRolesChanged), diff --git a/app/assets/javascripts/Services/DesktopManager.ts b/app/assets/javascripts/Services/DesktopManager.ts index 20a991513..4126aad15 100644 --- a/app/assets/javascripts/Services/DesktopManager.ts +++ b/app/assets/javascripts/Services/DesktopManager.ts @@ -13,7 +13,8 @@ import { DesktopClientRequiresWebMethods, DesktopDeviceInterface, } from '@standardnotes/snjs' -import { WebAppEvent, WebApplication } from '@/Application/Application' +import { WebApplication } from '@/Application/Application' +import { WebAppEvent } from '@/Application/WebAppEvent' export class DesktopManager extends ApplicationService @@ -106,11 +107,11 @@ export class DesktopManager } windowGainedFocus(): void { - this.webApplication.notifyWebEvent(WebAppEvent.DesktopWindowGainedFocus) + this.webApplication.notifyWebEvent(WebAppEvent.WindowDidFocus) } windowLostFocus(): void { - this.webApplication.notifyWebEvent(WebAppEvent.DesktopWindowLostFocus) + this.webApplication.notifyWebEvent(WebAppEvent.WindowDidBlur) } async onComponentInstallationComplete(componentData: DecryptedTransferPayload, error: unknown) { @@ -155,10 +156,10 @@ export class DesktopManager } didBeginBackup() { - this.webApplication.getViewControllerManager().beganBackupDownload() + this.webApplication.notifyWebEvent(WebAppEvent.BeganBackupDownload) } didFinishBackup(success: boolean) { - this.webApplication.getViewControllerManager().endedBackupDownload(success) + this.webApplication.notifyWebEvent(WebAppEvent.EndedBackupDownload, { success }) } } diff --git a/app/assets/javascripts/Services/ViewControllerManager.ts b/app/assets/javascripts/Services/ViewControllerManager.ts new file mode 100644 index 000000000..06a7d00fe --- /dev/null +++ b/app/assets/javascripts/Services/ViewControllerManager.ts @@ -0,0 +1,204 @@ +import { storage, StorageKey } from '@/Services/LocalStorage' +import { WebApplication } from '@/Application/Application' +import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController' +import { destroyAllObjectProperties } from '@/Utils' +import { ApplicationEvent, DeinitSource, WebOrDesktopDeviceInterface, InternalEventBus } from '@standardnotes/snjs' +import { action, makeObservable, observable } from 'mobx' +import { ActionsMenuController } from '../Controllers/ActionsMenuController' +import { FeaturesController } from '../Controllers/FeaturesController' +import { FilesController } from '../Controllers/FilesController' +import { NotesController } from '../Controllers/NotesController' +import { ItemListController } from '../Controllers/ItemList/ItemListController' +import { NoteTagsController } from '../Controllers/NoteTagsController' +import { NoAccountWarningController } from '../Controllers/NoAccountWarningController' +import { PreferencesController } from '../Controllers/PreferencesController' +import { PurchaseFlowController } from '../Controllers/PurchaseFlow/PurchaseFlowController' +import { QuickSettingsController } from '../Controllers/QuickSettingsController' +import { SearchOptionsController } from '../Controllers/SearchOptionsController' +import { SubscriptionController } from '../Controllers/Subscription/SubscriptionController' +import { SyncStatusController } from '../Controllers/SyncStatusController' +import { NavigationController } from '../Controllers/Navigation/NavigationController' +import { FilePreviewModalController } from '../Controllers/FilePreviewModalController' +import { SelectedItemsController } from '../Controllers/SelectedItemsController' + +export class ViewControllerManager { + readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures + + private unsubAppEventObserver!: () => void + showBetaWarning: boolean + public dealloced = false + + readonly accountMenuController: AccountMenuController + readonly actionsMenuController = new ActionsMenuController() + readonly featuresController: FeaturesController + readonly filePreviewModalController = new FilePreviewModalController() + readonly filesController: FilesController + readonly noAccountWarningController: NoAccountWarningController + readonly notesController: NotesController + readonly itemListController: ItemListController + readonly noteTagsController: NoteTagsController + readonly preferencesController = new PreferencesController() + readonly purchaseFlowController: PurchaseFlowController + readonly quickSettingsMenuController = new QuickSettingsController() + readonly searchOptionsController: SearchOptionsController + readonly subscriptionController: SubscriptionController + readonly syncStatusController = new SyncStatusController() + readonly navigationController: NavigationController + readonly selectionController: SelectedItemsController + + public isSessionsModalVisible = false + + private appEventObserverRemovers: (() => void)[] = [] + private eventBus: InternalEventBus + + constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) { + this.eventBus = new InternalEventBus() + + this.selectionController = new SelectedItemsController(application, this.eventBus) + + this.noteTagsController = new NoteTagsController(application, this.eventBus) + + this.featuresController = new FeaturesController(application, this.eventBus) + + this.navigationController = new NavigationController(application, this.featuresController, this.eventBus) + + this.notesController = new NotesController( + application, + this.selectionController, + this.noteTagsController, + this.navigationController, + this.eventBus, + ) + + this.searchOptionsController = new SearchOptionsController(application, this.eventBus) + + this.itemListController = new ItemListController( + application, + this.navigationController, + this.searchOptionsController, + this.selectionController, + this.notesController, + this.noteTagsController, + this.eventBus, + ) + + this.notesController.setServicestPostConstruction(this.itemListController) + this.noteTagsController.setServicestPostConstruction(this.itemListController) + this.selectionController.setServicestPostConstruction(this.itemListController, this.notesController) + + this.noAccountWarningController = new NoAccountWarningController(application, this.eventBus) + + this.accountMenuController = new AccountMenuController(application, this.eventBus) + + this.subscriptionController = new SubscriptionController(application, this.eventBus) + + this.purchaseFlowController = new PurchaseFlowController(application, this.eventBus) + + this.filesController = new FilesController( + application, + this.notesController, + this.selectionController, + this.filePreviewModalController, + this.eventBus, + ) + + this.addAppEventObserver() + + if (this.device.appVersion.includes('-beta')) { + this.showBetaWarning = storage.get(StorageKey.ShowBetaWarning) ?? true + } else { + this.showBetaWarning = false + } + + makeObservable(this, { + showBetaWarning: observable, + isSessionsModalVisible: observable, + preferencesController: observable, + + openSessionsModal: action, + closeSessionsModal: action, + }) + } + + deinit(source: DeinitSource): void { + this.dealloced = true + ;(this.application as unknown) = undefined + + if (source === DeinitSource.SignOut) { + storage.remove(StorageKey.ShowBetaWarning) + this.noAccountWarningController.reset() + } + + this.unsubAppEventObserver?.() + ;(this.unsubAppEventObserver as unknown) = undefined + + this.appEventObserverRemovers.forEach((remover) => remover()) + this.appEventObserverRemovers.length = 0 + ;(this.device as unknown) = undefined + ;(this.filePreviewModalController as unknown) = undefined + ;(this.preferencesController as unknown) = undefined + ;(this.quickSettingsMenuController as unknown) = undefined + ;(this.syncStatusController as unknown) = undefined + + this.actionsMenuController.reset() + ;(this.actionsMenuController as unknown) = undefined + + this.featuresController.deinit() + ;(this.featuresController as unknown) = undefined + + this.accountMenuController.deinit() + ;(this.accountMenuController as unknown) = undefined + + this.filesController.deinit() + ;(this.filesController as unknown) = undefined + + this.noAccountWarningController.deinit() + ;(this.noAccountWarningController as unknown) = undefined + + this.notesController.deinit() + ;(this.notesController as unknown) = undefined + + this.itemListController.deinit() + ;(this.itemListController as unknown) = undefined + + this.noteTagsController.deinit() + ;(this.noteTagsController as unknown) = undefined + + this.purchaseFlowController.deinit() + ;(this.purchaseFlowController as unknown) = undefined + + this.searchOptionsController.deinit() + ;(this.searchOptionsController as unknown) = undefined + + this.subscriptionController.deinit() + ;(this.subscriptionController as unknown) = undefined + + this.navigationController.deinit() + ;(this.navigationController as unknown) = undefined + + destroyAllObjectProperties(this) + } + + openSessionsModal(): void { + this.isSessionsModalVisible = true + } + + closeSessionsModal(): void { + this.isSessionsModalVisible = false + } + + addAppEventObserver() { + this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => { + switch (eventName) { + case ApplicationEvent.Launched: + if (window.location.search.includes('purchase=true')) { + this.purchaseFlowController.openPurchaseFlow() + } + break + case ApplicationEvent.SyncStatusChanged: + this.syncStatusController.update(this.application.sync.getSyncStatus()) + break + } + }) + } +} diff --git a/app/assets/javascripts/Services/ViewControllerManager/ViewControllerManager.ts b/app/assets/javascripts/Services/ViewControllerManager/ViewControllerManager.ts deleted file mode 100644 index 2621abaae..000000000 --- a/app/assets/javascripts/Services/ViewControllerManager/ViewControllerManager.ts +++ /dev/null @@ -1,298 +0,0 @@ -import { storage, StorageKey } from '@/Services/LocalStorage' -import { WebApplication, WebAppEvent } from '@/Application/Application' -import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController' -import { destroyAllObjectProperties, isDesktopApplication } from '@/Utils' -import { - ApplicationEvent, - ContentType, - DeinitSource, - PrefKey, - SNTag, - removeFromArray, - WebOrDesktopDeviceInterface, -} from '@standardnotes/snjs' -import { action, makeObservable, observable } from 'mobx' -import { ActionsMenuController } from '../../Controllers/ActionsMenuController' -import { FeaturesController } from '../../Controllers/FeaturesController' -import { FilesController } from '../../Controllers/FilesController' -import { NotesController } from '../../Controllers/NotesController' -import { ItemListController } from '../../Controllers/ItemList/ItemListController' -import { NoteTagsController } from '../../Controllers/NoteTagsController' -import { NoAccountWarningController } from '../../Controllers/NoAccountWarningController' -import { PreferencesController } from '../../Controllers/PreferencesController' -import { PurchaseFlowController } from '../../Controllers/PurchaseFlow/PurchaseFlowController' -import { QuickSettingsController } from '../../Controllers/QuickSettingsController' -import { SearchOptionsController } from '../../Controllers/SearchOptionsController' -import { SubscriptionController } from '../../Controllers/Subscription/SubscriptionController' -import { SyncStatusController } from '../../Controllers/SyncStatusController' -import { TagsController } from '../../Controllers/Navigation/TagsController' -import { FilePreviewModalController } from '../../Controllers/FilePreviewModalController' -import { SelectedItemsController } from '../../Controllers/SelectedItemsController' -import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem' -import { ViewControllerManagerEvent } from './ViewControllerManagerEvent' -import { EditorEventSource } from '../../Types/EditorEventSource' -import { PanelResizedData } from '../../Types/PanelResizedData' - -type ObserverCallback = (event: ViewControllerManagerEvent, data?: unknown) => Promise - -export class ViewControllerManager { - readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures - - observers: ObserverCallback[] = [] - locked = true - unsubAppEventObserver!: () => void - webAppEventDisposer?: () => void - onVisibilityChange: () => void - showBetaWarning: boolean - dealloced = false - - readonly accountMenuController: AccountMenuController - readonly actionsMenuController = new ActionsMenuController() - readonly featuresController: FeaturesController - readonly filePreviewModalController = new FilePreviewModalController() - readonly filesController: FilesController - readonly noAccountWarningController: NoAccountWarningController - readonly notesController: NotesController - readonly contentListController: ItemListController - readonly noteTagsController: NoteTagsController - readonly preferencesController = new PreferencesController() - readonly purchaseFlowController: PurchaseFlowController - readonly quickSettingsMenuController = new QuickSettingsController() - readonly searchOptionsController: SearchOptionsController - readonly subscriptionController: SubscriptionController - readonly syncStatusController = new SyncStatusController() - readonly navigationController: TagsController - readonly selectionController: SelectedItemsController - - isSessionsModalVisible = false - - private appEventObserverRemovers: (() => void)[] = [] - - constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) { - this.selectionController = new SelectedItemsController(application, this, this.appEventObserverRemovers) - this.notesController = new NotesController( - application, - this, - async () => { - await this.notifyEvent(ViewControllerManagerEvent.ActiveEditorChanged) - }, - this.appEventObserverRemovers, - ) - this.featuresController = new FeaturesController(application, this.appEventObserverRemovers) - this.navigationController = new TagsController( - application, - this, - this.appEventObserverRemovers, - this.featuresController, - ) - this.searchOptionsController = new SearchOptionsController(application, this.appEventObserverRemovers) - this.contentListController = new ItemListController(application, this, this.appEventObserverRemovers) - this.noteTagsController = new NoteTagsController(application, this, this.appEventObserverRemovers) - this.noAccountWarningController = new NoAccountWarningController(application, this.appEventObserverRemovers) - this.accountMenuController = new AccountMenuController(application, this.appEventObserverRemovers) - this.subscriptionController = new SubscriptionController(application, this.appEventObserverRemovers) - this.purchaseFlowController = new PurchaseFlowController(application) - this.filesController = new FilesController(application, this, this.appEventObserverRemovers) - this.addAppEventObserver() - this.onVisibilityChange = () => { - const visible = document.visibilityState === 'visible' - const event = visible ? ViewControllerManagerEvent.WindowDidFocus : ViewControllerManagerEvent.WindowDidBlur - this.notifyEvent(event).catch(console.error) - } - this.registerVisibilityObservers() - - if (this.device.appVersion.includes('-beta')) { - this.showBetaWarning = storage.get(StorageKey.ShowBetaWarning) ?? true - } else { - this.showBetaWarning = false - } - - makeObservable(this, { - showBetaWarning: observable, - isSessionsModalVisible: observable, - preferencesController: observable, - - enableBetaWarning: action, - disableBetaWarning: action, - openSessionsModal: action, - closeSessionsModal: action, - }) - } - - deinit(source: DeinitSource): void { - this.dealloced = true - ;(this.application as unknown) = undefined - - if (source === DeinitSource.SignOut) { - storage.remove(StorageKey.ShowBetaWarning) - this.noAccountWarningController.reset() - } - - this.unsubAppEventObserver?.() - ;(this.unsubAppEventObserver as unknown) = undefined - this.observers.length = 0 - - this.appEventObserverRemovers.forEach((remover) => remover()) - this.appEventObserverRemovers.length = 0 - ;(this.device as unknown) = undefined - - this.webAppEventDisposer?.() - this.webAppEventDisposer = undefined - ;(this.filePreviewModalController as unknown) = undefined - ;(this.preferencesController as unknown) = undefined - ;(this.quickSettingsMenuController as unknown) = undefined - ;(this.syncStatusController as unknown) = undefined - - this.actionsMenuController.reset() - ;(this.actionsMenuController as unknown) = undefined - - this.featuresController.deinit(source) - ;(this.featuresController as unknown) = undefined - - this.accountMenuController.deinit(source) - ;(this.accountMenuController as unknown) = undefined - - this.filesController.deinit(source) - ;(this.filesController as unknown) = undefined - - this.noAccountWarningController.deinit(source) - ;(this.noAccountWarningController as unknown) = undefined - - this.notesController.deinit(source) - ;(this.notesController as unknown) = undefined - - this.contentListController.deinit(source) - ;(this.contentListController as unknown) = undefined - - this.noteTagsController.deinit(source) - ;(this.noteTagsController as unknown) = undefined - - this.purchaseFlowController.deinit(source) - ;(this.purchaseFlowController as unknown) = undefined - - this.searchOptionsController.deinit(source) - ;(this.searchOptionsController as unknown) = undefined - - this.subscriptionController.deinit(source) - ;(this.subscriptionController as unknown) = undefined - - this.navigationController.deinit(source) - ;(this.navigationController as unknown) = undefined - - document.removeEventListener('visibilitychange', this.onVisibilityChange) - ;(this.onVisibilityChange as unknown) = undefined - - destroyAllObjectProperties(this) - } - - openSessionsModal(): void { - this.isSessionsModalVisible = true - } - - closeSessionsModal(): void { - this.isSessionsModalVisible = false - } - - disableBetaWarning() { - this.showBetaWarning = false - storage.set(StorageKey.ShowBetaWarning, false) - } - - enableBetaWarning() { - this.showBetaWarning = true - storage.set(StorageKey.ShowBetaWarning, true) - } - - public get version(): string { - return this.device.appVersion - } - - isGlobalSpellcheckEnabled(): boolean { - return this.application.getPreference(PrefKey.EditorSpellcheck, true) - } - - async toggleGlobalSpellcheck() { - const currentValue = this.isGlobalSpellcheckEnabled() - return this.application.setPreference(PrefKey.EditorSpellcheck, !currentValue) - } - - addAppEventObserver() { - this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => { - switch (eventName) { - case ApplicationEvent.Started: - this.locked = true - break - case ApplicationEvent.Launched: - this.locked = false - if (window.location.search.includes('purchase=true')) { - this.purchaseFlowController.openPurchaseFlow() - } - break - case ApplicationEvent.SyncStatusChanged: - this.syncStatusController.update(this.application.sync.getSyncStatus()) - break - } - }) - } - - isLocked() { - return this.locked - } - - registerVisibilityObservers() { - if (isDesktopApplication()) { - this.webAppEventDisposer = this.application.addWebEventObserver((event) => { - if (event === WebAppEvent.DesktopWindowGainedFocus) { - this.notifyEvent(ViewControllerManagerEvent.WindowDidFocus).catch(console.error) - } else if (event === WebAppEvent.DesktopWindowLostFocus) { - this.notifyEvent(ViewControllerManagerEvent.WindowDidBlur).catch(console.error) - } - }) - } else { - /* Tab visibility listener, web only */ - document.addEventListener('visibilitychange', this.onVisibilityChange) - } - } - - addObserver(callback: ObserverCallback): () => void { - this.observers.push(callback) - - const thislessObservers = this.observers - return () => { - removeFromArray(thislessObservers, callback) - } - } - - async notifyEvent(eventName: ViewControllerManagerEvent, data?: unknown) { - for (const callback of this.observers) { - await callback(eventName, data) - } - } - - /** Returns the tags that are referncing this note */ - public getItemTags(item: ListableContentItem) { - return this.application.items.itemsReferencingItem(item).filter((ref) => { - return ref.content_type === ContentType.Tag - }) as SNTag[] - } - - panelDidResize(name: string, collapsed: boolean) { - const data: PanelResizedData = { - panel: name, - collapsed: collapsed, - } - this.notifyEvent(ViewControllerManagerEvent.PanelResized, data).catch(console.error) - } - - editorDidFocus(eventSource: EditorEventSource) { - this.notifyEvent(ViewControllerManagerEvent.EditorFocused, { eventSource: eventSource }).catch(console.error) - } - - beganBackupDownload() { - this.notifyEvent(ViewControllerManagerEvent.BeganBackupDownload).catch(console.error) - } - - endedBackupDownload(success: boolean) { - this.notifyEvent(ViewControllerManagerEvent.EndedBackupDownload, { success: success }).catch(console.error) - } -} diff --git a/app/assets/javascripts/Services/ViewControllerManager/ViewControllerManagerEvent.ts b/app/assets/javascripts/Services/ViewControllerManager/ViewControllerManagerEvent.ts deleted file mode 100644 index 540b76f3b..000000000 --- a/app/assets/javascripts/Services/ViewControllerManager/ViewControllerManagerEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -export enum ViewControllerManagerEvent { - TagChanged, - ActiveEditorChanged, - PanelResized, - EditorFocused, - BeganBackupDownload, - EndedBackupDownload, - WindowDidFocus, - WindowDidBlur, -} diff --git a/app/assets/javascripts/Services/ViewControllerManager/index.ts b/app/assets/javascripts/Services/ViewControllerManager/index.ts deleted file mode 100644 index d3517e9a1..000000000 --- a/app/assets/javascripts/Services/ViewControllerManager/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ViewControllerManager' -export * from './ViewControllerManagerEvent' diff --git a/app/assets/javascripts/Types/Disposer.ts b/app/assets/javascripts/Types/Disposer.ts new file mode 100644 index 000000000..7bb1de228 --- /dev/null +++ b/app/assets/javascripts/Types/Disposer.ts @@ -0,0 +1 @@ +export type Disposer = () => void diff --git a/package.json b/package.json index 35cd85db2..0b48aa458 100644 --- a/package.json +++ b/package.json @@ -73,10 +73,10 @@ "@standardnotes/components": "1.8.2", "@standardnotes/filepicker": "1.16.2", "@standardnotes/icons": "^1.1.8", - "@standardnotes/services": "^1.13.3", "@standardnotes/sncrypto-web": "1.10.1", "@standardnotes/snjs": "^2.114.5", "@standardnotes/stylekit": "5.29.3", + "@standardnotes/services": "^1.13.6", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index 5274ec156..3b1fc927e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2366,6 +2366,14 @@ "@standardnotes/common" "^1.22.0" jsonwebtoken "^8.5.1" +"@standardnotes/auth@^3.19.2": + version "3.19.2" + resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.19.2.tgz#a2758cdde588190eebd30e567fe7756c6823adcd" + integrity sha512-m7MvSN2BHHh8+FZ3tUe6IpoPGzu/I1lmQF8snlYfuBmSxVdwVLTYhIbFAjfi4PST/Rx3FXnaoMnfJSR0k+OmWw== + dependencies: + "@standardnotes/common" "^1.22.0" + jsonwebtoken "^8.5.1" + "@standardnotes/common@^1.22.0": version "1.22.0" resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.22.0.tgz#397604fb4b92901bac276940a2647509b70a7ad2" @@ -2420,6 +2428,14 @@ "@standardnotes/auth" "^3.19.1" "@standardnotes/common" "^1.22.0" +"@standardnotes/features@^1.44.6": + version "1.44.6" + resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.44.6.tgz#1a7872a7e79026a553d3670f8610b2b79c1f5fa4" + integrity sha512-iP0oR4bb16Rx0kSspl0R8rSKY38hF59ExaoMIREf0MGH8WLjwDJyILafGfhxv8NjMeRtpIIXKbK+TokM6A5ZXg== + dependencies: + "@standardnotes/auth" "^3.19.2" + "@standardnotes/common" "^1.22.0" + "@standardnotes/filepicker@1.16.2": version "1.16.2" resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.2.tgz#d6fda94b5578f30e6b4f792c874e3eb11ce58453" @@ -2473,6 +2489,15 @@ "@standardnotes/responses" "^1.6.28" "@standardnotes/utils" "^1.6.10" +"@standardnotes/models@^1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.11.1.tgz#d622b7cffd7ee4faebcd564d7ae880b372ec7be8" + integrity sha512-XKXoV8Pi5iuzrjUGWs4grvxn3m2BQyt49Br+euToOkgvZvW5HIiaCGLwAtvP5S3d3ecgD5EvbLzGJIFEs5F4rw== + dependencies: + "@standardnotes/features" "^1.44.6" + "@standardnotes/responses" "^1.6.29" + "@standardnotes/utils" "^1.6.10" + "@standardnotes/responses@^1.6.27": version "1.6.27" resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.27.tgz#3a440090e5cee09f2980df5cc57a60f76adff735" @@ -2491,6 +2516,15 @@ "@standardnotes/common" "^1.22.0" "@standardnotes/features" "^1.44.5" +"@standardnotes/responses@^1.6.29": + version "1.6.29" + resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.29.tgz#fac5875bb84e382d7b1acf14c0af77876fe5c41d" + integrity sha512-BWrkR6gIWD+dC9/a+ii/pDzWtIlAsgZWICTwKZ34jBgjPSO1svewcHzXwBXILKZd6JvrUf6pglmSt3I9fmsHaQ== + dependencies: + "@standardnotes/auth" "^3.19.2" + "@standardnotes/common" "^1.22.0" + "@standardnotes/features" "^1.44.6" + "@standardnotes/services@^1.13.3": version "1.13.3" resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.3.tgz#b857a6ed42b15d40c2e9d08b41739f5fb6b0d3e0" @@ -2513,6 +2547,17 @@ "@standardnotes/responses" "^1.6.28" "@standardnotes/utils" "^1.6.10" +"@standardnotes/services@^1.13.6": + version "1.13.6" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.6.tgz#9ab4d0c3ed0ad3693ba54ac360f4fdaad08f7410" + integrity sha512-Vt/hptzJK4D6qUPp/cdhOLlRT57uf3CDjDUwyfnELXDyU8mFTVvS/M1deD1PecNWodsfLH7aDWeMWTKApP6LKg== + dependencies: + "@standardnotes/auth" "^3.19.2" + "@standardnotes/common" "^1.22.0" + "@standardnotes/models" "^1.11.1" + "@standardnotes/responses" "^1.6.29" + "@standardnotes/utils" "^1.6.10" + "@standardnotes/settings@^1.14.3": version "1.14.3" resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.14.3.tgz#021085e8c383a9893a2c49daa74cc0754ccd67b5" From ed5f6495aaa841744cbfb70a2f644c77c545e93e Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 2 Jun 2022 10:48:56 -0500 Subject: [PATCH 37/39] fix: file backups drag --- .../Panes/Backups/Files/BackupsDropZone.tsx | 66 ++---- .../Files/FileBackupsCrossPlatform.tsx | 2 + .../Backups/Files/FileBackupsDesktop.tsx | 6 +- package.json | 6 +- yarn.lock | 197 +++++------------- 5 files changed, 79 insertions(+), 198 deletions(-) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx index f406a2263..440d722ae 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx @@ -5,7 +5,6 @@ import { FileBackupMetadataFile, FileBackupsConstantsV1, FileItem, FileHandleRea import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' import Icon from '@/Components/Icon/Icon' import { StreamingFileApi } from '@standardnotes/filepicker' -import { isHandlingBackupDrag } from '@/Utils/DragTypeCheck' import { WebApplication } from '@/Application/Application' import EncryptionStatusItem from '../../Security/EncryptionStatusItem' import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment' @@ -53,77 +52,52 @@ const BackupsDropZone: FunctionComponent = ({ application }) => { void application.alertService.alert( `${decryptedFileItem.name} has been successfully decrypted and saved to your chosen directory.`, ) - setBinaryFile(undefined) - setDecryptedFileItem(undefined) - setDroppedFile(undefined) } else if (result === 'failed') { void application.alertService.alert( 'Unable to save file to local directory. This may be caused by failure to decrypt, or failure to save the file locally.', ) } + setBinaryFile(undefined) + setDecryptedFileItem(undefined) + setDroppedFile(undefined) setIsSavingAsDecrypted(false) }, [decryptedFileItem, application, binaryFile, fileSystem]) - const handleDrag = useCallback( - (event: DragEvent) => { - if (isHandlingBackupDrag(event, application)) { - event.preventDefault() - event.stopPropagation() - } - }, - [application], - ) + const handleDragOver = useCallback((event: DragEvent) => { + event.stopPropagation() + }, []) - const handleDragIn = useCallback( - (event: DragEvent) => { - if (!isHandlingBackupDrag(event, application)) { - return - } + const handleDragIn = useCallback((event: DragEvent) => { + event.stopPropagation() + }, []) - event.preventDefault() - event.stopPropagation() - }, - [application], - ) - - const handleDragOut = useCallback( - (event: DragEvent) => { - if (!isHandlingBackupDrag(event, application)) { - return - } - - event.preventDefault() - event.stopPropagation() - }, - [application], - ) + const handleDragOut = useCallback((event: DragEvent) => { + event.stopPropagation() + }, []) const handleDrop = useCallback( async (event: DragEvent) => { - if (!isHandlingBackupDrag(event, application)) { - return - } - event.preventDefault() event.stopPropagation() const items = event.dataTransfer?.items - if (!items || items.length === 0) { return } const item = items[0] const file = item.getAsFile() - if (!file) { return } const text = await file.text() - const type = application.files.isFileNameFileBackupRelated(file.name) + if (type === false) { + return + } + if (type === 'binary') { void application.alertService.alert('Please drag the metadata file instead of the encrypted data file.') return @@ -144,16 +118,16 @@ const BackupsDropZone: FunctionComponent = ({ application }) => { useEffect(() => { window.addEventListener('dragenter', handleDragIn) window.addEventListener('dragleave', handleDragOut) - window.addEventListener('dragover', handleDrag) + window.addEventListener('dragover', handleDragOver) window.addEventListener('drop', handleDrop) return () => { window.removeEventListener('dragenter', handleDragIn) window.removeEventListener('dragleave', handleDragOut) - window.removeEventListener('dragover', handleDrag) + window.removeEventListener('dragover', handleDragOver) window.removeEventListener('drop', handleDrop) } - }, [handleDragIn, handleDrop, handleDrag, handleDragOut]) + }, [handleDragIn, handleDrop, handleDragOver, handleDragOut]) if (!droppedFile) { return ( @@ -174,7 +148,7 @@ const BackupsDropZone: FunctionComponent = ({ application }) => { ]} + icon={} checkmark={true} /> diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx index 9e95dde16..011b08d6e 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsCrossPlatform.tsx @@ -5,6 +5,7 @@ import { WebApplication } from '@/Application/Application' import { useMemo } from 'react' import BackupsDropZone from './BackupsDropZone' import FileBackupsDesktop from './FileBackupsDesktop' +import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator' type Props = { application: WebApplication @@ -23,6 +24,7 @@ const FileBackupsCrossPlatform = ({ application }: Props) => { Automatically save encrypted backups of files uploaded to any device to this computer. To enable file backups, use the Standard Notes desktop application. + diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx index 2a57d4106..e4aa39118 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx @@ -73,6 +73,8 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => { )} + + {backupsEnabled && ( <> @@ -84,7 +86,7 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => { ]} + icon={} checkmark={false} /> @@ -107,6 +109,8 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => { )} + + diff --git a/package.json b/package.json index 0b48aa458..734c9564b 100644 --- a/package.json +++ b/package.json @@ -71,12 +71,12 @@ "@reach/tooltip": "^0.16.2", "@reach/visually-hidden": "^0.16.0", "@standardnotes/components": "1.8.2", - "@standardnotes/filepicker": "1.16.2", + "@standardnotes/filepicker": "1.16.8", "@standardnotes/icons": "^1.1.8", "@standardnotes/sncrypto-web": "1.10.1", - "@standardnotes/snjs": "^2.114.5", + "@standardnotes/snjs": "^2.114.11", "@standardnotes/stylekit": "5.29.3", - "@standardnotes/services": "^1.13.6", + "@standardnotes/services": "^1.13.8", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index 3b1fc927e..06a4d2a02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2350,22 +2350,6 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@standardnotes/auth@^3.19.0": - version "3.19.0" - resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.19.0.tgz#ca0ce3494fb147906b5d0f5c6cafe494f2f3a590" - integrity sha512-4ZieJuhlhqVQ7nwKjVdNubhEZkoHEaI5kW4wHdCcRNXCVofzHBctPdBSaRHzriJTUNSWfK66H0i5MOoU68etRQ== - dependencies: - "@standardnotes/common" "^1.22.0" - jsonwebtoken "^8.5.1" - -"@standardnotes/auth@^3.19.1": - version "3.19.1" - resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.19.1.tgz#09b1bf2a7c22bfb351b3f5af5a193566bfe4410c" - integrity sha512-ozzCan/uSdN8oQ2YSid5kVGts+13f63eKMyuiGesS1a/l0ezKWJHaFa3ulXETobhjJEF60yNQIjQ7sfdv+4L9A== - dependencies: - "@standardnotes/common" "^1.22.0" - jsonwebtoken "^8.5.1" - "@standardnotes/auth@^3.19.2": version "3.19.2" resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.19.2.tgz#a2758cdde588190eebd30e567fe7756c6823adcd" @@ -2395,38 +2379,22 @@ eslint-plugin-prettier "^4.0.0" prettier "^2.6.2" -"@standardnotes/domain-events@^2.28.14": - version "2.28.14" - resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.28.14.tgz#58de063de3ba93d016bdb7169b978d43b9c43df8" - integrity sha512-rMGQie66EK09AqRmxXOcVQSILt0TXdOGeVTRkKjZzGpAM+Y8YpOfG0/WOkBsB4pyMWBrSJvMA1aQtl7buslDrw== +"@standardnotes/domain-events@^2.29.0": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.29.0.tgz#75acb29533d21086daaa4a8cc0ad40072bd4e57e" + integrity sha512-KOXvFDlUwybiOA8E5/1HAUFOV+s68mcRF7vd6sQH6dxkeRm6vCjXAufduh4DboyZHaLeYJeZLsw5ssO43IpFoQ== dependencies: - "@standardnotes/auth" "^3.19.1" - "@standardnotes/features" "^1.44.5" + "@standardnotes/auth" "^3.19.2" + "@standardnotes/features" "^1.44.6" -"@standardnotes/encryption@^1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.8.5.tgz#48ea926e9c707e9d27e4c7698c634b5bfa85d662" - integrity sha512-Ih6HCnreQNkR8YTmcem2PvKtxTUadDYx20Tp4UF7TSpeaUL4ETLipamJ6aqYasKYeuf4uuoiQb7LLYiad37NyQ== +"@standardnotes/encryption@^1.8.8": + version "1.8.8" + resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.8.8.tgz#835bbf083a86c8b683f2599bd29124527f76797c" + integrity sha512-lvXe1r+JAzInRmP8wG+rAiPzCPze3rNGMmz9ddZxPtOVXz+WOMfSSGKlWoAQJ0SxdmNPJUyRH3ydewLlYuubaw== dependencies: - "@standardnotes/models" "^1.11.0" - "@standardnotes/responses" "^1.6.28" - "@standardnotes/services" "^1.13.5" - -"@standardnotes/features@^1.44.4": - version "1.44.4" - resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.44.4.tgz#9f8bd5d1ad6c67e0edccc92720928d62825e0fb5" - integrity sha512-85J1M3Snq/6wVz4ysmWAw6iDIdSmDRjkhwA8sq/EMY+cRTHcD4OU/oT0otU+fFfSJ7VEp1fzLeciH+SvFvajqw== - dependencies: - "@standardnotes/auth" "^3.19.0" - "@standardnotes/common" "^1.22.0" - -"@standardnotes/features@^1.44.5": - version "1.44.5" - resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.44.5.tgz#c1dd2d8b46387765c7bef76ee916b0698c1153b5" - integrity sha512-yMh9W6KP/HfqEJEGh469Vh6PlH/U0GSif4amt5paBIdhvZt2Rmqv81wt+ac9kPp7HkDxZhr8+Ly68OVznCeVjw== - dependencies: - "@standardnotes/auth" "^3.19.1" - "@standardnotes/common" "^1.22.0" + "@standardnotes/models" "^1.11.2" + "@standardnotes/responses" "^1.6.29" + "@standardnotes/services" "^1.13.8" "@standardnotes/features@^1.44.6": version "1.44.6" @@ -2436,34 +2404,25 @@ "@standardnotes/auth" "^3.19.2" "@standardnotes/common" "^1.22.0" -"@standardnotes/filepicker@1.16.2": - version "1.16.2" - resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.2.tgz#d6fda94b5578f30e6b4f792c874e3eb11ce58453" - integrity sha512-B290nRn2aJzVX1VXMTHhu66tbj1oZFaksTSLQxiEWeLQ76AFAQ3/5frVH/6Y8ahIvBn3XYyotOOyXeL9HkBu8g== +"@standardnotes/filepicker@1.16.8", "@standardnotes/filepicker@^1.16.8": + version "1.16.8" + resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.8.tgz#2ea7d2eaacf60e033efdf4e430a7ccfd8fd0d658" + integrity sha512-9QEeAbuvf2AiVlf3BgqG9rtATF8az+YXbbxJ1BKueoomtiBPwPsYGlpuavfmuidePInVPCb59Lzouyv7BY5IxA== dependencies: "@standardnotes/common" "^1.22.0" - "@standardnotes/services" "^1.13.3" + "@standardnotes/services" "^1.13.8" "@standardnotes/utils" "^1.6.10" -"@standardnotes/filepicker@^1.16.4": - version "1.16.4" - resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.4.tgz#742023dab908c477e28bf472183cae8309639d95" - integrity sha512-a8RHKtLgTG3WmgXvRafMNj5utGKaIZvgQhqyRfNt0EuIK1uSvdvt3jLA8VGeehP1JE0gc3XfZKCmE7vgNz8wDg== +"@standardnotes/files@^1.3.8": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.3.8.tgz#507ac1b6081e29f590894f36c8a9148c7e0781c7" + integrity sha512-TY25jKxmGxZhzJka3niHOuVZPOm0HkI+1OV+Ddy/jfsYhhSN19HOmyGmBkHmWewExE+ZlYZaaK0IZDXNC/LnOQ== dependencies: - "@standardnotes/common" "^1.22.0" - "@standardnotes/services" "^1.13.5" - "@standardnotes/utils" "^1.6.10" - -"@standardnotes/files@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.3.4.tgz#0b8c820b2aad2d24d5b837935999979e09a2a6bb" - integrity sha512-kSb8jhvnnLX43FlCL3JzMZxT3KjI+9I37DyZi9FR/s+8gQ6TyuCtk4JjCZxn1i+cRbdKyzeVPi2T24cvQEVs/g== - dependencies: - "@standardnotes/encryption" "^1.8.5" - "@standardnotes/filepicker" "^1.16.4" - "@standardnotes/models" "^1.11.0" - "@standardnotes/responses" "^1.6.28" - "@standardnotes/services" "^1.13.5" + "@standardnotes/encryption" "^1.8.8" + "@standardnotes/filepicker" "^1.16.8" + "@standardnotes/models" "^1.11.2" + "@standardnotes/responses" "^1.6.29" + "@standardnotes/services" "^1.13.8" "@standardnotes/utils" "^1.6.10" "@standardnotes/icons@^1.1.8": @@ -2471,51 +2430,15 @@ resolved "https://registry.yarnpkg.com/@standardnotes/icons/-/icons-1.1.8.tgz#958b73cc3dd68c7fe31dcceb8ee48627093ab468" integrity sha512-RhNzHEbSYFVwVz5+BqDAC5wJZ8DkQlboofwPxuTLSrmezjBeNi9kOw9metoC1Sf82u3bXJr5fgXAC8DEYXYKTg== -"@standardnotes/models@^1.10.2": - version "1.10.2" - resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.10.2.tgz#90062c89212fb0f23614e17ca76552de65af4efa" - integrity sha512-DCpprk48a/egWhc8oANH0p4LqyjJKTzqqC1r35JaBZ5eGnAdLl4KfLShdZl0JOOjJaylqR6PI1s4uBxsHwMcMA== - dependencies: - "@standardnotes/features" "^1.44.4" - "@standardnotes/responses" "^1.6.27" - "@standardnotes/utils" "^1.6.10" - -"@standardnotes/models@^1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.11.0.tgz#35647d0f8b1bb0a431298bd040c91343f4ffc2b1" - integrity sha512-5T4WBQeNfWpX9F5NUhI68ah5nAk+0+Umz3YphRjpa8MrmDiA32VH37LG43+Tp17vSxQeG2shVAduC15QmGch3g== - dependencies: - "@standardnotes/features" "^1.44.5" - "@standardnotes/responses" "^1.6.28" - "@standardnotes/utils" "^1.6.10" - -"@standardnotes/models@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.11.1.tgz#d622b7cffd7ee4faebcd564d7ae880b372ec7be8" - integrity sha512-XKXoV8Pi5iuzrjUGWs4grvxn3m2BQyt49Br+euToOkgvZvW5HIiaCGLwAtvP5S3d3ecgD5EvbLzGJIFEs5F4rw== +"@standardnotes/models@^1.11.2": + version "1.11.2" + resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.11.2.tgz#c0fee94c281b067a885f42d37d087987628d1dd4" + integrity sha512-0D3Z0aOqpTaKGkSIlQXUGMC8MFmNwbTk7EwU+l/q5jORANG1YXD7ZX1gveirMFVmxeCYxszXld0d9c3aKAt8Yg== dependencies: "@standardnotes/features" "^1.44.6" "@standardnotes/responses" "^1.6.29" "@standardnotes/utils" "^1.6.10" -"@standardnotes/responses@^1.6.27": - version "1.6.27" - resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.27.tgz#3a440090e5cee09f2980df5cc57a60f76adff735" - integrity sha512-yRFv9e6a36MXTnynSlL6i0aaHEVlGvcZi/6vChYGJzYigwjYJ/XmTXOAyKSTeem55vDuHB74Qml6tXM0Emxw8Q== - dependencies: - "@standardnotes/auth" "^3.19.0" - "@standardnotes/common" "^1.22.0" - "@standardnotes/features" "^1.44.4" - -"@standardnotes/responses@^1.6.28": - version "1.6.28" - resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.28.tgz#846e56968d104bac360aa9080627123326f70af9" - integrity sha512-oaxoH1Qw9VrIF7Fgak5NGayGDQZl5lOuuxyXh8ngCOP7Qpgrr/5whm5ZnKTMYwD8kE9/Vi22G9oxVTz1JOErkA== - dependencies: - "@standardnotes/auth" "^3.19.1" - "@standardnotes/common" "^1.22.0" - "@standardnotes/features" "^1.44.5" - "@standardnotes/responses@^1.6.29": version "1.6.29" resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.29.tgz#fac5875bb84e382d7b1acf14c0af77876fe5c41d" @@ -2525,36 +2448,14 @@ "@standardnotes/common" "^1.22.0" "@standardnotes/features" "^1.44.6" -"@standardnotes/services@^1.13.3": - version "1.13.3" - resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.3.tgz#b857a6ed42b15d40c2e9d08b41739f5fb6b0d3e0" - integrity sha512-V7MVlZ3RPJ0Aw9tRLJqA87GibQnzZzsrF4Gamut/XpS44ixK3XbyU+FRv5jX6d+BPtZeTTaLE4rqRICZeOvTrQ== - dependencies: - "@standardnotes/auth" "^3.19.0" - "@standardnotes/common" "^1.22.0" - "@standardnotes/models" "^1.10.2" - "@standardnotes/responses" "^1.6.27" - "@standardnotes/utils" "^1.6.10" - -"@standardnotes/services@^1.13.5": - version "1.13.5" - resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.5.tgz#644533f1a30b6d7459c295152c222a725c9a4f33" - integrity sha512-YDG8JgCI29Sa3s8f90rxeuakG7/MDuKSM8OytKKl16WDioKcKEysPlqo4blmj3ntK5mTR7aV/CGh4J+gNak+jw== - dependencies: - "@standardnotes/auth" "^3.19.1" - "@standardnotes/common" "^1.22.0" - "@standardnotes/models" "^1.11.0" - "@standardnotes/responses" "^1.6.28" - "@standardnotes/utils" "^1.6.10" - -"@standardnotes/services@^1.13.6": - version "1.13.6" - resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.6.tgz#9ab4d0c3ed0ad3693ba54ac360f4fdaad08f7410" - integrity sha512-Vt/hptzJK4D6qUPp/cdhOLlRT57uf3CDjDUwyfnELXDyU8mFTVvS/M1deD1PecNWodsfLH7aDWeMWTKApP6LKg== +"@standardnotes/services@^1.13.8": + version "1.13.8" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.8.tgz#8d1844253d8e4d7a1e75ecd50b1687d2e7f8b5ce" + integrity sha512-BMp9SWlfYQkUrBjHQq3HZgPeHvKOebboVqqbbzomjpxNdaPk/mTsH5mHHUu/SaEPkPkCCE0TZ266jlZJ2t4a1w== dependencies: "@standardnotes/auth" "^3.19.2" "@standardnotes/common" "^1.22.0" - "@standardnotes/models" "^1.11.1" + "@standardnotes/models" "^1.11.2" "@standardnotes/responses" "^1.6.29" "@standardnotes/utils" "^1.6.10" @@ -2577,21 +2478,21 @@ buffer "^6.0.3" libsodium-wrappers "^0.7.9" -"@standardnotes/snjs@^2.114.5": - version "2.114.5" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.114.5.tgz#1fee7e7c7558d591cfa99bc47a514eb08c67357d" - integrity sha512-SEriP3YsKoIn8idPbKNUnTocQtLt8jXngZ1O1DQrC0fL/aUxHeaVUinSjYyr8WXQqQatoA2Jngcouv3zpvmAkQ== +"@standardnotes/snjs@^2.114.11": + version "2.114.11" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.114.11.tgz#c7bc898e814c62cd7fa9bb4d5f5ffdc51e542e88" + integrity sha512-4ym8mT7S7b8yATp+CxmEaudFSsP0ixMYYX98eptaGSk5aCWG8OWRnq+VXC9NHH0cX8kPa4ywVdE20tW7NmA/1Q== dependencies: - "@standardnotes/auth" "^3.19.1" + "@standardnotes/auth" "^3.19.2" "@standardnotes/common" "^1.22.0" - "@standardnotes/domain-events" "^2.28.14" - "@standardnotes/encryption" "^1.8.5" - "@standardnotes/features" "^1.44.5" - "@standardnotes/filepicker" "^1.16.4" - "@standardnotes/files" "^1.3.4" - "@standardnotes/models" "^1.11.0" - "@standardnotes/responses" "^1.6.28" - "@standardnotes/services" "^1.13.5" + "@standardnotes/domain-events" "^2.29.0" + "@standardnotes/encryption" "^1.8.8" + "@standardnotes/features" "^1.44.6" + "@standardnotes/filepicker" "^1.16.8" + "@standardnotes/files" "^1.3.8" + "@standardnotes/models" "^1.11.2" + "@standardnotes/responses" "^1.6.29" + "@standardnotes/services" "^1.13.8" "@standardnotes/settings" "^1.14.3" "@standardnotes/sncrypto-common" "^1.9.0" "@standardnotes/utils" "^1.6.10" From 94199fb6b53ba19e77b2b43f47d717898c085ba1 Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 2 Jun 2022 11:00:06 -0500 Subject: [PATCH 38/39] fix: change undefined css class --- .../Preferences/Panes/Backups/Files/BackupsDropZone.tsx | 6 +++--- .../Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx | 4 ++-- .../Components/Preferences/Panes/General/Labs.tsx | 2 +- .../Preferences/Panes/Listed/ListedAccountItem.tsx | 2 +- .../Components/Preferences/Panes/Security/ErroredItems.tsx | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx index 440d722ae..5f3819a2b 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/BackupsDropZone.tsx @@ -52,15 +52,15 @@ const BackupsDropZone: FunctionComponent = ({ application }) => { void application.alertService.alert( `${decryptedFileItem.name} has been successfully decrypted and saved to your chosen directory.`, ) + setBinaryFile(undefined) + setDecryptedFileItem(undefined) + setDroppedFile(undefined) } else if (result === 'failed') { void application.alertService.alert( 'Unable to save file to local directory. This may be caused by failure to decrypt, or failure to save the file locally.', ) } - setBinaryFile(undefined) - setDecryptedFileItem(undefined) - setDroppedFile(undefined) setIsSavingAsDecrypted(false) }, [decryptedFileItem, application, binaryFile, fileSystem]) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx index e4aa39118..a9e97eafc 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/Files/FileBackupsDesktop.tsx @@ -67,7 +67,7 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => { {!backupsEnabled && ( <> - + File backups are not enabled. Enable to choose where your files are backed up. )} @@ -90,7 +90,7 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => { checkmark={false} /> -
    +
    - {showHorizontalSeparator && } + {showHorizontalSeparator && } ) })} diff --git a/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx b/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx index eaeab7bac..4939197ed 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Listed/ListedAccountItem.tsx @@ -37,7 +37,7 @@ const ListedAccountItem: FunctionComponent = ({ account, showSeparator, a )}
    - {showSeparator && } + {showSeparator && } ) } diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx index 7298d0c99..8815a0468 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/ErroredItems.tsx @@ -87,7 +87,7 @@ const ErroredItems: FunctionComponent = ({ viewControllerManager }: Props }} />
    - + {erroredItems.map((item, index) => { return ( @@ -128,7 +128,7 @@ const ErroredItems: FunctionComponent = ({ viewControllerManager }: Props - {index < erroredItems.length - 1 && } + {index < erroredItems.length - 1 && } ) })} From 462199406c99a9a88e2566075fb35f2c51777611 Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 2 Jun 2022 11:11:34 -0500 Subject: [PATCH 39/39] chore: upgrade deps --- package.json | 6 +++--- yarn.lock | 58 ++++++++++++++++++++++++++-------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index 734c9564b..86bb3d950 100644 --- a/package.json +++ b/package.json @@ -71,12 +71,12 @@ "@reach/tooltip": "^0.16.2", "@reach/visually-hidden": "^0.16.0", "@standardnotes/components": "1.8.2", - "@standardnotes/filepicker": "1.16.8", + "@standardnotes/filepicker": "1.16.9", "@standardnotes/icons": "^1.1.8", "@standardnotes/sncrypto-web": "1.10.1", - "@standardnotes/snjs": "^2.114.11", + "@standardnotes/snjs": "^2.114.12", "@standardnotes/stylekit": "5.29.3", - "@standardnotes/services": "^1.13.8", + "@standardnotes/services": "^1.13.9", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index 06a4d2a02..0ca1e7a96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2387,14 +2387,14 @@ "@standardnotes/auth" "^3.19.2" "@standardnotes/features" "^1.44.6" -"@standardnotes/encryption@^1.8.8": - version "1.8.8" - resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.8.8.tgz#835bbf083a86c8b683f2599bd29124527f76797c" - integrity sha512-lvXe1r+JAzInRmP8wG+rAiPzCPze3rNGMmz9ddZxPtOVXz+WOMfSSGKlWoAQJ0SxdmNPJUyRH3ydewLlYuubaw== +"@standardnotes/encryption@^1.8.9": + version "1.8.9" + resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.8.9.tgz#2f1114590f27ea1df758eff3cebae8695abac3bf" + integrity sha512-XDousGfN6NWTPHNoS+go917nzFh3GHgKz6Ox+t7fiy10OKCwEuE7B9tzhUoaanQYCHrXIahGjlzX+zJ3Eell4w== dependencies: "@standardnotes/models" "^1.11.2" "@standardnotes/responses" "^1.6.29" - "@standardnotes/services" "^1.13.8" + "@standardnotes/services" "^1.13.9" "@standardnotes/features@^1.44.6": version "1.44.6" @@ -2404,25 +2404,25 @@ "@standardnotes/auth" "^3.19.2" "@standardnotes/common" "^1.22.0" -"@standardnotes/filepicker@1.16.8", "@standardnotes/filepicker@^1.16.8": - version "1.16.8" - resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.8.tgz#2ea7d2eaacf60e033efdf4e430a7ccfd8fd0d658" - integrity sha512-9QEeAbuvf2AiVlf3BgqG9rtATF8az+YXbbxJ1BKueoomtiBPwPsYGlpuavfmuidePInVPCb59Lzouyv7BY5IxA== +"@standardnotes/filepicker@1.16.9", "@standardnotes/filepicker@^1.16.9": + version "1.16.9" + resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.9.tgz#32d1e43f10888c8516e05191a4aab06529c6d5b0" + integrity sha512-gZ8Y+WeKHDFd5rKwXaMuj+WUkpM8N0Gk2yC4TXxT3AG6y/NEzcmAOO+i9cBN5/wkK8HpyDjw7vKOwmlKdJhmAw== dependencies: "@standardnotes/common" "^1.22.0" - "@standardnotes/services" "^1.13.8" + "@standardnotes/services" "^1.13.9" "@standardnotes/utils" "^1.6.10" -"@standardnotes/files@^1.3.8": - version "1.3.8" - resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.3.8.tgz#507ac1b6081e29f590894f36c8a9148c7e0781c7" - integrity sha512-TY25jKxmGxZhzJka3niHOuVZPOm0HkI+1OV+Ddy/jfsYhhSN19HOmyGmBkHmWewExE+ZlYZaaK0IZDXNC/LnOQ== +"@standardnotes/files@^1.3.9": + version "1.3.9" + resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.3.9.tgz#349bc061eb2669073a998c5526ff678e1e6c941a" + integrity sha512-fyC3ynYOD2nndVp+xwjE0byUoMS7M2F934qVfwheDk3gVA2y0ZmScD0Fs9Lbwr0gaSM697Hf2bdNDH7oxc+VHQ== dependencies: - "@standardnotes/encryption" "^1.8.8" - "@standardnotes/filepicker" "^1.16.8" + "@standardnotes/encryption" "^1.8.9" + "@standardnotes/filepicker" "^1.16.9" "@standardnotes/models" "^1.11.2" "@standardnotes/responses" "^1.6.29" - "@standardnotes/services" "^1.13.8" + "@standardnotes/services" "^1.13.9" "@standardnotes/utils" "^1.6.10" "@standardnotes/icons@^1.1.8": @@ -2448,10 +2448,10 @@ "@standardnotes/common" "^1.22.0" "@standardnotes/features" "^1.44.6" -"@standardnotes/services@^1.13.8": - version "1.13.8" - resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.8.tgz#8d1844253d8e4d7a1e75ecd50b1687d2e7f8b5ce" - integrity sha512-BMp9SWlfYQkUrBjHQq3HZgPeHvKOebboVqqbbzomjpxNdaPk/mTsH5mHHUu/SaEPkPkCCE0TZ266jlZJ2t4a1w== +"@standardnotes/services@^1.13.9": + version "1.13.9" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.9.tgz#add529866797bd610d94ba2d0cd6b56479685362" + integrity sha512-6GyYyaTQNsZwic9YExvNqcVJXYbvnI4rUglelajog6nY+m7y3tpSonwGt74tjnCBb4jYkzj2BbQqNpOS3JK3Xg== dependencies: "@standardnotes/auth" "^3.19.2" "@standardnotes/common" "^1.22.0" @@ -2478,21 +2478,21 @@ buffer "^6.0.3" libsodium-wrappers "^0.7.9" -"@standardnotes/snjs@^2.114.11": - version "2.114.11" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.114.11.tgz#c7bc898e814c62cd7fa9bb4d5f5ffdc51e542e88" - integrity sha512-4ym8mT7S7b8yATp+CxmEaudFSsP0ixMYYX98eptaGSk5aCWG8OWRnq+VXC9NHH0cX8kPa4ywVdE20tW7NmA/1Q== +"@standardnotes/snjs@^2.114.12": + version "2.114.12" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.114.12.tgz#0eba281ec18113eada49386284cc4ea6934f2d97" + integrity sha512-NdJRIGvgFBBmOUmOgN3rvz++PVrzHr8IZpkPseI8/X79cf9T3wpJMe6CHhRYwamHMhlgAVFj4678SCkTlUtrnA== dependencies: "@standardnotes/auth" "^3.19.2" "@standardnotes/common" "^1.22.0" "@standardnotes/domain-events" "^2.29.0" - "@standardnotes/encryption" "^1.8.8" + "@standardnotes/encryption" "^1.8.9" "@standardnotes/features" "^1.44.6" - "@standardnotes/filepicker" "^1.16.8" - "@standardnotes/files" "^1.3.8" + "@standardnotes/filepicker" "^1.16.9" + "@standardnotes/files" "^1.3.9" "@standardnotes/models" "^1.11.2" "@standardnotes/responses" "^1.6.29" - "@standardnotes/services" "^1.13.8" + "@standardnotes/services" "^1.13.9" "@standardnotes/settings" "^1.14.3" "@standardnotes/sncrypto-common" "^1.9.0" "@standardnotes/utils" "^1.6.10"