From 8e467f9e6d21231f27849e9dd40885900fdaaa07 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Wed, 13 Apr 2022 22:02:34 +0530 Subject: [PATCH] refactor: format and lint codebase (#971) --- .eslintrc | 7 +- .prettierrc | 3 + app/assets/javascripts/@types/modules.ts | 2 +- .../javascripts/@types/qrcode.react.d.ts | 2 +- app/assets/javascripts/App.tsx | 79 ++ .../Components/Abstract/PureComponent.tsx | 135 +++ .../AccountMenu/AdvancedOptions.tsx | 115 +-- .../AccountMenu/ConfirmPassword.tsx | 119 ++- .../AccountMenu/CreateAccount.tsx | 110 +-- .../AccountMenu/GeneralAccountMenu.tsx | 107 +-- .../AccountMenu/SignIn.tsx | 148 +-- .../AccountMenu/User.tsx | 27 +- .../WorkspaceSwitcher/WorkspaceMenuItem.tsx | 48 +- .../WorkspaceSwitcherMenu.tsx | 64 ++ .../WorkspaceSwitcherOption.tsx | 60 +- .../AccountMenu/index.tsx | 117 +-- .../ApplicationGroupView/index.tsx} | 29 +- .../Components/ApplicationView/index.tsx | 216 +++++ .../AttachedFilesButton.tsx | 368 +++++++ .../AttachedFilesPopover.tsx | 151 ++- .../AttachedFilesPopover/PopoverFileItem.tsx | 81 +- .../PopoverFileItemAction.tsx | 31 + .../PopoverFileSubmenu.tsx | 112 +-- .../Bubble/index.tsx} | 16 +- .../Button}/Button.tsx | 64 +- .../Button}/IconButton.tsx | 32 +- .../Components/Button/RoundIconButton.tsx | 40 + .../ChallengeModal/ChallengeModal.tsx | 248 +++-- .../ChallengeModal/ChallengePrompt.tsx | 59 +- .../ChangeEditor}/ChangeEditorButton.tsx | 97 +- .../ChangeEditor}/ChangeEditorMenu.tsx | 205 ++-- .../ChangeEditor}/createEditorMenuGroups.ts | 65 +- .../Checkbox/index.tsx} | 18 +- .../ComponentView/IsDeprecated.tsx | 20 +- .../ComponentView/IsExpired.tsx | 49 +- .../ComponentView/IssueOnLoading.tsx | 19 +- .../ComponentView/OfflineRestricted.tsx | 20 +- .../ComponentView/UrlMissing.tsx | 18 +- .../Components/ComponentView/index.tsx | 221 +++++ .../Components/ConfirmSignoutModal/index.tsx | 98 ++ .../Dropdown/index.tsx} | 70 +- .../Files/FilePreviewInfoPanel.tsx | 22 +- .../Files/FilePreviewModal.tsx | 124 ++- .../Files/FilePreviewModalProvider.tsx | 48 + .../Components/Files/isFilePreviewable.ts | 12 + .../Footer/index.tsx} | 391 ++++---- .../Icon.tsx => Components/Icon/index.tsx} | 30 +- .../Components/Input/DecoratedInput.tsx | 75 ++ .../Components/Input/DecoratedInputProps.ts | 15 + .../Input/DecoratedPasswordInput.tsx | 42 + .../Input}/FloatingLabelInput.tsx | 56 +- .../javascripts/Components/Input/Input.tsx | 14 + .../{components => Components}/Menu/Menu.tsx | 98 +- .../Menu/MenuItem.tsx | 113 +-- .../MultipleSelectedNotes/index.tsx | 36 + .../Components/Navigation/index.tsx | 82 ++ .../NoAccountWarning/index.tsx} | 26 +- .../Components/NoteGroupView/index.tsx | 57 ++ .../NoteTags}/NoteTag.tsx | 111 ++- .../NoteTags}/NoteTagsContainer.tsx | 24 +- .../NoteView/NoteView.test.ts | 144 ++- .../NoteView/NoteView.tsx | 903 ++++++++---------- .../Components/NotesContextMenu/index.tsx | 47 + .../NotesList}/NotesListItem.tsx | 87 +- .../NotesList}/NotesListOptionsMenu.tsx | 162 ++-- .../Components/NotesList/index.tsx | 104 ++ .../Components/NotesOptions/AddTagOption.tsx | 111 +++ .../NotesOptions/ChangeEditorOption.tsx | 108 +-- .../NotesOptions/ListedActionsOption.tsx | 200 ++-- .../NotesOptions/NotesOptions.tsx | 299 +++--- .../NotesOptions}/NotesOptionsPanel.tsx | 82 +- .../Components/NotesView/index.tsx | 260 +++++ .../Components/OtherSessionsSignOut/index.tsx | 70 ++ .../Components/PanelResizer/index.tsx | 326 +++++++ .../PasswordWizard/index.tsx} | 217 ++--- .../PermissionsModal/index.tsx} | 49 +- .../Components/PinNoteButton/index.tsx | 39 + .../Preferences/Panes/Account/Advanced.tsx | 38 + .../Panes/Account}/Authentication.tsx | 44 +- .../Account/ChangeEmail}/ChangeEmailForm.tsx | 22 +- .../ChangeEmail/ChangeEmailSuccess.tsx | 13 + .../Panes/Account/ChangeEmail/index.tsx | 171 ++++ .../Preferences/Panes/Account/Credentials.tsx | 75 ++ .../Panes/Account/OfflineSubscription.tsx | 128 +++ .../Panes/Account}/SignOutView.tsx | 58 +- .../Account/Subscription/NoSubscription.tsx | 51 + .../Account/Subscription}/Subscription.tsx | 36 +- .../Subscription/SubscriptionInformation.tsx | 85 ++ .../Preferences/Panes/Account/Sync.tsx | 65 ++ .../Preferences/Panes/Account/index.tsx | 29 + .../Preferences/Panes/Appearance.tsx | 169 ++++ .../CloudBackups}/CloudBackupProvider.tsx | 203 ++-- .../Panes/Backups/CloudBackups}/index.tsx | 127 ++- .../Panes/Backups}/DataBackups.tsx | 170 ++-- .../Panes/Backups}/EmailBackups.tsx | 173 ++-- .../Preferences/Panes/Backups/index.tsx | 22 + .../Preferences/Panes}/CloudLink.tsx | 55 +- .../Extensions}/ConfirmCustomExtension.tsx | 22 +- .../Panes/Extensions/ExtensionItem.tsx | 104 ++ .../Extensions/ExtensionsLatestVersions.ts | 42 + .../Panes/Extensions}/RenameExtension.tsx | 47 +- .../Preferences/Panes/Extensions/index.tsx} | 99 +- .../Preferences/Panes/General}/Defaults.tsx | 155 ++- .../Preferences/Panes/General}/Labs.tsx | 94 +- .../Preferences/Panes/General/Tools.tsx | 62 ++ .../Preferences/Panes/General/index.tsx | 31 + .../Preferences/Panes}/HelpFeedback.tsx | 55 +- .../Preferences/Panes/Listed/BlogItem.tsx | 48 + .../Preferences/Panes/Listed/index.tsx} | 82 +- .../Preferences/Panes/Security/Encryption.tsx | 72 ++ .../Panes/Security}/PasscodeLock.tsx | 248 ++--- .../Preferences/Panes/Security/Privacy.tsx | 146 +++ .../Panes/Security/Protections.tsx | 103 ++ .../Preferences/Panes/Security/index.tsx | 25 + .../Panes/TwoFactorAuth}/AuthAppInfoPopup.tsx | 55 +- .../Panes/TwoFactorAuth/Bullet.tsx | 5 + .../Panes/TwoFactorAuth/CopyButton.tsx | 21 + .../Panes/TwoFactorAuth/MfaProps.ts | 6 + .../Panes/TwoFactorAuth}/SaveSecretKey.tsx | 35 +- .../Panes/TwoFactorAuth}/ScanQRCode.tsx | 28 +- .../TwoFactorAuth}/TwoFactorActivation.ts | 82 +- .../TwoFactorAuth/TwoFactorActivationView.tsx | 22 + .../Panes/TwoFactorAuth}/TwoFactorAuth.ts | 107 +-- .../Panes/TwoFactorAuth/TwoFactorAuthView.tsx | 77 ++ .../Panes/TwoFactorAuth}/TwoFactorSuccess.tsx | 23 +- .../Panes/TwoFactorAuth}/Verification.tsx | 46 +- .../TwoFactorAuth/download-secret-key.tsx | 13 + .../Preferences/Panes/TwoFactorAuth/index.tsx | 11 + .../Components/Preferences/Panes/index.ts | 5 + .../PreferencesComponents}/Content.tsx | 32 +- .../PreferencesComponents/MenuItem.tsx | 24 + .../PreferencesGroup.tsx | 16 +- .../PreferencesPane.tsx | 4 +- .../PreferencesSegment.tsx | 8 + .../PreferencesComponents/index.ts | 5 + .../Preferences/PreferencesMenu.ts | 67 +- .../Preferences/PreferencesMenuView.tsx | 12 +- .../Preferences/PreferencesView.tsx | 116 +++ .../Preferences/PreferencesViewWrapper.tsx | 25 +- .../Preferences/Providers/MfaProvider.ts | 13 + .../Preferences/Providers/UserProvider.ts | 3 + .../Components/Preferences/Providers/index.ts | 2 + .../PremiumFeaturesModal/index.tsx} | 61 +- .../ProtectedNoteOverlay/index.tsx} | 27 +- .../PurchaseFlow/Panes/CreateAccount.tsx | 206 ++++ .../Components/PurchaseFlow/Panes/SignIn.tsx | 167 ++++ .../PurchaseFlow/PurchaseFlowView.tsx | 43 +- .../PurchaseFlow/PurchaseFlowWrapper.tsx | 48 + .../QuickSettingsMenu/EventHandlers.ts} | 62 +- .../QuickSettingsMenu/FocusModeSwitch.tsx | 47 +- .../Components/QuickSettingsMenu/ThemeItem.ts | 7 + .../QuickSettingsMenu/ThemesMenuButton.tsx | 74 +- .../QuickSettingsMenu/index.tsx} | 287 +++--- .../HistoryListContainer.tsx | 176 ++-- .../RevisionHistoryModal/HistoryListItem.tsx | 18 +- .../LegacyHistoryList.tsx | 69 +- .../RemoteHistoryList.tsx | 94 ++ .../RevisionContentLocked.tsx | 45 +- .../RevisionHistoryModalWrapper.tsx | 248 +++-- .../SelectedRevisionContent.tsx | 75 ++ .../SessionHistoryList.tsx | 78 +- .../RevisionHistoryModal/utils.ts | 102 +- .../SearchOptions/index.tsx} | 25 +- .../SessionsModal/index.tsx} | 160 ++-- .../Shared/AccordionItem.tsx | 30 +- .../Shared/HorizontalSeparator.tsx | 14 +- .../Shared/ModalDialog.tsx | 40 +- .../Switch/index.tsx} | 48 +- .../SyncResolutionMenu/index.tsx} | 44 +- .../TagAutocomplete/AutocompleteTagHint.tsx | 72 ++ .../TagAutocomplete/AutocompleteTagInput.tsx | 146 +++ .../TagAutocomplete/AutocompleteTagResult.tsx | 104 ++ .../javascripts/Components/Tags/DragNDrop.ts | 9 + .../Components/Tags/RootTagDropZone.tsx | 46 + .../Components/Tags/SmartViewsList.tsx | 27 + .../Components/Tags/SmartViewsListItem.tsx | 153 +++ .../Components/Tags/SmartViewsSection.tsx | 16 + .../Components/Tags/TagContextMenu.tsx | 110 +++ .../Tags/TagsList.tsx | 55 +- .../Tags/TagsListItem.tsx | 229 +++-- .../Components/Tags/TagsSection.tsx | 66 ++ .../Components/Tags/TagsSectionAddButton.tsx | 22 + .../Components/Tags/TagsSectionTitle.tsx | 55 ++ .../javascripts/Components/TitleBar/Title.tsx | 5 + .../Components/TitleBar/TitleBar.tsx | 5 + app/assets/javascripts/Constants.ts | 18 + app/assets/javascripts/Crypto.ts | 3 + .../javascripts/{database.ts => Database.ts} | 215 ++--- .../{element_ids.ts => ElementIDs.ts} | 2 +- app/assets/javascripts/{enums.ts => Enums.ts} | 0 .../javascripts/Hooks/useBeforeUnload.tsx | 11 + .../javascripts/Hooks/useCloseOnBlur.ts | 24 + .../Hooks/useCloseOnClickOutside.ts | 29 + .../Hooks/useListKeyboardNavigation.ts | 96 ++ .../javascripts/Hooks/usePremiumModal.tsx | 61 ++ .../AlertService.ts} | 57 +- .../javascripts/Services/ArchiveManager.ts | 159 +++ .../AutolockService.ts} | 84 +- app/assets/javascripts/Services/Bridge.ts | 37 + .../BrowserBridge.ts} | 19 +- .../DesktopManager.ts} | 118 +-- .../ioService.ts => Services/IOService.ts} | 172 ++-- .../LocalStorage.ts} | 18 +- .../javascripts/Services/StatusManager.ts | 30 + .../javascripts/Services/ThemeManager.ts | 318 ++++++ ...tartApplication.ts => StartApplication.ts} | 6 +- .../javascripts/{strings.ts => Strings.ts} | 119 ++- app/assets/javascripts/Types.ts | 12 + .../hoist-non-react-statics.d.ts | 66 +- .../AppState/AccountMenuState.ts} | 175 ++-- .../UIModels/AppState/ActionsMenuState.ts | 22 + .../javascripts/UIModels/AppState/AppState.ts | 429 +++++++++ .../UIModels/AppState/FeaturesState.ts | 87 ++ .../UIModels/AppState/FilesState.ts | 169 ++++ .../AppState/NoAccountWarningState.ts} | 34 +- .../AppState/NoteTagsState.ts} | 186 ++-- .../AppState/NotesState.ts} | 375 ++++---- .../AppState/NotesViewState.ts} | 507 +++++----- .../AppState/PreferencesState.ts} | 28 +- .../AppState/PurchaseFlowState.ts} | 28 +- .../AppState/QuickSettingsState.ts} | 38 +- .../AppState/SearchOptionsState.ts} | 50 +- .../AppState/SubscriptionState.ts} | 81 +- .../UIModels/AppState/SyncState.ts | 33 + .../AppState/TagsState.ts} | 459 ++++----- .../javascripts/UIModels/AppState/index.ts | 1 + .../javascripts/UIModels/Application.ts | 155 +++ .../javascripts/UIModels/ApplicationGroup.ts | 75 ++ .../CalculateDifferenceBetweenDatesInDays.ts | 19 + .../Utils/CalculateSubmenuStyle.tsx | 64 ++ .../Utils/ConcatenateUint8Arrays.ts | 12 + .../{utils/isMobile.ts => Utils/IsMobile.ts} | 16 +- .../javascripts/Utils/ManageSubscription.ts | 13 + app/assets/javascripts/Utils/SortThemes.ts | 14 + .../javascripts/Utils/StringUtils.spec.ts | 61 ++ app/assets/javascripts/Utils/StringUtils.ts | 29 + .../{utils/utils.ts => Utils/Utils.ts} | 140 ++- app/assets/javascripts/Utils/index.ts | 5 + app/assets/javascripts/Version.ts | 8 + ...ice_interface.ts => WebDeviceInterface.ts} | 118 +-- .../__mocks__/@standardnotes/snjs.js | 4 +- app/assets/javascripts/app.tsx | 79 -- .../components/Abstract/PureComponent.tsx | 140 --- .../WorkspaceSwitcherMenu.tsx | 69 -- .../components/ApplicationView.tsx | 250 ----- .../AttachedFilesButton.tsx | 401 -------- .../PopoverFileItemAction.tsx | 32 - .../components/AutocompleteTagHint.tsx | 81 -- .../components/AutocompleteTagInput.tsx | 160 ---- .../components/AutocompleteTagResult.tsx | 115 --- .../components/ComponentView/index.tsx | 250 ----- .../components/ConfirmSignoutModal.tsx | 114 --- .../components/ConfirmationDialog.tsx | 35 - .../javascripts/components/DecoratedInput.tsx | 98 -- .../components/DecoratedPasswordInput.tsx | 42 - .../Files/FilePreviewModalProvider.tsx | 53 - .../components/Files/isFilePreviewable.ts | 12 - app/assets/javascripts/components/Input.tsx | 22 - app/assets/javascripts/components/MenuRow.tsx | 115 --- .../components/MultipleSelectedNotes.tsx | 40 - .../javascripts/components/Navigation.tsx | 89 -- .../javascripts/components/NoteGroupView.tsx | 70 -- .../components/NotesContextMenu.tsx | 53 - .../javascripts/components/NotesList.tsx | 118 --- .../components/NotesOptions/AddTagOption.tsx | 127 --- .../javascripts/components/NotesView.tsx | 294 ------ .../components/OtherSessionsSignOut.tsx | 79 -- .../javascripts/components/PanelResizer.tsx | 340 ------- .../javascripts/components/PinNoteButton.tsx | 41 - .../Preferences/PreferencesView.tsx | 130 --- .../Preferences/components/MenuItem.tsx | 31 - .../components/PreferencesSegment.tsx | 9 - .../Preferences/components/index.ts | 5 - .../Preferences/panes/AccountPreferences.tsx | 33 - .../Preferences/panes/Appearance.tsx | 201 ---- .../components/Preferences/panes/Backups.tsx | 23 - .../components/Preferences/panes/General.tsx | 29 - .../components/Preferences/panes/Security.tsx | 26 - .../Preferences/panes/account/Advanced.tsx | 44 - .../Preferences/panes/account/Credentials.tsx | 81 -- .../Preferences/panes/account/Sync.tsx | 69 -- .../changeEmail/ChangeEmailSuccess.tsx | 15 - .../panes/account/changeEmail/index.tsx | 180 ---- .../Preferences/panes/account/index.ts | 6 - .../panes/account/offlineSubscription.tsx | 138 --- .../account/subscription/NoSubscription.tsx | 57 -- .../subscription/SubscriptionInformation.tsx | 91 -- .../panes/backups-segments/index.ts | 3 - .../extensions-segments/ExtensionItem.tsx | 115 --- .../ExtensionsLatestVersions.ts | 55 -- .../panes/extensions-segments/index.ts | 3 - .../panes/general-segments/Tools.tsx | 67 -- .../panes/general-segments/index.ts | 3 - .../components/Preferences/panes/index.ts | 5 - .../Preferences/panes/listed/BlogItem.tsx | 56 -- .../panes/security-segments/Encryption.tsx | 86 -- .../panes/security-segments/Privacy.tsx | 166 ---- .../panes/security-segments/Protections.tsx | 118 --- .../panes/security-segments/index.ts | 3 - .../panes/two-factor-auth/Bullet.tsx | 9 - .../panes/two-factor-auth/CopyButton.tsx | 23 - .../panes/two-factor-auth/MfaProps.ts | 6 - .../TwoFactorActivationView.tsx | 22 - .../two-factor-auth/TwoFactorAuthView.tsx | 91 -- .../two-factor-auth/download-secret-key.tsx | 13 - .../panes/two-factor-auth/index.tsx | 13 - .../Preferences/providers/MfaProvider.ts | 13 - .../Preferences/providers/UserProvider.ts | 3 - .../components/Preferences/providers/index.ts | 2 - .../javascripts/components/Premium/index.ts | 1 - .../components/Premium/usePremiumModal.tsx | 63 -- .../PurchaseFlow/PurchaseFlowWrapper.tsx | 49 - .../PurchaseFlow/panes/CreateAccount.tsx | 220 ----- .../components/PurchaseFlow/panes/SignIn.tsx | 175 ---- .../RemoteHistoryList.tsx | 115 --- .../SelectedRevisionContent.tsx | 95 -- .../components/RevisionPreviewModal.tsx | 187 ---- .../components/RoundIconButton.tsx | 43 - .../components/Tags/RootTagDropZone.tsx | 48 - .../components/Tags/SmartViewsList.tsx | 29 - .../components/Tags/SmartViewsListItem.tsx | 167 ---- .../components/Tags/SmartViewsSection.tsx | 18 - .../components/Tags/TagContextMenu.tsx | 118 --- .../components/Tags/TagsSection.tsx | 73 -- .../components/Tags/TagsSectionAddButton.tsx | 24 - .../components/Tags/TagsSectionTitle.tsx | 61 -- .../javascripts/components/Tags/dragndrop.ts | 9 - .../javascripts/components/TitleBar.tsx | 13 - app/assets/javascripts/components/utils.ts | 194 ---- app/assets/javascripts/constants.ts | 18 - app/assets/javascripts/crypto.ts | 3 - .../javascripts/hooks/manageSubscription.ts | 10 - .../javascripts/hooks/useBeforeUnload.tsx | 11 - app/assets/javascripts/index.ts | 6 +- app/assets/javascripts/jest.config.js | 9 +- .../javascripts/services/archiveManager.ts | 168 ---- app/assets/javascripts/services/bridge.ts | 40 - .../javascripts/services/statusManager.ts | 30 - .../javascripts/services/themeManager.ts | 345 ------- app/assets/javascripts/tsconfig.json | 3 +- app/assets/javascripts/types.ts | 12 - .../ui_models/app_state/actions_menu_state.ts | 22 - .../ui_models/app_state/app_state.ts | 482 ---------- .../ui_models/app_state/features_state.ts | 104 -- .../ui_models/app_state/files_state.ts | 193 ---- .../javascripts/ui_models/app_state/index.ts | 6 - .../ui_models/app_state/sync_state.ts | 36 - .../javascripts/ui_models/application.ts | 155 --- .../ui_models/application_group.ts | 81 -- .../utils/calculateSubmenuStyle.tsx | 77 -- .../utils/concatenateUint8Arrays.ts | 14 - app/assets/javascripts/utils/index.ts | 5 - .../javascripts/utils/stringUtils.spec.ts | 65 -- app/assets/javascripts/utils/stringUtils.ts | 39 - app/assets/javascripts/version.ts | 8 - app/assets/stylesheets/_columns.scss | 2 +- app/assets/stylesheets/_ionicons.scss | 1 - app/assets/stylesheets/_main.scss | 51 +- app/assets/stylesheets/_modals.scss | 6 +- app/assets/stylesheets/_notes.scss | 4 +- app/assets/stylesheets/_scrollbar.scss | 2 +- app/assets/stylesheets/_sessions-modal.scss | 4 +- app/assets/stylesheets/_sn.scss | 5 +- app/assets/stylesheets/_stylekit-sub.scss | 14 +- app/assets/stylesheets/_ui.scss | 49 +- package.json | 9 +- yarn.lock | 88 ++ 367 files changed, 13778 insertions(+), 16093 deletions(-) create mode 100644 app/assets/javascripts/App.tsx create mode 100644 app/assets/javascripts/Components/Abstract/PureComponent.tsx rename app/assets/javascripts/{components => Components}/AccountMenu/AdvancedOptions.tsx (71%) rename app/assets/javascripts/{components => Components}/AccountMenu/ConfirmPassword.tsx (60%) rename app/assets/javascripts/{components => Components}/AccountMenu/CreateAccount.tsx (58%) rename app/assets/javascripts/{components => Components}/AccountMenu/GeneralAccountMenu.tsx (69%) rename app/assets/javascripts/{components => Components}/AccountMenu/SignIn.tsx (62%) rename app/assets/javascripts/{components => Components}/AccountMenu/User.tsx (68%) rename app/assets/javascripts/{components => Components}/AccountMenu/WorkspaceSwitcher/WorkspaceMenuItem.tsx (67%) create mode 100644 app/assets/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu.tsx rename app/assets/javascripts/{components => Components}/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx (55%) rename app/assets/javascripts/{components => Components}/AccountMenu/index.tsx (54%) rename app/assets/javascripts/{components/ApplicationGroupView.tsx => Components/ApplicationGroupView/index.tsx} (54%) create mode 100644 app/assets/javascripts/Components/ApplicationView/index.tsx create mode 100644 app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx rename app/assets/javascripts/{components => Components}/AttachedFilesPopover/AttachedFilesPopover.tsx (64%) rename app/assets/javascripts/{components => Components}/AttachedFilesPopover/PopoverFileItem.tsx (62%) create mode 100644 app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItemAction.tsx rename app/assets/javascripts/{components => Components}/AttachedFilesPopover/PopoverFileSubmenu.tsx (74%) rename app/assets/javascripts/{components/Bubble.tsx => Components/Bubble/index.tsx} (70%) rename app/assets/javascripts/{components => Components/Button}/Button.tsx (54%) rename app/assets/javascripts/{components => Components/Button}/IconButton.tsx (66%) create mode 100644 app/assets/javascripts/Components/Button/RoundIconButton.tsx rename app/assets/javascripts/{components => Components}/ChallengeModal/ChallengeModal.tsx (54%) rename app/assets/javascripts/{components => Components}/ChallengeModal/ChallengePrompt.tsx (65%) rename app/assets/javascripts/{components => Components/ChangeEditor}/ChangeEditorButton.tsx (51%) rename app/assets/javascripts/{components/NotesOptions/changeEditor => Components/ChangeEditor}/ChangeEditorMenu.tsx (50%) rename app/assets/javascripts/{components/NotesOptions/changeEditor => Components/ChangeEditor}/createEditorMenuGroups.ts (70%) rename app/assets/javascripts/{components/Checkbox.tsx => Components/Checkbox/index.tsx} (73%) rename app/assets/javascripts/{components => Components}/ComponentView/IsDeprecated.tsx (57%) rename app/assets/javascripts/{components => Components}/ComponentView/IsExpired.tsx (53%) rename app/assets/javascripts/{components => Components}/ComponentView/IssueOnLoading.tsx (59%) rename app/assets/javascripts/{components => Components}/ComponentView/OfflineRestricted.tsx (69%) rename app/assets/javascripts/{components => Components}/ComponentView/UrlMissing.tsx (60%) create mode 100644 app/assets/javascripts/Components/ComponentView/index.tsx create mode 100644 app/assets/javascripts/Components/ConfirmSignoutModal/index.tsx rename app/assets/javascripts/{components/Dropdown.tsx => Components/Dropdown/index.tsx} (74%) rename app/assets/javascripts/{components => Components}/Files/FilePreviewInfoPanel.tsx (70%) rename app/assets/javascripts/{components => Components}/Files/FilePreviewModal.tsx (68%) create mode 100644 app/assets/javascripts/Components/Files/FilePreviewModalProvider.tsx create mode 100644 app/assets/javascripts/Components/Files/isFilePreviewable.ts rename app/assets/javascripts/{components/Footer.tsx => Components/Footer/index.tsx} (57%) rename app/assets/javascripts/{components/Icon.tsx => Components/Icon/index.tsx} (91%) create mode 100644 app/assets/javascripts/Components/Input/DecoratedInput.tsx create mode 100644 app/assets/javascripts/Components/Input/DecoratedInputProps.ts create mode 100644 app/assets/javascripts/Components/Input/DecoratedPasswordInput.tsx rename app/assets/javascripts/{components => Components/Input}/FloatingLabelInput.tsx (51%) create mode 100644 app/assets/javascripts/Components/Input/Input.tsx rename app/assets/javascripts/{components => Components}/Menu/Menu.tsx (53%) rename app/assets/javascripts/{components => Components}/Menu/MenuItem.tsx (51%) create mode 100644 app/assets/javascripts/Components/MultipleSelectedNotes/index.tsx create mode 100644 app/assets/javascripts/Components/Navigation/index.tsx rename app/assets/javascripts/{components/NoAccountWarning.tsx => Components/NoAccountWarning/index.tsx} (62%) create mode 100644 app/assets/javascripts/Components/NoteGroupView/index.tsx rename app/assets/javascripts/{components => Components/NoteTags}/NoteTag.tsx (50%) rename app/assets/javascripts/{components => Components/NoteTags}/NoteTagsContainer.tsx (50%) rename app/assets/javascripts/{components => Components}/NoteView/NoteView.test.ts (73%) rename app/assets/javascripts/{components => Components}/NoteView/NoteView.tsx (60%) create mode 100644 app/assets/javascripts/Components/NotesContextMenu/index.tsx rename app/assets/javascripts/{components => Components/NotesList}/NotesListItem.tsx (70%) rename app/assets/javascripts/{components => Components/NotesList}/NotesListOptionsMenu.tsx (80%) create mode 100644 app/assets/javascripts/Components/NotesList/index.tsx create mode 100644 app/assets/javascripts/Components/NotesOptions/AddTagOption.tsx rename app/assets/javascripts/{components => Components}/NotesOptions/ChangeEditorOption.tsx (57%) rename app/assets/javascripts/{components => Components}/NotesOptions/ListedActionsOption.tsx (68%) rename app/assets/javascripts/{components => Components}/NotesOptions/NotesOptions.tsx (66%) rename app/assets/javascripts/{components => Components/NotesOptions}/NotesOptionsPanel.tsx (51%) create mode 100644 app/assets/javascripts/Components/NotesView/index.tsx create mode 100644 app/assets/javascripts/Components/OtherSessionsSignOut/index.tsx create mode 100644 app/assets/javascripts/Components/PanelResizer/index.tsx rename app/assets/javascripts/{components/PasswordWizard.tsx => Components/PasswordWizard/index.tsx} (66%) rename app/assets/javascripts/{components/PermissionsModal.tsx => Components/PermissionsModal/index.tsx} (77%) create mode 100644 app/assets/javascripts/Components/PinNoteButton/index.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/Advanced.tsx rename app/assets/javascripts/{components/Preferences/panes/account => Components/Preferences/Panes/Account}/Authentication.tsx (59%) rename app/assets/javascripts/{components/Preferences/panes/account/changeEmail => Components/Preferences/Panes/Account/ChangeEmail}/ChangeEmailForm.tsx (74%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailSuccess.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/ChangeEmail/index.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/Credentials.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/OfflineSubscription.tsx rename app/assets/javascripts/{components/Preferences/panes/account => Components/Preferences/Panes/Account}/SignOutView.tsx (63%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/NoSubscription.tsx rename app/assets/javascripts/{components/Preferences/panes/account/subscription => Components/Preferences/Panes/Account/Subscription}/Subscription.tsx (59%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/Subscription/SubscriptionInformation.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/Sync.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Account/index.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Appearance.tsx rename app/assets/javascripts/{components/Preferences/panes/backups-segments/cloud-backups => Components/Preferences/Panes/Backups/CloudBackups}/CloudBackupProvider.tsx (52%) rename app/assets/javascripts/{components/Preferences/panes/backups-segments/cloud-backups => Components/Preferences/Panes/Backups/CloudBackups}/index.tsx (62%) rename app/assets/javascripts/{components/Preferences/panes/backups-segments => Components/Preferences/Panes/Backups}/DataBackups.tsx (57%) rename app/assets/javascripts/{components/Preferences/panes/backups-segments => Components/Preferences/Panes/Backups}/EmailBackups.tsx (57%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Backups/index.tsx rename app/assets/javascripts/{components/Preferences/panes => Components/Preferences/Panes}/CloudLink.tsx (65%) rename app/assets/javascripts/{components/Preferences/panes/extensions-segments => Components/Preferences/Panes/Extensions}/ConfirmCustomExtension.tsx (82%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionItem.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Extensions/ExtensionsLatestVersions.ts rename app/assets/javascripts/{components/Preferences/panes/extensions-segments => Components/Preferences/Panes/Extensions}/RenameExtension.tsx (57%) rename app/assets/javascripts/{components/Preferences/panes/Extensions.tsx => Components/Preferences/Panes/Extensions/index.tsx} (62%) rename app/assets/javascripts/{components/Preferences/panes/general-segments => Components/Preferences/Panes/General}/Defaults.tsx (56%) rename app/assets/javascripts/{components/Preferences/panes/general-segments => Components/Preferences/Panes/General}/Labs.tsx (51%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/General/Tools.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/General/index.tsx rename app/assets/javascripts/{components/Preferences/panes => Components/Preferences/Panes}/HelpFeedback.tsx (65%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Listed/BlogItem.tsx rename app/assets/javascripts/{components/Preferences/panes/Listed.tsx => Components/Preferences/Panes/Listed/index.tsx} (58%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx rename app/assets/javascripts/{components/Preferences/panes/security-segments => Components/Preferences/Panes/Security}/PasscodeLock.tsx (56%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Security/Privacy.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Security/Protections.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Security/index.tsx rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/AuthAppInfoPopup.tsx (56%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/Bullet.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/CopyButton.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/MfaProps.ts rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/SaveSecretKey.tsx (77%) rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/ScanQRCode.tsx (79%) rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/TwoFactorActivation.ts (55%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/TwoFactorActivationView.tsx rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/TwoFactorAuth.ts (51%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/TwoFactorAuthView.tsx rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/TwoFactorSuccess.tsx (57%) rename app/assets/javascripts/{components/Preferences/panes/two-factor-auth => Components/Preferences/Panes/TwoFactorAuth}/Verification.tsx (61%) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/download-secret-key.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/TwoFactorAuth/index.tsx create mode 100644 app/assets/javascripts/Components/Preferences/Panes/index.ts rename app/assets/javascripts/{components/Preferences/components => Components/Preferences/PreferencesComponents}/Content.tsx (57%) create mode 100644 app/assets/javascripts/Components/Preferences/PreferencesComponents/MenuItem.tsx rename app/assets/javascripts/{components/Preferences/components => Components/Preferences/PreferencesComponents}/PreferencesGroup.tsx (58%) rename app/assets/javascripts/{components/Preferences/components => Components/Preferences/PreferencesComponents}/PreferencesPane.tsx (91%) create mode 100644 app/assets/javascripts/Components/Preferences/PreferencesComponents/PreferencesSegment.tsx create mode 100644 app/assets/javascripts/Components/Preferences/PreferencesComponents/index.ts rename app/assets/javascripts/{components => Components}/Preferences/PreferencesMenu.ts (72%) rename app/assets/javascripts/{components => Components}/Preferences/PreferencesMenuView.tsx (64%) create mode 100644 app/assets/javascripts/Components/Preferences/PreferencesView.tsx rename app/assets/javascripts/{components => Components}/Preferences/PreferencesViewWrapper.tsx (50%) create mode 100644 app/assets/javascripts/Components/Preferences/Providers/MfaProvider.ts create mode 100644 app/assets/javascripts/Components/Preferences/Providers/UserProvider.ts create mode 100644 app/assets/javascripts/Components/Preferences/Providers/index.ts rename app/assets/javascripts/{components/PremiumFeaturesModal.tsx => Components/PremiumFeaturesModal/index.tsx} (57%) rename app/assets/javascripts/{components/ProtectedNoteOverlay.tsx => Components/ProtectedNoteOverlay/index.tsx} (63%) create mode 100644 app/assets/javascripts/Components/PurchaseFlow/Panes/CreateAccount.tsx create mode 100644 app/assets/javascripts/Components/PurchaseFlow/Panes/SignIn.tsx rename app/assets/javascripts/{components => Components}/PurchaseFlow/PurchaseFlowView.tsx (70%) create mode 100644 app/assets/javascripts/Components/PurchaseFlow/PurchaseFlowWrapper.tsx rename app/assets/javascripts/{components/QuickSettingsMenu/eventHandlers.ts => Components/QuickSettingsMenu/EventHandlers.ts} (56%) rename app/assets/javascripts/{components => Components}/QuickSettingsMenu/FocusModeSwitch.tsx (57%) create mode 100644 app/assets/javascripts/Components/QuickSettingsMenu/ThemeItem.ts rename app/assets/javascripts/{components => Components}/QuickSettingsMenu/ThemesMenuButton.tsx (51%) rename app/assets/javascripts/{components/QuickSettingsMenu/QuickSettingsMenu.tsx => Components/QuickSettingsMenu/index.tsx} (57%) rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/HistoryListContainer.tsx (57%) rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/HistoryListItem.tsx (63%) rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/LegacyHistoryList.tsx (56%) create mode 100644 app/assets/javascripts/Components/RevisionHistoryModal/RemoteHistoryList.tsx rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/RevisionContentLocked.tsx (62%) rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/RevisionHistoryModalWrapper.tsx (60%) create mode 100644 app/assets/javascripts/Components/RevisionHistoryModal/SelectedRevisionContent.tsx rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/SessionHistoryList.tsx (57%) rename app/assets/javascripts/{components => Components}/RevisionHistoryModal/utils.ts (54%) rename app/assets/javascripts/{components/SearchOptions.tsx => Components/SearchOptions/index.tsx} (67%) rename app/assets/javascripts/{components/SessionsModal.tsx => Components/SessionsModal/index.tsx} (65%) rename app/assets/javascripts/{components => Components}/Shared/AccordionItem.tsx (58%) rename app/assets/javascripts/{components => Components}/Shared/HorizontalSeparator.tsx (56%) rename app/assets/javascripts/{components => Components}/Shared/ModalDialog.tsx (76%) rename app/assets/javascripts/{components/Switch.tsx => Components/Switch/index.tsx} (55%) rename app/assets/javascripts/{components/SyncResolutionMenu.tsx => Components/SyncResolutionMenu/index.tsx} (59%) create mode 100644 app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagHint.tsx create mode 100644 app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagInput.tsx create mode 100644 app/assets/javascripts/Components/TagAutocomplete/AutocompleteTagResult.tsx create mode 100644 app/assets/javascripts/Components/Tags/DragNDrop.ts create mode 100644 app/assets/javascripts/Components/Tags/RootTagDropZone.tsx create mode 100644 app/assets/javascripts/Components/Tags/SmartViewsList.tsx create mode 100644 app/assets/javascripts/Components/Tags/SmartViewsListItem.tsx create mode 100644 app/assets/javascripts/Components/Tags/SmartViewsSection.tsx create mode 100644 app/assets/javascripts/Components/Tags/TagContextMenu.tsx rename app/assets/javascripts/{components => Components}/Tags/TagsList.tsx (51%) rename app/assets/javascripts/{components => Components}/Tags/TagsListItem.tsx (55%) create mode 100644 app/assets/javascripts/Components/Tags/TagsSection.tsx create mode 100644 app/assets/javascripts/Components/Tags/TagsSectionAddButton.tsx create mode 100644 app/assets/javascripts/Components/Tags/TagsSectionTitle.tsx create mode 100644 app/assets/javascripts/Components/TitleBar/Title.tsx create mode 100644 app/assets/javascripts/Components/TitleBar/TitleBar.tsx create mode 100644 app/assets/javascripts/Constants.ts create mode 100644 app/assets/javascripts/Crypto.ts rename app/assets/javascripts/{database.ts => Database.ts} (50%) rename app/assets/javascripts/{element_ids.ts => ElementIDs.ts} (98%) rename app/assets/javascripts/{enums.ts => Enums.ts} (100%) create mode 100644 app/assets/javascripts/Hooks/useBeforeUnload.tsx create mode 100644 app/assets/javascripts/Hooks/useCloseOnBlur.ts create mode 100644 app/assets/javascripts/Hooks/useCloseOnClickOutside.ts create mode 100644 app/assets/javascripts/Hooks/useListKeyboardNavigation.ts create mode 100644 app/assets/javascripts/Hooks/usePremiumModal.tsx rename app/assets/javascripts/{services/alertService.ts => Services/AlertService.ts} (71%) create mode 100644 app/assets/javascripts/Services/ArchiveManager.ts rename app/assets/javascripts/{services/autolock_service.ts => Services/AutolockService.ts} (59%) create mode 100644 app/assets/javascripts/Services/Bridge.ts rename app/assets/javascripts/{services/browserBridge.ts => Services/BrowserBridge.ts} (69%) rename app/assets/javascripts/{services/desktopManager.ts => Services/DesktopManager.ts} (58%) rename app/assets/javascripts/{services/ioService.ts => Services/IOService.ts} (50%) rename app/assets/javascripts/{services/localStorage.ts => Services/LocalStorage.ts} (55%) create mode 100644 app/assets/javascripts/Services/StatusManager.ts create mode 100644 app/assets/javascripts/Services/ThemeManager.ts rename app/assets/javascripts/{startApplication.ts => StartApplication.ts} (58%) rename app/assets/javascripts/{strings.ts => Strings.ts} (75%) create mode 100644 app/assets/javascripts/Types.ts rename app/assets/javascripts/{typings => Typings}/hoist-non-react-statics.d.ts (61%) rename app/assets/javascripts/{ui_models/app_state/account_menu_state.ts => UIModels/AppState/AccountMenuState.ts} (54%) create mode 100644 app/assets/javascripts/UIModels/AppState/ActionsMenuState.ts create mode 100644 app/assets/javascripts/UIModels/AppState/AppState.ts create mode 100644 app/assets/javascripts/UIModels/AppState/FeaturesState.ts create mode 100644 app/assets/javascripts/UIModels/AppState/FilesState.ts rename app/assets/javascripts/{ui_models/app_state/no_account_warning_state.ts => UIModels/AppState/NoAccountWarningState.ts} (61%) rename app/assets/javascripts/{ui_models/app_state/note_tags_state.ts => UIModels/AppState/NoteTagsState.ts} (50%) rename app/assets/javascripts/{ui_models/app_state/notes_state.ts => UIModels/AppState/NotesState.ts} (51%) rename app/assets/javascripts/{ui_models/app_state/notes_view_state.ts => UIModels/AppState/NotesViewState.ts} (54%) rename app/assets/javascripts/{ui_models/app_state/preferences_state.ts => UIModels/AppState/PreferencesState.ts} (59%) rename app/assets/javascripts/{ui_models/app_state/purchase_flow_state.ts => UIModels/AppState/PurchaseFlowState.ts} (52%) rename app/assets/javascripts/{ui_models/app_state/quick_settings_state.ts => UIModels/AppState/QuickSettingsState.ts} (57%) rename app/assets/javascripts/{ui_models/app_state/search_options_state.ts => UIModels/AppState/SearchOptionsState.ts} (54%) rename app/assets/javascripts/{ui_models/app_state/subscription_state.ts => UIModels/AppState/SubscriptionState.ts} (64%) create mode 100644 app/assets/javascripts/UIModels/AppState/SyncState.ts rename app/assets/javascripts/{ui_models/app_state/tags_state.ts => UIModels/AppState/TagsState.ts} (52%) create mode 100644 app/assets/javascripts/UIModels/AppState/index.ts create mode 100644 app/assets/javascripts/UIModels/Application.ts create mode 100644 app/assets/javascripts/UIModels/ApplicationGroup.ts create mode 100644 app/assets/javascripts/Utils/CalculateDifferenceBetweenDatesInDays.ts create mode 100644 app/assets/javascripts/Utils/CalculateSubmenuStyle.tsx create mode 100644 app/assets/javascripts/Utils/ConcatenateUint8Arrays.ts rename app/assets/javascripts/{utils/isMobile.ts => Utils/IsMobile.ts} (91%) create mode 100644 app/assets/javascripts/Utils/ManageSubscription.ts create mode 100644 app/assets/javascripts/Utils/SortThemes.ts create mode 100644 app/assets/javascripts/Utils/StringUtils.spec.ts create mode 100644 app/assets/javascripts/Utils/StringUtils.ts rename app/assets/javascripts/{utils/utils.ts => Utils/Utils.ts} (55%) create mode 100644 app/assets/javascripts/Utils/index.ts create mode 100644 app/assets/javascripts/Version.ts rename app/assets/javascripts/{web_device_interface.ts => WebDeviceInterface.ts} (50%) delete mode 100644 app/assets/javascripts/app.tsx delete mode 100644 app/assets/javascripts/components/Abstract/PureComponent.tsx delete mode 100644 app/assets/javascripts/components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu.tsx delete mode 100644 app/assets/javascripts/components/ApplicationView.tsx delete mode 100644 app/assets/javascripts/components/AttachedFilesPopover/AttachedFilesButton.tsx delete mode 100644 app/assets/javascripts/components/AttachedFilesPopover/PopoverFileItemAction.tsx delete mode 100644 app/assets/javascripts/components/AutocompleteTagHint.tsx delete mode 100644 app/assets/javascripts/components/AutocompleteTagInput.tsx delete mode 100644 app/assets/javascripts/components/AutocompleteTagResult.tsx delete mode 100644 app/assets/javascripts/components/ComponentView/index.tsx delete mode 100644 app/assets/javascripts/components/ConfirmSignoutModal.tsx delete mode 100644 app/assets/javascripts/components/ConfirmationDialog.tsx delete mode 100644 app/assets/javascripts/components/DecoratedInput.tsx delete mode 100644 app/assets/javascripts/components/DecoratedPasswordInput.tsx delete mode 100644 app/assets/javascripts/components/Files/FilePreviewModalProvider.tsx delete mode 100644 app/assets/javascripts/components/Files/isFilePreviewable.ts delete mode 100644 app/assets/javascripts/components/Input.tsx delete mode 100644 app/assets/javascripts/components/MenuRow.tsx delete mode 100644 app/assets/javascripts/components/MultipleSelectedNotes.tsx delete mode 100644 app/assets/javascripts/components/Navigation.tsx delete mode 100644 app/assets/javascripts/components/NoteGroupView.tsx delete mode 100644 app/assets/javascripts/components/NotesContextMenu.tsx delete mode 100644 app/assets/javascripts/components/NotesList.tsx delete mode 100644 app/assets/javascripts/components/NotesOptions/AddTagOption.tsx delete mode 100644 app/assets/javascripts/components/NotesView.tsx delete mode 100644 app/assets/javascripts/components/OtherSessionsSignOut.tsx delete mode 100644 app/assets/javascripts/components/PanelResizer.tsx delete mode 100644 app/assets/javascripts/components/PinNoteButton.tsx delete mode 100644 app/assets/javascripts/components/Preferences/PreferencesView.tsx delete mode 100644 app/assets/javascripts/components/Preferences/components/MenuItem.tsx delete mode 100644 app/assets/javascripts/components/Preferences/components/PreferencesSegment.tsx delete mode 100644 app/assets/javascripts/components/Preferences/components/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/AccountPreferences.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/Appearance.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/Backups.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/General.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/Security.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/Advanced.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/Credentials.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/Sync.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/changeEmail/ChangeEmailSuccess.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/changeEmail/index.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/offlineSubscription.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/subscription/NoSubscription.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/account/subscription/SubscriptionInformation.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/backups-segments/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/extensions-segments/ExtensionItem.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/extensions-segments/ExtensionsLatestVersions.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/extensions-segments/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/general-segments/Tools.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/general-segments/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/listed/BlogItem.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/security-segments/Encryption.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/security-segments/Privacy.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/security-segments/Protections.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/security-segments/index.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/Bullet.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/CopyButton.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/MfaProps.ts delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/TwoFactorActivationView.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/TwoFactorAuthView.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/download-secret-key.tsx delete mode 100644 app/assets/javascripts/components/Preferences/panes/two-factor-auth/index.tsx delete mode 100644 app/assets/javascripts/components/Preferences/providers/MfaProvider.ts delete mode 100644 app/assets/javascripts/components/Preferences/providers/UserProvider.ts delete mode 100644 app/assets/javascripts/components/Preferences/providers/index.ts delete mode 100644 app/assets/javascripts/components/Premium/index.ts delete mode 100644 app/assets/javascripts/components/Premium/usePremiumModal.tsx delete mode 100644 app/assets/javascripts/components/PurchaseFlow/PurchaseFlowWrapper.tsx delete mode 100644 app/assets/javascripts/components/PurchaseFlow/panes/CreateAccount.tsx delete mode 100644 app/assets/javascripts/components/PurchaseFlow/panes/SignIn.tsx delete mode 100644 app/assets/javascripts/components/RevisionHistoryModal/RemoteHistoryList.tsx delete mode 100644 app/assets/javascripts/components/RevisionHistoryModal/SelectedRevisionContent.tsx delete mode 100644 app/assets/javascripts/components/RevisionPreviewModal.tsx delete mode 100644 app/assets/javascripts/components/RoundIconButton.tsx delete mode 100644 app/assets/javascripts/components/Tags/RootTagDropZone.tsx delete mode 100644 app/assets/javascripts/components/Tags/SmartViewsList.tsx delete mode 100644 app/assets/javascripts/components/Tags/SmartViewsListItem.tsx delete mode 100644 app/assets/javascripts/components/Tags/SmartViewsSection.tsx delete mode 100644 app/assets/javascripts/components/Tags/TagContextMenu.tsx delete mode 100644 app/assets/javascripts/components/Tags/TagsSection.tsx delete mode 100644 app/assets/javascripts/components/Tags/TagsSectionAddButton.tsx delete mode 100644 app/assets/javascripts/components/Tags/TagsSectionTitle.tsx delete mode 100644 app/assets/javascripts/components/Tags/dragndrop.ts delete mode 100644 app/assets/javascripts/components/TitleBar.tsx delete mode 100644 app/assets/javascripts/components/utils.ts delete mode 100644 app/assets/javascripts/constants.ts delete mode 100644 app/assets/javascripts/crypto.ts delete mode 100644 app/assets/javascripts/hooks/manageSubscription.ts delete mode 100644 app/assets/javascripts/hooks/useBeforeUnload.tsx delete mode 100644 app/assets/javascripts/services/archiveManager.ts delete mode 100644 app/assets/javascripts/services/bridge.ts delete mode 100644 app/assets/javascripts/services/statusManager.ts delete mode 100644 app/assets/javascripts/services/themeManager.ts delete mode 100644 app/assets/javascripts/types.ts delete mode 100644 app/assets/javascripts/ui_models/app_state/actions_menu_state.ts delete mode 100644 app/assets/javascripts/ui_models/app_state/app_state.ts delete mode 100644 app/assets/javascripts/ui_models/app_state/features_state.ts delete mode 100644 app/assets/javascripts/ui_models/app_state/files_state.ts delete mode 100644 app/assets/javascripts/ui_models/app_state/index.ts delete mode 100644 app/assets/javascripts/ui_models/app_state/sync_state.ts delete mode 100644 app/assets/javascripts/ui_models/application.ts delete mode 100644 app/assets/javascripts/ui_models/application_group.ts delete mode 100644 app/assets/javascripts/utils/calculateSubmenuStyle.tsx delete mode 100644 app/assets/javascripts/utils/concatenateUint8Arrays.ts delete mode 100644 app/assets/javascripts/utils/index.ts delete mode 100644 app/assets/javascripts/utils/stringUtils.spec.ts delete mode 100644 app/assets/javascripts/utils/stringUtils.ts delete mode 100644 app/assets/javascripts/version.ts diff --git a/.eslintrc b/.eslintrc index b572d609c..8219c6088 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,8 @@ "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier", - "plugin:react-hooks/recommended" + "plugin:react-hooks/recommended", + "./node_modules/@standardnotes/config/src/.eslintrc" ], "plugins": ["@typescript-eslint", "react", "react-hooks"], "parserOptions": { @@ -23,7 +24,9 @@ "react-hooks/exhaustive-deps": "error", "eol-last": "error", "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }], - "no-trailing-spaces": "error" + "no-trailing-spaces": "error", + "@typescript-eslint/no-explicit-any": "warn", + "no-invalid-this": "warn" }, "env": { "browser": true diff --git a/.prettierrc b/.prettierrc index 9e74d98a6..cb8ee2671 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,6 @@ { "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "semi": false } diff --git a/app/assets/javascripts/@types/modules.ts b/app/assets/javascripts/@types/modules.ts index b5518926f..8b139eba2 100644 --- a/app/assets/javascripts/@types/modules.ts +++ b/app/assets/javascripts/@types/modules.ts @@ -1,3 +1,3 @@ declare module '*.svg' { - export default function SvgComponent(props: React.SVGProps): JSX.Element; + export default function SvgComponent(props: React.SVGProps): JSX.Element } diff --git a/app/assets/javascripts/@types/qrcode.react.d.ts b/app/assets/javascripts/@types/qrcode.react.d.ts index f997f01d9..c30c2c732 100644 --- a/app/assets/javascripts/@types/qrcode.react.d.ts +++ b/app/assets/javascripts/@types/qrcode.react.d.ts @@ -1 +1 @@ -declare module 'qrcode.react'; +declare module 'qrcode.react' diff --git a/app/assets/javascripts/App.tsx b/app/assets/javascripts/App.tsx new file mode 100644 index 000000000..0cb0d0f49 --- /dev/null +++ b/app/assets/javascripts/App.tsx @@ -0,0 +1,79 @@ +'use strict' + +declare global { + interface Window { + dashboardUrl?: string + defaultSyncServer: string + devAccountEmail?: string + devAccountPassword?: string + devAccountServer?: string + enabledUnfinishedFeatures: boolean + plansUrl?: string + purchaseUrl?: string + startApplication?: StartApplication + websocketUrl: string + electronAppVersion?: string + } +} + +import { IsWebPlatform, WebAppVersion } from '@/Version' +import { Runtime, SNLog } from '@standardnotes/snjs' +import { render } from 'preact' +import { ApplicationGroupView } from './Components/ApplicationGroupView' +import { Bridge } from './Services/Bridge' +import { BrowserBridge } from './Services/BrowserBridge' +import { StartApplication } from './StartApplication' +import { ApplicationGroup } from './UIModels/ApplicationGroup' +import { isDev } from './Utils' + +const startApplication: StartApplication = async function startApplication( + defaultSyncServerHost: string, + bridge: Bridge, + enableUnfinishedFeatures: boolean, + webSocketUrl: string, +) { + SNLog.onLog = console.log + SNLog.onError = console.error + + const mainApplicationGroup = new ApplicationGroup( + defaultSyncServerHost, + bridge, + enableUnfinishedFeatures ? Runtime.Dev : Runtime.Prod, + webSocketUrl, + ) + + if (isDev) { + Object.defineProperties(window, { + application: { + get: () => mainApplicationGroup.primaryApplication, + }, + }) + } + + const renderApp = () => { + render( + , + document.body.appendChild(document.createElement('div')), + ) + } + + const domReady = document.readyState === 'complete' || document.readyState === 'interactive' + if (domReady) { + renderApp() + } else { + window.addEventListener('DOMContentLoaded', () => { + renderApp() + }) + } +} + +if (IsWebPlatform) { + startApplication( + window.defaultSyncServer, + new BrowserBridge(WebAppVersion), + window.enabledUnfinishedFeatures, + window.websocketUrl, + ).catch(console.error) +} else { + window.startApplication = startApplication +} diff --git a/app/assets/javascripts/Components/Abstract/PureComponent.tsx b/app/assets/javascripts/Components/Abstract/PureComponent.tsx new file mode 100644 index 000000000..6351b53bd --- /dev/null +++ b/app/assets/javascripts/Components/Abstract/PureComponent.tsx @@ -0,0 +1,135 @@ +import { ApplicationEvent } from '@standardnotes/snjs' +import { WebApplication } from '@/UIModels/Application' +import { AppState, AppStateEvent } from '@/UIModels/AppState' +import { autorun, IReactionDisposer, IReactionPublic } from 'mobx' +import { Component } from 'preact' +import { findDOMNode, unmountComponentAtNode } from 'preact/compat' + +export type PureComponentState = Partial> +export type PureComponentProps = Partial> + +export abstract class PureComponent< + P = PureComponentProps, + S = PureComponentState, +> extends Component { + private unsubApp!: () => void + private unsubState!: () => void + private reactionDisposers: IReactionDisposer[] = [] + + constructor(props: P, protected application: WebApplication) { + super(props) + } + + override componentDidMount() { + this.addAppEventObserver() + this.addAppStateObserver() + } + + deinit(): void { + this.unsubApp?.() + this.unsubState?.() + for (const disposer of this.reactionDisposers) { + disposer() + } + this.reactionDisposers.length = 0 + ;(this.unsubApp as unknown) = undefined + ;(this.unsubState as unknown) = undefined + } + + protected dismissModal(): void { + const elem = this.getElement() + if (!elem) { + return + } + + const parent = elem.parentElement + if (!parent) { + return + } + parent.remove() + unmountComponentAtNode(parent) + } + + override componentWillUnmount(): void { + this.deinit() + } + + render() { + return
Must override
+ } + + public get appState(): AppState { + return this.application.getAppState() + } + + protected getElement(): Element | null { + return findDOMNode(this) + } + + autorun(view: (r: IReactionPublic) => void): void { + this.reactionDisposers.push(autorun(view)) + } + + addAppStateObserver() { + this.unsubState = this.application.getAppState().addObserver(async (eventName, data) => { + this.onAppStateEvent(eventName, data) + }) + } + + onAppStateEvent(_eventName: AppStateEvent, _data: unknown) { + /** Optional override */ + } + + addAppEventObserver() { + if (this.application.isStarted()) { + this.onAppStart().catch(console.error) + } + if (this.application.isLaunched()) { + this.onAppLaunch().catch(console.error) + } + this.unsubApp = this.application.addEventObserver(async (eventName, data: unknown) => { + this.onAppEvent(eventName, data) + if (eventName === ApplicationEvent.Started) { + await this.onAppStart() + } else if (eventName === ApplicationEvent.Launched) { + await this.onAppLaunch() + } else if (eventName === ApplicationEvent.CompletedIncrementalSync) { + this.onAppIncrementalSync() + } else if (eventName === ApplicationEvent.CompletedFullSync) { + this.onAppFullSync() + } else if (eventName === ApplicationEvent.KeyStatusChanged) { + this.onAppKeyChange().catch(console.error) + } else if (eventName === ApplicationEvent.LocalDataLoaded) { + this.onLocalDataLoaded() + } + }) + } + + onAppEvent(_eventName: ApplicationEvent, _data?: unknown) { + /** Optional override */ + } + + async onAppStart() { + /** Optional override */ + } + + onLocalDataLoaded() { + /** Optional override */ + } + + async onAppLaunch() { + /** Optional override */ + } + + async onAppKeyChange() { + /** Optional override */ + } + + onAppIncrementalSync() { + /** Optional override */ + } + + onAppFullSync() { + /** Optional override */ + } +} diff --git a/app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx b/app/assets/javascripts/Components/AccountMenu/AdvancedOptions.tsx similarity index 71% rename from app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx rename to app/assets/javascripts/Components/AccountMenu/AdvancedOptions.tsx index ec67e0ed3..266660f11 100644 --- a/app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx +++ b/app/assets/javascripts/Components/AccountMenu/AdvancedOptions.tsx @@ -1,96 +1,85 @@ -import { WebApplication } from '@/ui_models/application'; -import { AppState } from '@/ui_models/app_state'; -import { observer } from 'mobx-react-lite'; -import { FunctionComponent } from 'preact'; -import { useEffect, useState } from 'preact/hooks'; -import { Checkbox } from '../Checkbox'; -import { DecoratedInput } from '../DecoratedInput'; -import { Icon } from '../Icon'; +import { WebApplication } from '@/UIModels/Application' +import { AppState } from '@/UIModels/AppState' +import { observer } from 'mobx-react-lite' +import { FunctionComponent } from 'preact' +import { useEffect, useState } from 'preact/hooks' +import { Checkbox } from '@/Components/Checkbox' +import { DecoratedInput } from '@/Components/Input/DecoratedInput' +import { Icon } from '@/Components/Icon' type Props = { - application: WebApplication; - appState: AppState; - disabled?: boolean; - onVaultChange?: (isVault: boolean, vaultedEmail?: string) => void; - onStrictSignInChange?: (isStrictSignIn: boolean) => void; -}; + application: WebApplication + appState: AppState + disabled?: boolean + onVaultChange?: (isVault: boolean, vaultedEmail?: string) => void + onStrictSignInChange?: (isStrictSignIn: boolean) => void +} export const AdvancedOptions: FunctionComponent = observer( - ({ - appState, - application, - disabled = false, - onVaultChange, - onStrictSignInChange, - children, - }) => { - const { server, setServer, enableServerOption, setEnableServerOption } = - appState.accountMenu; - const [showAdvanced, setShowAdvanced] = useState(false); + ({ appState, application, disabled = false, onVaultChange, onStrictSignInChange, children }) => { + const { server, setServer, enableServerOption, setEnableServerOption } = appState.accountMenu + const [showAdvanced, setShowAdvanced] = useState(false) - const [isVault, setIsVault] = useState(false); - const [vaultName, setVaultName] = useState(''); - const [vaultUserphrase, setVaultUserphrase] = useState(''); + const [isVault, setIsVault] = useState(false) + const [vaultName, setVaultName] = useState('') + const [vaultUserphrase, setVaultUserphrase] = useState('') - const [isStrictSignin, setIsStrictSignin] = useState(false); + const [isStrictSignin, setIsStrictSignin] = useState(false) useEffect(() => { const recomputeVaultedEmail = async () => { - const vaultedEmail = await application.vaultToEmail( - vaultName, - vaultUserphrase - ); + const vaultedEmail = await application.vaultToEmail(vaultName, vaultUserphrase) if (!vaultedEmail) { if (vaultName?.length > 0 && vaultUserphrase?.length > 0) { - application.alertService.alert('Unable to compute vault name.'); + application.alertService.alert('Unable to compute vault name.').catch(console.error) } - return; + return } - onVaultChange?.(true, vaultedEmail); - }; + onVaultChange?.(true, vaultedEmail) + } if (vaultName && vaultUserphrase) { - recomputeVaultedEmail(); + recomputeVaultedEmail().catch(console.error) } - }, [vaultName, vaultUserphrase, application, onVaultChange]); + }, [vaultName, vaultUserphrase, application, onVaultChange]) useEffect(() => { - onVaultChange?.(isVault); - }, [isVault, onVaultChange]); + onVaultChange?.(isVault) + }, [isVault, onVaultChange]) const handleIsVaultChange = () => { - setIsVault(!isVault); - }; + setIsVault(!isVault) + } const handleVaultNameChange = (name: string) => { - setVaultName(name); - }; + setVaultName(name) + } const handleVaultUserphraseChange = (userphrase: string) => { - setVaultUserphrase(userphrase); - }; + setVaultUserphrase(userphrase) + } const handleServerOptionChange = (e: Event) => { if (e.target instanceof HTMLInputElement) { - setEnableServerOption(e.target.checked); + setEnableServerOption(e.target.checked) } - }; + } const handleSyncServerChange = (server: string) => { - setServer(server); - application.setCustomHost(server); - }; + setServer(server) + application.setCustomHost(server).catch(console.error) + } const handleStrictSigninChange = () => { - const newValue = !isStrictSignin; - setIsStrictSignin(newValue); - onStrictSignInChange?.(newValue); - }; + const newValue = !isStrictSignin + setIsStrictSignin(newValue) + onStrictSignInChange?.(newValue) + } const toggleShowAdvanced = () => { - setShowAdvanced(!showAdvanced); - }; + setShowAdvanced(!showAdvanced) + } return ( <> @@ -130,7 +119,7 @@ export const AdvancedOptions: FunctionComponent = observer( {appState.enableUnfinishedFeatures && isVault && ( <> ]} type="text" placeholder="Vault name" @@ -139,7 +128,7 @@ export const AdvancedOptions: FunctionComponent = observer( disabled={disabled} /> ]} type="text" placeholder="Vault userphrase" @@ -188,6 +177,6 @@ export const AdvancedOptions: FunctionComponent = observer( ) : null} - ); - } -); + ) + }, +) diff --git a/app/assets/javascripts/components/AccountMenu/ConfirmPassword.tsx b/app/assets/javascripts/Components/AccountMenu/ConfirmPassword.tsx similarity index 60% rename from app/assets/javascripts/components/AccountMenu/ConfirmPassword.tsx rename to app/assets/javascripts/Components/AccountMenu/ConfirmPassword.tsx index cbb981c8c..bf633af22 100644 --- a/app/assets/javascripts/components/AccountMenu/ConfirmPassword.tsx +++ b/app/assets/javascripts/Components/AccountMenu/ConfirmPassword.tsx @@ -1,96 +1,96 @@ -import { STRING_NON_MATCHING_PASSWORDS } from '@/strings'; -import { WebApplication } from '@/ui_models/application'; -import { AppState } from '@/ui_models/app_state'; -import { observer } from 'mobx-react-lite'; -import { FunctionComponent } from 'preact'; -import { useEffect, useRef, useState } from 'preact/hooks'; -import { AccountMenuPane } from '.'; -import { Button } from '../Button'; -import { Checkbox } from '../Checkbox'; -import { DecoratedPasswordInput } from '../DecoratedPasswordInput'; -import { Icon } from '../Icon'; -import { IconButton } from '../IconButton'; +import { STRING_NON_MATCHING_PASSWORDS } from '@/Strings' +import { WebApplication } from '@/UIModels/Application' +import { AppState } from '@/UIModels/AppState' +import { observer } from 'mobx-react-lite' +import { FunctionComponent } from 'preact' +import { useEffect, useRef, useState } from 'preact/hooks' +import { AccountMenuPane } from '.' +import { Button } from '@/Components/Button/Button' +import { Checkbox } from '@/Components/Checkbox' +import { DecoratedPasswordInput } from '@/Components/Input/DecoratedPasswordInput' +import { Icon } from '@/Components/Icon' +import { IconButton } from '@/Components/Button/IconButton' type Props = { - appState: AppState; - application: WebApplication; - setMenuPane: (pane: AccountMenuPane) => void; - email: string; - password: string; -}; + appState: AppState + application: WebApplication + setMenuPane: (pane: AccountMenuPane) => void + email: string + password: string +} export const ConfirmPassword: FunctionComponent = observer( ({ application, appState, setMenuPane, email, password }) => { - const { notesAndTagsCount } = appState.accountMenu; - const [confirmPassword, setConfirmPassword] = useState(''); - const [isRegistering, setIsRegistering] = useState(false); - const [isEphemeral, setIsEphemeral] = useState(false); - const [shouldMergeLocal, setShouldMergeLocal] = useState(true); - const [error, setError] = useState(''); + const { notesAndTagsCount } = appState.accountMenu + const [confirmPassword, setConfirmPassword] = useState('') + const [isRegistering, setIsRegistering] = useState(false) + const [isEphemeral, setIsEphemeral] = useState(false) + const [shouldMergeLocal, setShouldMergeLocal] = useState(true) + const [error, setError] = useState('') - const passwordInputRef = useRef(null); + const passwordInputRef = useRef(null) useEffect(() => { - passwordInputRef.current?.focus(); - }, []); + passwordInputRef.current?.focus() + }, []) const handlePasswordChange = (text: string) => { - setConfirmPassword(text); - }; + setConfirmPassword(text) + } const handleEphemeralChange = () => { - setIsEphemeral(!isEphemeral); - }; + setIsEphemeral(!isEphemeral) + } const handleShouldMergeChange = () => { - setShouldMergeLocal(!shouldMergeLocal); - }; + setShouldMergeLocal(!shouldMergeLocal) + } const handleKeyDown = (e: KeyboardEvent) => { if (error.length) { - setError(''); + setError('') } if (e.key === 'Enter') { - handleConfirmFormSubmit(e); + handleConfirmFormSubmit(e) } - }; + } const handleConfirmFormSubmit = (e: Event) => { - e.preventDefault(); + e.preventDefault() if (!password) { - passwordInputRef.current?.focus(); - return; + passwordInputRef.current?.focus() + return } if (password === confirmPassword) { - setIsRegistering(true); + setIsRegistering(true) application .register(email, password, isEphemeral, shouldMergeLocal) .then((res) => { if (res.error) { - throw new Error(res.error.message); + throw new Error(res.error.message) } - appState.accountMenu.closeAccountMenu(); - appState.accountMenu.setCurrentPane(AccountMenuPane.GeneralMenu); + appState.accountMenu.closeAccountMenu() + appState.accountMenu.setCurrentPane(AccountMenuPane.GeneralMenu) }) .catch((err) => { - console.error(err); - setError(err.message); + console.error(err) + setError(err.message) }) .finally(() => { - setIsRegistering(false); - }); + setIsRegistering(false) + }) } else { - setError(STRING_NON_MATCHING_PASSWORDS); - setConfirmPassword(''); - passwordInputRef.current?.focus(); + setError(STRING_NON_MATCHING_PASSWORDS) + setConfirmPassword('') + passwordInputRef.current?.focus() } - }; + } const handleGoBack = () => { - setMenuPane(AccountMenuPane.Register); - }; + setMenuPane(AccountMenuPane.Register) + } return ( <> @@ -110,8 +110,7 @@ export const ConfirmPassword: FunctionComponent = observer( Standard Notes does not have a password reset option - . If you forget your password, you will permanently lose access to - your data. + . If you forget your password, you will permanently lose access to your data.
= observer( {error ?
{error}
: null} )} @@ -179,7 +164,7 @@ export const AttachedFilesPopover: FunctionComponent = observer( getIconType={application.iconsController.getIconForFileType} closeOnBlur={closeOnBlur} /> - ); + ) }) ) : (
@@ -193,17 +178,10 @@ export const AttachedFilesPopover: FunctionComponent = observer( ? 'No files attached to this note' : 'No files found in this account'}
- -
- Or drop your files here -
+
Or drop your files here
)} @@ -214,13 +192,10 @@ export const AttachedFilesPopover: FunctionComponent = observer( onBlur={closeOnBlur} > - {currentTab === PopoverTabs.AttachedFiles - ? 'Attach' - : 'Upload'}{' '} - files + {currentTab === PopoverTabs.AttachedFiles ? 'Attach' : 'Upload'} files )} - ); - } -); + ) + }, +) diff --git a/app/assets/javascripts/components/AttachedFilesPopover/PopoverFileItem.tsx b/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItem.tsx similarity index 62% rename from app/assets/javascripts/components/AttachedFilesPopover/PopoverFileItem.tsx rename to app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItem.tsx index 1f6981f47..d5b61ceb3 100644 --- a/app/assets/javascripts/components/AttachedFilesPopover/PopoverFileItem.tsx +++ b/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItem.tsx @@ -1,29 +1,26 @@ -import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants'; -import { KeyboardKey } from '@/services/ioService'; -import { formatSizeToReadableString } from '@standardnotes/filepicker'; -import { IconType, SNFile } from '@standardnotes/snjs'; -import { FunctionComponent } from 'preact'; -import { useEffect, useRef, useState } from 'preact/hooks'; -import { Icon, ICONS } from '../Icon'; -import { - PopoverFileItemAction, - PopoverFileItemActionType, -} from './PopoverFileItemAction'; -import { PopoverFileSubmenu } from './PopoverFileSubmenu'; +import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' +import { KeyboardKey } from '@/Services/IOService' +import { formatSizeToReadableString } from '@standardnotes/filepicker' +import { IconType, SNFile } from '@standardnotes/snjs' +import { FunctionComponent } from 'preact' +import { useEffect, useRef, useState } from 'preact/hooks' +import { Icon, ICONS } from '@/Components/Icon' +import { PopoverFileItemAction, PopoverFileItemActionType } from './PopoverFileItemAction' +import { PopoverFileSubmenu } from './PopoverFileSubmenu' export const getFileIconComponent = (iconType: string, className: string) => { - const IconComponent = ICONS[iconType as keyof typeof ICONS]; + const IconComponent = ICONS[iconType as keyof typeof ICONS] - return ; -}; + return +} export type PopoverFileItemProps = { - file: SNFile; - isAttachedToNote: boolean; - handleFileAction: (action: PopoverFileItemAction) => Promise; - getIconType(type: string): IconType; - closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void; -}; + file: SNFile + isAttachedToNote: boolean + handleFileAction: (action: PopoverFileItemAction) => Promise + getIconType(type: string): IconType + closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void +} export const PopoverFileItem: FunctionComponent = ({ file, @@ -32,16 +29,16 @@ export const PopoverFileItem: FunctionComponent = ({ getIconType, closeOnBlur, }) => { - const [fileName, setFileName] = useState(file.name); - const [isRenamingFile, setIsRenamingFile] = useState(false); - const itemRef = useRef(null); - const fileNameInputRef = useRef(null); + const [fileName, setFileName] = useState(file.name) + const [isRenamingFile, setIsRenamingFile] = useState(false) + const itemRef = useRef(null) + const fileNameInputRef = useRef(null) useEffect(() => { if (isRenamingFile) { - fileNameInputRef.current?.focus(); + fileNameInputRef.current?.focus() } - }, [isRenamingFile]); + }, [isRenamingFile]) const renameFile = async (file: SNFile, name: string) => { await handleFileAction({ @@ -50,23 +47,23 @@ export const PopoverFileItem: FunctionComponent = ({ file, name, }, - }); - setIsRenamingFile(false); - }; + }) + setIsRenamingFile(false) + } const handleFileNameInput = (event: Event) => { - setFileName((event.target as HTMLInputElement).value); - }; + setFileName((event.target as HTMLInputElement).value) + } const handleFileNameInputKeyDown = (event: KeyboardEvent) => { if (event.key === KeyboardKey.Enter) { - itemRef.current?.focus(); + itemRef.current?.focus() } - }; + } const handleFileNameInputBlur = () => { - renameFile(file, fileName); - }; + renameFile(file, fileName).catch(console.error) + } return (
= ({ tabIndex={FOCUSABLE_BUT_NOT_TABBABLE} >
- {getFileIconComponent( - getIconType(file.mimeType), - 'w-8 h-8 flex-shrink-0' - )} + {getFileIconComponent(getIconType(file.mimeType), 'w-8 h-8 flex-shrink-0')}
{isRenamingFile ? ( = ({
)}
- {file.created_at.toLocaleString()} ·{' '} - {formatSizeToReadableString(file.size)} + {file.created_at.toLocaleString()} · {formatSizeToReadableString(file.size)}
@@ -115,5 +108,5 @@ export const PopoverFileItem: FunctionComponent = ({ closeOnBlur={closeOnBlur} /> - ); -}; + ) +} diff --git a/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItemAction.tsx b/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItemAction.tsx new file mode 100644 index 000000000..b774c6b90 --- /dev/null +++ b/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileItemAction.tsx @@ -0,0 +1,31 @@ +import { SNFile } from '@standardnotes/snjs' + +export enum PopoverFileItemActionType { + AttachFileToNote, + DetachFileToNote, + DeleteFile, + DownloadFile, + RenameFile, + ToggleFileProtection, +} + +export type PopoverFileItemAction = + | { + type: Exclude< + PopoverFileItemActionType, + PopoverFileItemActionType.RenameFile | PopoverFileItemActionType.ToggleFileProtection + > + payload: SNFile + } + | { + type: PopoverFileItemActionType.ToggleFileProtection + payload: SNFile + callback: (isProtected: boolean) => void + } + | { + type: PopoverFileItemActionType.RenameFile + payload: { + file: SNFile + name: string + } + } diff --git a/app/assets/javascripts/components/AttachedFilesPopover/PopoverFileSubmenu.tsx b/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileSubmenu.tsx similarity index 74% rename from app/assets/javascripts/components/AttachedFilesPopover/PopoverFileSubmenu.tsx rename to app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileSubmenu.tsx index d9183779d..fe5a94e5f 100644 --- a/app/assets/javascripts/components/AttachedFilesPopover/PopoverFileSubmenu.tsx +++ b/app/assets/javascripts/Components/AttachedFilesPopover/PopoverFileSubmenu.tsx @@ -1,31 +1,18 @@ -import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants'; -import { - calculateSubmenuStyle, - SubmenuStyle, -} from '@/utils/calculateSubmenuStyle'; -import { - Disclosure, - DisclosureButton, - DisclosurePanel, -} from '@reach/disclosure'; -import { FunctionComponent } from 'preact'; -import { - StateUpdater, - useCallback, - useEffect, - useRef, - useState, -} from 'preact/hooks'; -import { Icon } from '../Icon'; -import { Switch } from '../Switch'; -import { useCloseOnBlur } from '../utils'; -import { useFilePreviewModal } from '../Files/FilePreviewModalProvider'; -import { PopoverFileItemProps } from './PopoverFileItem'; -import { PopoverFileItemActionType } from './PopoverFileItemAction'; +import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants' +import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle' +import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' +import { FunctionComponent } from 'preact' +import { StateUpdater, useCallback, useEffect, useRef, useState } from 'preact/hooks' +import { Icon } from '@/Components/Icon' +import { Switch } from '@/Components/Switch' +import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' +import { useFilePreviewModal } from '@/Components/Files/FilePreviewModalProvider' +import { PopoverFileItemProps } from './PopoverFileItem' +import { PopoverFileItemActionType } from './PopoverFileItemAction' type Props = Omit & { - setIsRenamingFile: StateUpdater; -}; + setIsRenamingFile: StateUpdater +} export const PopoverFileSubmenu: FunctionComponent = ({ file, @@ -33,54 +20,51 @@ export const PopoverFileSubmenu: FunctionComponent = ({ handleFileAction, setIsRenamingFile, }) => { - const filePreviewModal = useFilePreviewModal(); + const filePreviewModal = useFilePreviewModal() - const menuContainerRef = useRef(null); - const menuButtonRef = useRef(null); - const menuRef = useRef(null); + const menuContainerRef = useRef(null) + const menuButtonRef = useRef(null) + const menuRef = useRef(null) - const [isMenuOpen, setIsMenuOpen] = useState(false); - const [isFileProtected, setIsFileProtected] = useState(file.protected); + const [isMenuOpen, setIsMenuOpen] = useState(false) + const [isFileProtected, setIsFileProtected] = useState(file.protected) const [menuStyle, setMenuStyle] = useState({ right: 0, bottom: 0, maxHeight: 'auto', - }); - const [closeOnBlur] = useCloseOnBlur(menuContainerRef, setIsMenuOpen); + }) + const [closeOnBlur] = useCloseOnBlur(menuContainerRef, setIsMenuOpen) const closeMenu = () => { - setIsMenuOpen(false); - }; + setIsMenuOpen(false) + } const toggleMenu = () => { if (!isMenuOpen) { - const menuPosition = calculateSubmenuStyle(menuButtonRef.current); + const menuPosition = calculateSubmenuStyle(menuButtonRef.current) if (menuPosition) { - setMenuStyle(menuPosition); + setMenuStyle(menuPosition) } } - setIsMenuOpen(!isMenuOpen); - }; + setIsMenuOpen(!isMenuOpen) + } const recalculateMenuStyle = useCallback(() => { - const newMenuPosition = calculateSubmenuStyle( - menuButtonRef.current, - menuRef.current - ); + const newMenuPosition = calculateSubmenuStyle(menuButtonRef.current, menuRef.current) if (newMenuPosition) { - setMenuStyle(newMenuPosition); + setMenuStyle(newMenuPosition) } - }, []); + }, []) useEffect(() => { if (isMenuOpen) { setTimeout(() => { - recalculateMenuStyle(); - }); + recalculateMenuStyle() + }) } - }, [isMenuOpen, recalculateMenuStyle]); + }, [isMenuOpen, recalculateMenuStyle]) return (
@@ -106,8 +90,8 @@ export const PopoverFileSubmenu: FunctionComponent = ({ onBlur={closeOnBlur} className="sn-dropdown-item focus:bg-info-backdrop" onClick={() => { - filePreviewModal.activate(file); - closeMenu(); + filePreviewModal.activate(file) + closeMenu() }} > @@ -121,8 +105,8 @@ export const PopoverFileSubmenu: FunctionComponent = ({ handleFileAction({ type: PopoverFileItemActionType.DetachFileToNote, payload: file, - }); - closeMenu(); + }).catch(console.error) + closeMenu() }} > @@ -136,8 +120,8 @@ export const PopoverFileSubmenu: FunctionComponent = ({ handleFileAction({ type: PopoverFileItemActionType.AttachFileToNote, payload: file, - }); - closeMenu(); + }).catch(console.error) + closeMenu() }} > @@ -152,9 +136,9 @@ export const PopoverFileSubmenu: FunctionComponent = ({ type: PopoverFileItemActionType.ToggleFileProtection, payload: file, callback: (isProtected: boolean) => { - setIsFileProtected(isProtected); + setIsFileProtected(isProtected) }, - }); + }).catch(console.error) }} onBlur={closeOnBlur} > @@ -176,8 +160,8 @@ export const PopoverFileSubmenu: FunctionComponent = ({ handleFileAction({ type: PopoverFileItemActionType.DownloadFile, payload: file, - }); - closeMenu(); + }).catch(console.error) + closeMenu() }} > @@ -187,7 +171,7 @@ export const PopoverFileSubmenu: FunctionComponent = ({ onBlur={closeOnBlur} className="sn-dropdown-item focus:bg-info-backdrop" onClick={() => { - setIsRenamingFile(true); + setIsRenamingFile(true) }} > @@ -200,8 +184,8 @@ export const PopoverFileSubmenu: FunctionComponent = ({ handleFileAction({ type: PopoverFileItemActionType.DeleteFile, payload: file, - }); - closeMenu(); + }).catch(console.error) + closeMenu() }} > @@ -212,5 +196,5 @@ export const PopoverFileSubmenu: FunctionComponent = ({
- ); -}; + ) +} diff --git a/app/assets/javascripts/components/Bubble.tsx b/app/assets/javascripts/Components/Bubble/index.tsx similarity index 70% rename from app/assets/javascripts/components/Bubble.tsx rename to app/assets/javascripts/Components/Bubble/index.tsx index e164a4f50..5a2146823 100644 --- a/app/assets/javascripts/components/Bubble.tsx +++ b/app/assets/javascripts/Components/Bubble/index.tsx @@ -1,25 +1,23 @@ interface BubbleProperties { - label: string; - selected: boolean; - onSelect: () => void; + label: string + selected: boolean + onSelect: () => void } const styles = { base: 'px-2 py-1.5 text-center rounded-full cursor-pointer transition border-1 border-solid active:border-info active:bg-info active:color-neutral-contrast', unselected: 'color-neutral border-secondary', selected: 'border-info bg-info color-neutral-contrast', -}; +} const Bubble = ({ label, selected, onSelect }: BubbleProperties) => ( {label} -); +) -export default Bubble; +export default Bubble diff --git a/app/assets/javascripts/components/Button.tsx b/app/assets/javascripts/Components/Button/Button.tsx similarity index 54% rename from app/assets/javascripts/components/Button.tsx rename to app/assets/javascripts/Components/Button/Button.tsx index 1afc15904..eca0e336e 100644 --- a/app/assets/javascripts/components/Button.tsx +++ b/app/assets/javascripts/Components/Button/Button.tsx @@ -1,58 +1,44 @@ -import { JSXInternal } from 'preact/src/jsx'; -import { ComponentChildren, FunctionComponent, Ref } from 'preact'; -import { forwardRef } from 'preact/compat'; +import { JSXInternal } from 'preact/src/jsx' +import { ComponentChildren, FunctionComponent, Ref } from 'preact' +import { forwardRef } from 'preact/compat' -const baseClass = `rounded px-4 py-1.75 font-bold text-sm fit-content`; +const baseClass = 'rounded px-4 py-1.75 font-bold text-sm fit-content' -type ButtonVariant = 'normal' | 'primary'; +type ButtonVariant = 'normal' | 'primary' -const getClassName = ( - variant: ButtonVariant, - danger: boolean, - disabled: boolean -) => { - const borders = - variant === 'normal' ? 'border-solid border-main border-1' : 'no-border'; - const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer'; +const getClassName = (variant: ButtonVariant, danger: boolean, disabled: boolean) => { + const borders = variant === 'normal' ? 'border-solid border-main border-1' : 'no-border' + const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer' - let colors = - variant === 'normal' - ? 'bg-default color-text' - : 'bg-info color-info-contrast'; + let colors = variant === 'normal' ? 'bg-default color-text' : 'bg-info color-info-contrast' let focusHoverStates = variant === 'normal' ? 'focus:bg-contrast focus:outline-none hover:bg-contrast' - : 'hover:brightness-130 focus:outline-none focus:brightness-130'; + : 'hover:brightness-130 focus:outline-none focus:brightness-130' if (danger) { - colors = - variant === 'normal' - ? 'bg-default color-danger' - : 'bg-danger color-info-contrast'; + colors = variant === 'normal' ? 'bg-default color-danger' : 'bg-danger color-info-contrast' } if (disabled) { - colors = - variant === 'normal' - ? 'bg-default color-grey-2' - : 'bg-grey-2 color-info-contrast'; + colors = variant === 'normal' ? 'bg-default color-grey-2' : 'bg-grey-2 color-info-contrast' focusHoverStates = variant === 'normal' ? 'focus:bg-default focus:outline-none hover:bg-default' - : 'focus:brightness-default focus:outline-none hover:brightness-default'; + : 'focus:brightness-default focus:outline-none hover:brightness-default' } - return `${baseClass} ${colors} ${borders} ${focusHoverStates} ${cursor}`; -}; + return `${baseClass} ${colors} ${borders} ${focusHoverStates} ${cursor}` +} type ButtonProps = JSXInternal.HTMLAttributes & { - children?: ComponentChildren; - className?: string; - variant?: ButtonVariant; - dangerStyle?: boolean; - label?: string; -}; + children?: ComponentChildren + className?: string + variant?: ButtonVariant + dangerStyle?: boolean + label?: string +} export const Button: FunctionComponent = forwardRef( ( @@ -65,7 +51,7 @@ export const Button: FunctionComponent = forwardRef( children, ...props }: ButtonProps, - ref: Ref + ref: Ref, ) => { return ( - ); - } -); + ) + }, +) diff --git a/app/assets/javascripts/components/IconButton.tsx b/app/assets/javascripts/Components/Button/IconButton.tsx similarity index 66% rename from app/assets/javascripts/components/IconButton.tsx rename to app/assets/javascripts/Components/Button/IconButton.tsx index 6349be7bc..f74bec08c 100644 --- a/app/assets/javascripts/components/IconButton.tsx +++ b/app/assets/javascripts/Components/Button/IconButton.tsx @@ -1,27 +1,27 @@ -import { FunctionComponent } from 'preact'; -import { Icon } from './Icon'; -import { IconType } from '@standardnotes/snjs'; +import { FunctionComponent } from 'preact' +import { Icon } from '@/Components/Icon' +import { IconType } from '@standardnotes/snjs' interface Props { /** * onClick - preventDefault is handled within the component */ - onClick: () => void; + onClick: () => void - className?: string; + className?: string - icon: IconType; + icon: IconType - iconClassName?: string; + iconClassName?: string /** * Button tooltip */ - title: string; + title: string - focusable: boolean; + focusable: boolean - disabled?: boolean; + disabled?: boolean } /** @@ -38,10 +38,10 @@ export const IconButton: FunctionComponent = ({ disabled = false, }) => { const click = (e: MouseEvent) => { - e.preventDefault(); - onClick(); - }; - const focusableClass = focusable ? '' : 'focus:shadow-none'; + e.preventDefault() + onClick() + } + const focusableClass = focusable ? '' : 'focus:shadow-none' return ( - ); -}; + ) +} diff --git a/app/assets/javascripts/Components/Button/RoundIconButton.tsx b/app/assets/javascripts/Components/Button/RoundIconButton.tsx new file mode 100644 index 000000000..ee3d20325 --- /dev/null +++ b/app/assets/javascripts/Components/Button/RoundIconButton.tsx @@ -0,0 +1,40 @@ +import { FunctionComponent } from 'preact' +import { Icon } from '@/Components/Icon' +import { IconType } from '@standardnotes/snjs' + +type ButtonType = 'normal' | 'primary' + +interface Props { + /** + * onClick - preventDefault is handled within the component + */ + onClick: () => void + + type: ButtonType + + className?: string + + icon: IconType +} + +/** + * IconButton component with an icon + * preventDefault is already handled within the component + */ +export const RoundIconButton: FunctionComponent = ({ + onClick, + type, + className, + icon: iconType, +}) => { + const click = (e: MouseEvent) => { + e.preventDefault() + onClick() + } + const classes = type === 'primary' ? 'info ' : '' + return ( + + ) +} diff --git a/app/assets/javascripts/components/ChallengeModal/ChallengeModal.tsx b/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx similarity index 54% rename from app/assets/javascripts/components/ChallengeModal/ChallengeModal.tsx rename to app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx index 4f006c1db..c3990d784 100644 --- a/app/assets/javascripts/components/ChallengeModal/ChallengeModal.tsx +++ b/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx @@ -1,5 +1,5 @@ -import { WebApplication } from '@/ui_models/application'; -import { DialogContent, DialogOverlay } from '@reach/dialog'; +import { WebApplication } from '@/UIModels/Application' +import { DialogContent, DialogOverlay } from '@reach/dialog' import { ButtonType, Challenge, @@ -7,90 +7,87 @@ import { ChallengeReason, ChallengeValue, removeFromArray, -} from '@standardnotes/snjs'; -import { ProtectedIllustration } from '@standardnotes/stylekit'; -import { FunctionComponent } from 'preact'; -import { useCallback, useEffect, useState } from 'preact/hooks'; -import { Button } from '../Button'; -import { Icon } from '../Icon'; -import { ChallengeModalPrompt } from './ChallengePrompt'; +} from '@standardnotes/snjs' +import { ProtectedIllustration } from '@standardnotes/stylekit' +import { FunctionComponent } from 'preact' +import { useCallback, useEffect, useState } from 'preact/hooks' +import { Button } from '@/Components/Button/Button' +import { Icon } from '@/Components/Icon' +import { ChallengeModalPrompt } from './ChallengePrompt' type InputValue = { - prompt: ChallengePrompt; - value: string | number | boolean; - invalid: boolean; -}; + prompt: ChallengePrompt + value: string | number | boolean + invalid: boolean +} -export type ChallengeModalValues = Record; +export type ChallengeModalValues = Record type Props = { - application: WebApplication; - challenge: Challenge; - onDismiss: (challenge: Challenge) => Promise; -}; + application: WebApplication + challenge: Challenge + onDismiss: (challenge: Challenge) => Promise +} const validateValues = ( values: ChallengeModalValues, - prompts: ChallengePrompt[] + prompts: ChallengePrompt[], ): ChallengeModalValues | undefined => { - let hasInvalidValues = false; - const validatedValues = { ...values }; + let hasInvalidValues = false + const validatedValues = { ...values } for (const prompt of prompts) { - const value = validatedValues[prompt.id]; + const value = validatedValues[prompt.id] if (typeof value.value === 'string' && value.value.length === 0) { - validatedValues[prompt.id].invalid = true; - hasInvalidValues = true; + validatedValues[prompt.id].invalid = true + hasInvalidValues = true } } if (!hasInvalidValues) { - return validatedValues; + return validatedValues } -}; + return undefined +} -export const ChallengeModal: FunctionComponent = ({ - application, - challenge, - onDismiss, -}) => { +export const ChallengeModal: FunctionComponent = ({ application, challenge, onDismiss }) => { const [values, setValues] = useState(() => { - const values = {} as ChallengeModalValues; + const values = {} as ChallengeModalValues for (const prompt of challenge.prompts) { values[prompt.id] = { prompt, value: prompt.initialValue ?? '', invalid: false, - }; + } } - return values; - }); - const [isSubmitting, setIsSubmitting] = useState(false); - const [isProcessing, setIsProcessing] = useState(false); - const [, setProcessingPrompts] = useState([]); - const [bypassModalFocusLock, setBypassModalFocusLock] = useState(false); + return values + }) + const [isSubmitting, setIsSubmitting] = useState(false) + const [isProcessing, setIsProcessing] = useState(false) + const [, setProcessingPrompts] = useState([]) + const [bypassModalFocusLock, setBypassModalFocusLock] = useState(false) const shouldShowForgotPasscode = [ ChallengeReason.ApplicationUnlock, ChallengeReason.Migration, - ].includes(challenge.reason); + ].includes(challenge.reason) const submit = async () => { - const validatedValues = validateValues(values, challenge.prompts); + const validatedValues = validateValues(values, challenge.prompts) if (!validatedValues) { - return; + return } if (isSubmitting || isProcessing) { - return; + return } - setIsSubmitting(true); - setIsProcessing(true); - const valuesToProcess: ChallengeValue[] = []; + setIsSubmitting(true) + setIsProcessing(true) + const valuesToProcess: ChallengeValue[] = [] for (const inputValue of Object.values(validatedValues)) { - const rawValue = inputValue.value; - const value = new ChallengeValue(inputValue.prompt, rawValue); - valuesToProcess.push(value); + const rawValue = inputValue.value + const value = new ChallengeValue(inputValue.prompt, rawValue) + valuesToProcess.push(value) } - const processingPrompts = valuesToProcess.map((v) => v.prompt); - setIsProcessing(processingPrompts.length > 0); - setProcessingPrompts(processingPrompts); + const processingPrompts = valuesToProcess.map((v) => v.prompt) + setIsProcessing(processingPrompts.length > 0) + setProcessingPrompts(processingPrompts) /** * Unfortunately neccessary to wait 50ms so that the above setState call completely * updates the UI to change processing state, before we enter into UI blocking operation @@ -98,90 +95,85 @@ export const ChallengeModal: FunctionComponent = ({ */ setTimeout(() => { if (valuesToProcess.length > 0) { - application.submitValuesForChallenge(challenge, valuesToProcess); + application.submitValuesForChallenge(challenge, valuesToProcess).catch(console.error) } else { - setIsProcessing(false); + setIsProcessing(false) } - setIsSubmitting(false); - }, 50); - }; + setIsSubmitting(false) + }, 50) + } const onValueChange = useCallback( (value: string | number, prompt: ChallengePrompt) => { - const newValues = { ...values }; - newValues[prompt.id].invalid = false; - newValues[prompt.id].value = value; - setValues(newValues); + const newValues = { ...values } + newValues[prompt.id].invalid = false + newValues[prompt.id].value = value + setValues(newValues) }, - [values] - ); + [values], + ) const closeModal = () => { if (challenge.cancelable) { - onDismiss(challenge); + onDismiss(challenge).catch(console.error) } - }; + } useEffect(() => { - const removeChallengeObserver = application.addChallengeObserver( - challenge, - { - onValidValue: (value) => { - setValues((values) => { - const newValues = { ...values }; - newValues[value.prompt.id].invalid = false; - return newValues; - }); + const removeChallengeObserver = application.addChallengeObserver(challenge, { + onValidValue: (value) => { + setValues((values) => { + const newValues = { ...values } + newValues[value.prompt.id].invalid = false + return newValues + }) + setProcessingPrompts((currentlyProcessingPrompts) => { + const processingPrompts = currentlyProcessingPrompts.slice() + removeFromArray(processingPrompts, value.prompt) + setIsProcessing(processingPrompts.length > 0) + return processingPrompts + }) + }, + onInvalidValue: (value) => { + setValues((values) => { + const newValues = { ...values } + newValues[value.prompt.id].invalid = true + return newValues + }) + /** If custom validation, treat all values together and not individually */ + if (!value.prompt.validates) { + setProcessingPrompts([]) + setIsProcessing(false) + } else { setProcessingPrompts((currentlyProcessingPrompts) => { - const processingPrompts = currentlyProcessingPrompts.slice(); - removeFromArray(processingPrompts, value.prompt); - setIsProcessing(processingPrompts.length > 0); - return processingPrompts; - }); - }, - onInvalidValue: (value) => { - setValues((values) => { - const newValues = { ...values }; - newValues[value.prompt.id].invalid = true; - return newValues; - }); - /** If custom validation, treat all values together and not individually */ - if (!value.prompt.validates) { - setProcessingPrompts([]); - setIsProcessing(false); - } else { - setProcessingPrompts((currentlyProcessingPrompts) => { - const processingPrompts = currentlyProcessingPrompts.slice(); - removeFromArray(processingPrompts, value.prompt); - setIsProcessing(processingPrompts.length > 0); - return processingPrompts; - }); - } - }, - onComplete: () => { - onDismiss(challenge); - }, - onCancel: () => { - onDismiss(challenge); - }, - } - ); + const processingPrompts = currentlyProcessingPrompts.slice() + removeFromArray(processingPrompts, value.prompt) + setIsProcessing(processingPrompts.length > 0) + return processingPrompts + }) + } + }, + onComplete: () => { + onDismiss(challenge).catch(console.error) + }, + onCancel: () => { + onDismiss(challenge).catch(console.error) + }, + }) return () => { - removeChallengeObserver(); - }; - }, [application, challenge, onDismiss]); + removeChallengeObserver() + } + }, [application, challenge, onDismiss]) if (!challenge.prompts) { - return null; + return null } return ( = ({ )} -
- {challenge.heading} -
-
- {challenge.subheading} -
+
{challenge.heading}
+
{challenge.subheading}
{ - e.preventDefault(); - submit(); + e.preventDefault() + submit().catch(console.error) }} > {challenge.prompts.map((prompt, index) => ( @@ -232,7 +220,7 @@ export const ChallengeModal: FunctionComponent = ({ disabled={isProcessing} className="min-w-76 mb-3.5" onClick={() => { - submit(); + submit().catch(console.error) }} > {isProcessing ? 'Generating Keys...' : 'Unlock'} @@ -241,23 +229,23 @@ export const ChallengeModal: FunctionComponent = ({ + - ); -}; + ) +} diff --git a/app/assets/javascripts/components/ComponentView/IsExpired.tsx b/app/assets/javascripts/Components/ComponentView/IsExpired.tsx similarity index 53% rename from app/assets/javascripts/components/ComponentView/IsExpired.tsx rename to app/assets/javascripts/Components/ComponentView/IsExpired.tsx index ce95e0750..d2df6cee1 100644 --- a/app/assets/javascripts/components/ComponentView/IsExpired.tsx +++ b/app/assets/javascripts/Components/ComponentView/IsExpired.tsx @@ -1,29 +1,25 @@ -import { FeatureStatus } from '@standardnotes/snjs'; -import { FunctionalComponent } from 'preact'; +import { FeatureStatus } from '@standardnotes/snjs' +import { FunctionalComponent } from 'preact' interface IProps { - expiredDate: string; - componentName: string; - featureStatus: FeatureStatus; - manageSubscription: () => void; + expiredDate: string + componentName: string + featureStatus: FeatureStatus + manageSubscription: () => void } -const statusString = ( - featureStatus: FeatureStatus, - expiredDate: string, - componentName: string -) => { +const statusString = (featureStatus: FeatureStatus, expiredDate: string, componentName: string) => { switch (featureStatus) { case FeatureStatus.InCurrentPlanButExpired: - return `Your subscription expired on ${expiredDate}`; + return `Your subscription expired on ${expiredDate}` case FeatureStatus.NoUserSubscription: - return `You do not have an active subscription`; + return 'You do not have an active subscription' case FeatureStatus.NotInCurrentPlan: - return `Please upgrade your plan to access ${componentName}`; + return `Please upgrade your plan to access ${componentName}` default: - return `${componentName} is valid and you should not be seeing this message`; + return `${componentName} is valid and you should not be seeing this message` } -}; +} export const IsExpired: FunctionalComponent = ({ expiredDate, @@ -41,27 +37,18 @@ export const IsExpired: FunctionalComponent = ({
- - {statusString(featureStatus, expiredDate, componentName)} - -
- {componentName} is in a read-only state. -
+ {statusString(featureStatus, expiredDate, componentName)} +
{componentName} is in a read-only state.
-
manageSubscription()} - > - +
manageSubscription()}> +
- ); -}; + ) +} diff --git a/app/assets/javascripts/components/ComponentView/IssueOnLoading.tsx b/app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx similarity index 59% rename from app/assets/javascripts/components/ComponentView/IssueOnLoading.tsx rename to app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx index b2c70b408..69f3c48ca 100644 --- a/app/assets/javascripts/components/ComponentView/IssueOnLoading.tsx +++ b/app/assets/javascripts/Components/ComponentView/IssueOnLoading.tsx @@ -1,22 +1,17 @@ -import { FunctionalComponent } from 'preact'; +import { FunctionalComponent } from 'preact' interface IProps { - componentName: string; - reloadIframe: () => void; + componentName: string + reloadIframe: () => void } -export const IssueOnLoading: FunctionalComponent = ({ - componentName, - reloadIframe, -}) => { +export const IssueOnLoading: FunctionalComponent = ({ componentName, reloadIframe }) => { return (
-
- There was an issue loading {componentName}. -
+
There was an issue loading {componentName}.
@@ -26,5 +21,5 @@ export const IssueOnLoading: FunctionalComponent = ({
- ); -}; + ) +} diff --git a/app/assets/javascripts/components/ComponentView/OfflineRestricted.tsx b/app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx similarity index 69% rename from app/assets/javascripts/components/ComponentView/OfflineRestricted.tsx rename to app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx index a60ee0de3..290ef05d0 100644 --- a/app/assets/javascripts/components/ComponentView/OfflineRestricted.tsx +++ b/app/assets/javascripts/Components/ComponentView/OfflineRestricted.tsx @@ -1,4 +1,4 @@ -import { FunctionalComponent } from 'preact'; +import { FunctionalComponent } from 'preact' export const OfflineRestricted: FunctionalComponent = () => { return ( @@ -11,21 +11,17 @@ export const OfflineRestricted: FunctionalComponent = () => { You have restricted this component to not use a hosted version.
- Locally-installed components are not available in the web - application. + Locally-installed components are not available in the web application.
-
- To continue, choose from the following options: -
+
To continue, choose from the following options:
  • - Enable the Hosted option for this component by opening the - Preferences {'>'} General {'>'} Advanced Settings menu and{' '} - toggling 'Use hosted when local is unavailable' under this - component's options. Then press Reload. + Enable the Hosted option for this component by opening the Preferences {'>'}{' '} + General {'>'} Advanced Settings menu and toggling 'Use hosted when local is + unavailable' under this component's options. Then press Reload.
  • Use the desktop application.
@@ -35,5 +31,5 @@ export const OfflineRestricted: FunctionalComponent = () => {
- ); -}; + ) +} diff --git a/app/assets/javascripts/components/ComponentView/UrlMissing.tsx b/app/assets/javascripts/Components/ComponentView/UrlMissing.tsx similarity index 60% rename from app/assets/javascripts/components/ComponentView/UrlMissing.tsx rename to app/assets/javascripts/Components/ComponentView/UrlMissing.tsx index fa6a56af2..c077671f6 100644 --- a/app/assets/javascripts/components/ComponentView/UrlMissing.tsx +++ b/app/assets/javascripts/Components/ComponentView/UrlMissing.tsx @@ -1,7 +1,7 @@ -import { FunctionalComponent } from 'preact'; +import { FunctionalComponent } from 'preact' interface IProps { - componentName: string; + componentName: string } export const UrlMissing: FunctionalComponent = ({ componentName }) => { @@ -14,16 +14,14 @@ export const UrlMissing: FunctionalComponent = ({ componentName }) => { This extension is missing its URL property.

- In order to access your note immediately, - please switch from {componentName} to the Plain Editor. -

-
-

- Please contact help@standardnotes.com to remedy this issue. + In order to access your note immediately, please switch from {componentName} to the + Plain Editor.

+
+

Please contact help@standardnotes.com to remedy this issue.

- ); -}; + ) +} diff --git a/app/assets/javascripts/Components/ComponentView/index.tsx b/app/assets/javascripts/Components/ComponentView/index.tsx new file mode 100644 index 000000000..be26e251d --- /dev/null +++ b/app/assets/javascripts/Components/ComponentView/index.tsx @@ -0,0 +1,221 @@ +import { + ComponentAction, + FeatureStatus, + SNComponent, + dateToLocalizedString, + ComponentViewer, + ComponentViewerEvent, + ComponentViewerError, +} from '@standardnotes/snjs' +import { WebApplication } from '@/UIModels/Application' +import { FunctionalComponent } from 'preact' +import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' +import { observer } from 'mobx-react-lite' +import { OfflineRestricted } from '@/Components/ComponentView/OfflineRestricted' +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 { openSubscriptionDashboard } from '@/Utils/ManageSubscription' + +interface IProps { + application: WebApplication + appState: AppState + componentViewer: ComponentViewer + requestReload?: (viewer: ComponentViewer, force?: boolean) => void + onLoad?: (component: SNComponent) => void + manualDealloc?: boolean +} + +/** + * The maximum amount of time we'll wait for a component + * to load before displaying error + */ +const MaxLoadThreshold = 4000 +const VisibilityChangeKey = 'visibilitychange' +const MSToWaitAfterIframeLoadToAvoidFlicker = 35 + +export const ComponentView: FunctionalComponent = observer( + ({ application, onLoad, componentViewer, requestReload }) => { + const iframeRef = useRef(null) + const excessiveLoadingTimeout = useRef | undefined>(undefined) + + const [hasIssueLoading, setHasIssueLoading] = useState(false) + const [isLoading, setIsLoading] = useState(true) + const [featureStatus, setFeatureStatus] = useState( + componentViewer.getFeatureStatus(), + ) + const [isComponentValid, setIsComponentValid] = useState(true) + const [error, setError] = useState(undefined) + const [deprecationMessage, setDeprecationMessage] = useState(undefined) + const [isDeprecationMessageDismissed, setIsDeprecationMessageDismissed] = useState(false) + const [didAttemptReload, setDidAttemptReload] = useState(false) + + const component = componentViewer.component + + const manageSubscription = useCallback(() => { + openSubscriptionDashboard(application) + }, [application]) + + const reloadValidityStatus = useCallback(() => { + setFeatureStatus(componentViewer.getFeatureStatus()) + if (!componentViewer.lockReadonly) { + componentViewer.setReadonly(featureStatus !== FeatureStatus.Entitled) + } + setIsComponentValid(componentViewer.shouldRender()) + + if (isLoading && !isComponentValid) { + setIsLoading(false) + } + + setError(componentViewer.getError()) + setDeprecationMessage(component.deprecationMessage) + }, [componentViewer, component.deprecationMessage, featureStatus, isComponentValid, isLoading]) + + useEffect(() => { + reloadValidityStatus() + }, [reloadValidityStatus]) + + const dismissDeprecationMessage = () => { + setIsDeprecationMessageDismissed(true) + } + + const onVisibilityChange = useCallback(() => { + if (document.visibilityState === 'hidden') { + return + } + if (hasIssueLoading) { + requestReload?.(componentViewer) + } + }, [hasIssueLoading, componentViewer, requestReload]) + + const handleIframeTakingTooLongToLoad = useCallback(async () => { + setIsLoading(false) + setHasIssueLoading(true) + + if (!didAttemptReload) { + setDidAttemptReload(true) + requestReload?.(componentViewer) + } else { + document.addEventListener(VisibilityChangeKey, onVisibilityChange) + } + }, [didAttemptReload, onVisibilityChange, componentViewer, requestReload]) + + useMemo(() => { + const loadTimeout = setTimeout(() => { + handleIframeTakingTooLongToLoad().catch(console.error) + }, MaxLoadThreshold) + + excessiveLoadingTimeout.current = loadTimeout + + return () => { + excessiveLoadingTimeout.current && clearTimeout(excessiveLoadingTimeout.current) + } + }, [handleIframeTakingTooLongToLoad]) + + const onIframeLoad = useCallback(() => { + const iframe = iframeRef.current as HTMLIFrameElement + const contentWindow = iframe.contentWindow as Window + excessiveLoadingTimeout.current && clearTimeout(excessiveLoadingTimeout.current) + + componentViewer.setWindow(contentWindow).catch(console.error) + + setTimeout(() => { + setIsLoading(false) + setHasIssueLoading(false) + onLoad?.(component) + }, MSToWaitAfterIframeLoadToAvoidFlicker) + }, [componentViewer, onLoad, component, excessiveLoadingTimeout]) + + useEffect(() => { + const removeFeaturesChangedObserver = componentViewer.addEventObserver((event) => { + if (event === ComponentViewerEvent.FeatureStatusUpdated) { + setFeatureStatus(componentViewer.getFeatureStatus()) + } + }) + + return () => { + removeFeaturesChangedObserver() + } + }, [componentViewer]) + + useEffect(() => { + const removeActionObserver = componentViewer.addActionObserver((action, data) => { + switch (action) { + case ComponentAction.KeyDown: + application.io.handleComponentKeyDown(data.keyboardModifier) + break + case ComponentAction.KeyUp: + application.io.handleComponentKeyUp(data.keyboardModifier) + break + case ComponentAction.Click: + application.getAppState().notes.setContextMenuOpen(false) + break + default: + return + } + }) + return () => { + removeActionObserver() + } + }, [componentViewer, application]) + + useEffect(() => { + const unregisterDesktopObserver = application + .getDesktopService() + .registerUpdateObserver((updatedComponent: SNComponent) => { + if (updatedComponent.uuid === component.uuid && updatedComponent.active) { + requestReload?.(componentViewer) + } + }) + + 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 &&
} + + ) + }, +) diff --git a/app/assets/javascripts/Components/ConfirmSignoutModal/index.tsx b/app/assets/javascripts/Components/ConfirmSignoutModal/index.tsx new file mode 100644 index 000000000..132b42574 --- /dev/null +++ b/app/assets/javascripts/Components/ConfirmSignoutModal/index.tsx @@ -0,0 +1,98 @@ +import { useEffect, useRef, useState } from 'preact/hooks' +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 { observer } from 'mobx-react-lite' + +type Props = { + application: WebApplication + appState: AppState +} + +export const ConfirmSignoutContainer = observer((props: Props) => { + if (!props.appState.accountMenu.signingOut) { + return null + } + return +}) + +export const ConfirmSignoutModal = observer(({ application, appState }: Props) => { + const [deleteLocalBackups, setDeleteLocalBackups] = useState(false) + + const cancelRef = useRef(null) + function closeDialog() { + appState.accountMenu.setSigningOut(false) + } + + const [localBackupsCount, setLocalBackupsCount] = useState(0) + + useEffect(() => { + application.bridge.localBackupsCount().then(setLocalBackupsCount).catch(console.error) + }, [appState.accountMenu.signingOut, application.bridge]) + + return ( + +
+
+
+
+
+ + Sign out workspace? + + +

{STRING_SIGN_OUT_CONFIRMATION}

+
+ {localBackupsCount > 0 && ( +
+
+ + +
+ )} +
+ + +
+
+
+
+
+
+
+ ) +}) diff --git a/app/assets/javascripts/components/Dropdown.tsx b/app/assets/javascripts/Components/Dropdown/index.tsx similarity index 74% rename from app/assets/javascripts/components/Dropdown.tsx rename to app/assets/javascripts/Components/Dropdown/index.tsx index 837697520..9bc9b6b22 100644 --- a/app/assets/javascripts/components/Dropdown.tsx +++ b/app/assets/javascripts/Components/Dropdown/index.tsx @@ -5,32 +5,32 @@ import { ListboxList, ListboxOption, ListboxPopover, -} from '@reach/listbox'; -import VisuallyHidden from '@reach/visually-hidden'; -import { FunctionComponent } from 'preact'; -import { Icon } from './Icon'; -import { IconType } from '@standardnotes/snjs'; +} from '@reach/listbox' +import VisuallyHidden from '@reach/visually-hidden' +import { FunctionComponent } from 'preact' +import { Icon } from '@/Components/Icon' +import { IconType } from '@standardnotes/snjs' export type DropdownItem = { - icon?: IconType; - iconClassName?: string; - label: string; - value: string; - disabled?: boolean; -}; + icon?: IconType + iconClassName?: string + label: string + value: string + disabled?: boolean +} type DropdownProps = { - id: string; - label: string; - items: DropdownItem[]; - value: string; - onChange: (value: string, item: DropdownItem) => void; - disabled?: boolean; -}; + id: string + label: string + items: DropdownItem[] + value: string + onChange: (value: string, item: DropdownItem) => void + disabled?: boolean +} type ListboxButtonProps = DropdownItem & { - isExpanded: boolean; -}; + isExpanded: boolean +} const CustomDropdownButton: FunctionComponent = ({ label, @@ -47,15 +47,11 @@ const CustomDropdownButton: FunctionComponent = ({ ) : null}
{label}
- + -); +) export const Dropdown: FunctionComponent = ({ id, @@ -65,15 +61,13 @@ export const Dropdown: FunctionComponent = ({ onChange, disabled, }) => { - const labelId = `${id}-label`; + const labelId = `${id}-label` const handleChange = (value: string) => { - const selectedItem = items.find( - (item) => item.value === value - ) as DropdownItem; + const selectedItem = items.find((item) => item.value === value) as DropdownItem - onChange(value, selectedItem); - }; + onChange(value, selectedItem) + } return ( <> @@ -87,16 +81,16 @@ export const Dropdown: FunctionComponent = ({ { - const current = items.find((item) => item.value === value); - const icon = current ? current?.icon : null; - const iconClassName = current ? current?.iconClassName : null; + const current = items.find((item) => item.value === value) + const icon = current ? current?.icon : null + const iconClassName = current ? current?.iconClassName : null return CustomDropdownButton({ value: value ? value : label.toLowerCase(), label, isExpanded, ...(icon ? { icon } : null), ...(iconClassName ? { iconClassName } : null), - }); + }) }} /> @@ -125,5 +119,5 @@ export const Dropdown: FunctionComponent = ({ - ); -}; + ) +} diff --git a/app/assets/javascripts/components/Files/FilePreviewInfoPanel.tsx b/app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx similarity index 70% rename from app/assets/javascripts/components/Files/FilePreviewInfoPanel.tsx rename to app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx index cf215c80d..5a12704e9 100644 --- a/app/assets/javascripts/components/Files/FilePreviewInfoPanel.tsx +++ b/app/assets/javascripts/Components/Files/FilePreviewInfoPanel.tsx @@ -1,11 +1,11 @@ -import { formatSizeToReadableString } from '@standardnotes/filepicker'; -import { SNFile } from '@standardnotes/snjs'; -import { FunctionComponent } from 'preact'; -import { Icon } from '../Icon'; +import { formatSizeToReadableString } from '@standardnotes/filepicker' +import { SNFile } from '@standardnotes/snjs' +import { FunctionComponent } from 'preact' +import { Icon } from '@/Components/Icon' type Props = { - file: SNFile; -}; + file: SNFile +} export const FilePreviewInfoPanel: FunctionComponent = ({ file }) => { return ( @@ -18,12 +18,10 @@ export const FilePreviewInfoPanel: FunctionComponent = ({ file }) => { Type: {file.mimeType}
- Size:{' '} - {formatSizeToReadableString(file.size)} + Size: {formatSizeToReadableString(file.size)}
- Created:{' '} - {file.created_at.toLocaleString()} + Created: {file.created_at.toLocaleString()}
Last Modified:{' '} @@ -33,5 +31,5 @@ export const FilePreviewInfoPanel: FunctionComponent = ({ file }) => { File ID: {file.uuid}
- ); -}; + ) +} diff --git a/app/assets/javascripts/components/Files/FilePreviewModal.tsx b/app/assets/javascripts/Components/Files/FilePreviewModal.tsx similarity index 68% rename from app/assets/javascripts/components/Files/FilePreviewModal.tsx rename to app/assets/javascripts/Components/Files/FilePreviewModal.tsx index 6ea7f46e0..f709fde05 100644 --- a/app/assets/javascripts/components/Files/FilePreviewModal.tsx +++ b/app/assets/javascripts/Components/Files/FilePreviewModal.tsx @@ -1,88 +1,81 @@ -import { WebApplication } from '@/ui_models/application'; -import { concatenateUint8Arrays } from '@/utils/concatenateUint8Arrays'; -import { DialogContent, DialogOverlay } from '@reach/dialog'; -import { SNFile } from '@standardnotes/snjs'; -import { NoPreviewIllustration } from '@standardnotes/stylekit'; -import { FunctionComponent } from 'preact'; -import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; -import { getFileIconComponent } from '../AttachedFilesPopover/PopoverFileItem'; -import { Button } from '../Button'; -import { Icon } from '../Icon'; -import { FilePreviewInfoPanel } from './FilePreviewInfoPanel'; -import { isFileTypePreviewable } from './isFilePreviewable'; +import { WebApplication } from '@/UIModels/Application' +import { concatenateUint8Arrays } from '@/Utils/ConcatenateUint8Arrays' +import { DialogContent, DialogOverlay } from '@reach/dialog' +import { SNFile } from '@standardnotes/snjs' +import { NoPreviewIllustration } from '@standardnotes/stylekit' +import { FunctionComponent } from 'preact' +import { useCallback, useEffect, useRef, useState } from 'preact/hooks' +import { getFileIconComponent } from '@/Components/AttachedFilesPopover/PopoverFileItem' +import { Button } from '@/Components/Button/Button' +import { Icon } from '@/Components/Icon' +import { FilePreviewInfoPanel } from './FilePreviewInfoPanel' +import { isFileTypePreviewable } from './isFilePreviewable' type Props = { - application: WebApplication; - file: SNFile; - onDismiss: () => void; -}; + application: WebApplication + file: SNFile + onDismiss: () => void +} const getPreviewComponentForFile = (file: SNFile, objectUrl: string) => { if (file.mimeType.startsWith('image/')) { - return ; + return } if (file.mimeType.startsWith('video/')) { - return