You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-29 23:48:19 +02:00
Compare commits
8 Commits
v2.9.6
...
table_edit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b519d55abf | ||
|
|
5a862443d8 | ||
|
|
30e191663d | ||
|
|
70cd2395fb | ||
|
|
2f1b6fbee1 | ||
|
|
8c0d4a0f71 | ||
|
|
bc08c6dcc3 | ||
|
|
a06365039d |
223
.eslintignore
223
.eslintignore
@@ -6,7 +6,6 @@ _releases/
|
||||
*.min.js
|
||||
**/commands/index.ts
|
||||
**/node_modules/
|
||||
packages/generator-joplin/generators/app/templates/api/
|
||||
Assets/
|
||||
docs/
|
||||
highlight.pack.js
|
||||
@@ -47,7 +46,7 @@ packages/app-desktop/packageInfo.js
|
||||
packages/app-desktop/services/electron-context-menu.js
|
||||
packages/app-desktop/vendor/lib/
|
||||
packages/app-mobile/android
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.bundle.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.bundle.js
|
||||
packages/app-mobile/ios
|
||||
packages/app-mobile/lib/rnInjectedJs/
|
||||
packages/app-mobile/locales
|
||||
@@ -70,7 +69,6 @@ packages/tools/node_modules
|
||||
packages/tools/PortableAppsLauncher
|
||||
packages/turndown-plugin-gfm/
|
||||
packages/turndown/
|
||||
packages/pdf-viewer/dist
|
||||
plugin_types/
|
||||
readme/
|
||||
|
||||
@@ -117,9 +115,6 @@ packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js.map
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.d.ts
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
|
||||
packages/app-cli/tests/services/plugins/defaultPluginsUtils.d.ts
|
||||
packages/app-cli/tests/services/plugins/defaultPluginsUtils.js
|
||||
packages/app-cli/tests/services/plugins/defaultPluginsUtils.js.map
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
|
||||
@@ -333,9 +328,6 @@ packages/app-desktop/gui/MainScreen/commands/openItem.js.map
|
||||
packages/app-desktop/gui/MainScreen/commands/openNote.d.ts
|
||||
packages/app-desktop/gui/MainScreen/commands/openNote.js
|
||||
packages/app-desktop/gui/MainScreen/commands/openNote.js.map
|
||||
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.d.ts
|
||||
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.js
|
||||
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.js.map
|
||||
packages/app-desktop/gui/MainScreen/commands/openTag.d.ts
|
||||
packages/app-desktop/gui/MainScreen/commands/openTag.js
|
||||
packages/app-desktop/gui/MainScreen/commands/openTag.js.map
|
||||
@@ -429,6 +421,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js.map
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.d.ts
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js.map
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/tables.d.ts
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/tables.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/tables.js.map
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.d.ts
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js.map
|
||||
@@ -600,9 +595,6 @@ packages/app-desktop/gui/OneDriveLoginScreen.js.map
|
||||
packages/app-desktop/gui/PasswordInput/PasswordInput.d.ts
|
||||
packages/app-desktop/gui/PasswordInput/PasswordInput.js
|
||||
packages/app-desktop/gui/PasswordInput/PasswordInput.js.map
|
||||
packages/app-desktop/gui/PdfViewer.d.ts
|
||||
packages/app-desktop/gui/PdfViewer.js
|
||||
packages/app-desktop/gui/PdfViewer.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
|
||||
@@ -699,6 +691,9 @@ packages/app-desktop/gui/StyleSheets/StyleSheetContainer.js.map
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.d.ts
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js.map
|
||||
packages/app-desktop/gui/TableEditorDialog/Dialog.d.ts
|
||||
packages/app-desktop/gui/TableEditorDialog/Dialog.js
|
||||
packages/app-desktop/gui/TableEditorDialog/Dialog.js.map
|
||||
packages/app-desktop/gui/TagList.d.ts
|
||||
packages/app-desktop/gui/TagList.js
|
||||
packages/app-desktop/gui/TagList.js.map
|
||||
@@ -852,15 +847,6 @@ packages/app-mobile/components/BackButtonDialogBox.js.map
|
||||
packages/app-mobile/components/CameraView.d.ts
|
||||
packages/app-mobile/components/CameraView.js
|
||||
packages/app-mobile/components/CameraView.js.map
|
||||
packages/app-mobile/components/CustomButton.d.ts
|
||||
packages/app-mobile/components/CustomButton.js
|
||||
packages/app-mobile/components/CustomButton.js.map
|
||||
packages/app-mobile/components/Dropdown.d.ts
|
||||
packages/app-mobile/components/Dropdown.js
|
||||
packages/app-mobile/components/Dropdown.js.map
|
||||
packages/app-mobile/components/ExtendedWebView.d.ts
|
||||
packages/app-mobile/components/ExtendedWebView.js
|
||||
packages/app-mobile/components/ExtendedWebView.js.map
|
||||
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.d.ts
|
||||
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
|
||||
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js.map
|
||||
@@ -873,114 +859,15 @@ packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js.ma
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.d.ts
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/createEditor.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/createEditor.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/createEditor.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/theme.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/types.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/types.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/types.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.js.map
|
||||
packages/app-mobile/components/NoteEditor/EditLinkDialog.d.ts
|
||||
packages/app-mobile/components/NoteEditor/EditLinkDialog.js
|
||||
packages/app-mobile/components/NoteEditor/EditLinkDialog.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.js.map
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.d.ts
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js.map
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.d.ts
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js.map
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.d.ts
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.js
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.js.map
|
||||
packages/app-mobile/components/NoteEditor/types.d.ts
|
||||
packages/app-mobile/components/NoteEditor/types.js
|
||||
packages/app-mobile/components/NoteEditor/types.js.map
|
||||
packages/app-mobile/components/NotesBar.d.ts
|
||||
packages/app-mobile/components/NotesBar.js
|
||||
packages/app-mobile/components/NotesBar.js.map
|
||||
packages/app-mobile/components/NotesBarListItem.d.ts
|
||||
packages/app-mobile/components/NotesBarListItem.js
|
||||
packages/app-mobile/components/NotesBarListItem.js.map
|
||||
packages/app-mobile/components/ScreenHeader.d.ts
|
||||
packages/app-mobile/components/ScreenHeader.js
|
||||
packages/app-mobile/components/ScreenHeader.js.map
|
||||
packages/app-mobile/components/SelectDateTimeDialog.d.ts
|
||||
packages/app-mobile/components/SelectDateTimeDialog.js
|
||||
packages/app-mobile/components/SelectDateTimeDialog.js.map
|
||||
packages/app-mobile/components/SideMenu.d.ts
|
||||
packages/app-mobile/components/SideMenu.js
|
||||
packages/app-mobile/components/SideMenu.js.map
|
||||
packages/app-mobile/components/checkbox.d.ts
|
||||
packages/app-mobile/components/checkbox.js
|
||||
packages/app-mobile/components/checkbox.js.map
|
||||
packages/app-mobile/components/getResponsiveValue.d.ts
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/getResponsiveValue.js.map
|
||||
packages/app-mobile/components/getResponsiveValue.test.d.ts
|
||||
packages/app-mobile/components/getResponsiveValue.test.js
|
||||
packages/app-mobile/components/getResponsiveValue.test.js.map
|
||||
packages/app-mobile/components/global-style.d.ts
|
||||
packages/app-mobile/components/global-style.js
|
||||
packages/app-mobile/components/global-style.js.map
|
||||
packages/app-mobile/components/screens/ConfigScreen.d.ts
|
||||
packages/app-mobile/components/screens/ConfigScreen.js
|
||||
packages/app-mobile/components/screens/ConfigScreen.js.map
|
||||
@@ -993,12 +880,6 @@ packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js.map
|
||||
packages/app-mobile/components/screens/encryption-config.d.ts
|
||||
packages/app-mobile/components/screens/encryption-config.js
|
||||
packages/app-mobile/components/screens/encryption-config.js.map
|
||||
packages/app-mobile/components/useStyles.d.ts
|
||||
packages/app-mobile/components/useStyles.js
|
||||
packages/app-mobile/components/useStyles.js.map
|
||||
packages/app-mobile/gulpfile.d.ts
|
||||
packages/app-mobile/gulpfile.js
|
||||
packages/app-mobile/gulpfile.js.map
|
||||
packages/app-mobile/root.d.ts
|
||||
packages/app-mobile/root.js
|
||||
packages/app-mobile/root.js.map
|
||||
@@ -1014,9 +895,6 @@ packages/app-mobile/services/e2ee/RSA.react-native.js.map
|
||||
packages/app-mobile/setupQuickActions.d.ts
|
||||
packages/app-mobile/setupQuickActions.js
|
||||
packages/app-mobile/setupQuickActions.js.map
|
||||
packages/app-mobile/tools/buildInjectedJs.d.ts
|
||||
packages/app-mobile/tools/buildInjectedJs.js
|
||||
packages/app-mobile/tools/buildInjectedJs.js.map
|
||||
packages/app-mobile/utils/ShareExtension.d.ts
|
||||
packages/app-mobile/utils/ShareExtension.js
|
||||
packages/app-mobile/utils/ShareExtension.js.map
|
||||
@@ -1116,12 +994,6 @@ packages/lib/ClipperServer.js.map
|
||||
packages/lib/CssUtils.d.ts
|
||||
packages/lib/CssUtils.js
|
||||
packages/lib/CssUtils.js.map
|
||||
packages/lib/EventDispatcher.d.ts
|
||||
packages/lib/EventDispatcher.js
|
||||
packages/lib/EventDispatcher.js.map
|
||||
packages/lib/EventDispatcher.test.d.ts
|
||||
packages/lib/EventDispatcher.test.js
|
||||
packages/lib/EventDispatcher.test.js.map
|
||||
packages/lib/HtmlToMd.d.ts
|
||||
packages/lib/HtmlToMd.js
|
||||
packages/lib/HtmlToMd.js.map
|
||||
@@ -1602,9 +1474,6 @@ packages/lib/services/interop/InteropService_Importer_Md_frontmatter.test.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.js.map
|
||||
packages/lib/services/interop/types.d.ts
|
||||
packages/lib/services/interop/types.js
|
||||
packages/lib/services/interop/types.js.map
|
||||
@@ -1626,9 +1495,6 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js.map
|
||||
packages/lib/services/keychain/KeychainServiceDriverBase.d.ts
|
||||
packages/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
packages/lib/services/keychain/KeychainServiceDriverBase.js.map
|
||||
packages/lib/services/plugins/BasePlatformImplementation.d.ts
|
||||
packages/lib/services/plugins/BasePlatformImplementation.js
|
||||
packages/lib/services/plugins/BasePlatformImplementation.js.map
|
||||
packages/lib/services/plugins/BasePluginRunner.d.ts
|
||||
packages/lib/services/plugins/BasePluginRunner.js
|
||||
packages/lib/services/plugins/BasePluginRunner.js.map
|
||||
@@ -1713,12 +1579,6 @@ packages/lib/services/plugins/api/JoplinWorkspace.js.map
|
||||
packages/lib/services/plugins/api/types.d.ts
|
||||
packages/lib/services/plugins/api/types.js
|
||||
packages/lib/services/plugins/api/types.js.map
|
||||
packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.d.ts
|
||||
packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js
|
||||
packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js.map
|
||||
packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.d.ts
|
||||
packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js
|
||||
packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js.map
|
||||
packages/lib/services/plugins/reducer.d.ts
|
||||
packages/lib/services/plugins/reducer.js
|
||||
packages/lib/services/plugins/reducer.js.map
|
||||
@@ -2025,60 +1885,6 @@ packages/lib/uuid.js.map
|
||||
packages/lib/versionInfo.d.ts
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.js.map
|
||||
packages/pdf-viewer/FullViewer.d.ts
|
||||
packages/pdf-viewer/FullViewer.js
|
||||
packages/pdf-viewer/FullViewer.js.map
|
||||
packages/pdf-viewer/Page.d.ts
|
||||
packages/pdf-viewer/Page.js
|
||||
packages/pdf-viewer/Page.js.map
|
||||
packages/pdf-viewer/PdfDocument.d.ts
|
||||
packages/pdf-viewer/PdfDocument.js
|
||||
packages/pdf-viewer/PdfDocument.js.map
|
||||
packages/pdf-viewer/VerticalPages.d.ts
|
||||
packages/pdf-viewer/VerticalPages.js
|
||||
packages/pdf-viewer/VerticalPages.js.map
|
||||
packages/pdf-viewer/hooks/useIsFocused.d.ts
|
||||
packages/pdf-viewer/hooks/useIsFocused.js
|
||||
packages/pdf-viewer/hooks/useIsFocused.js.map
|
||||
packages/pdf-viewer/hooks/useIsVisible.d.ts
|
||||
packages/pdf-viewer/hooks/useIsVisible.js
|
||||
packages/pdf-viewer/hooks/useIsVisible.js.map
|
||||
packages/pdf-viewer/hooks/usePdfDocument.d.ts
|
||||
packages/pdf-viewer/hooks/usePdfDocument.js
|
||||
packages/pdf-viewer/hooks/usePdfDocument.js.map
|
||||
packages/pdf-viewer/hooks/useScaledSize.d.ts
|
||||
packages/pdf-viewer/hooks/useScaledSize.js
|
||||
packages/pdf-viewer/hooks/useScaledSize.js.map
|
||||
packages/pdf-viewer/hooks/useScrollSaver.d.ts
|
||||
packages/pdf-viewer/hooks/useScrollSaver.js
|
||||
packages/pdf-viewer/hooks/useScrollSaver.js.map
|
||||
packages/pdf-viewer/hooks/useVisibleOnSelect.d.ts
|
||||
packages/pdf-viewer/hooks/useVisibleOnSelect.js
|
||||
packages/pdf-viewer/hooks/useVisibleOnSelect.js.map
|
||||
packages/pdf-viewer/main.d.ts
|
||||
packages/pdf-viewer/main.js
|
||||
packages/pdf-viewer/main.js.map
|
||||
packages/pdf-viewer/messageService.d.ts
|
||||
packages/pdf-viewer/messageService.js
|
||||
packages/pdf-viewer/messageService.js.map
|
||||
packages/pdf-viewer/miniViewer.d.ts
|
||||
packages/pdf-viewer/miniViewer.js
|
||||
packages/pdf-viewer/miniViewer.js.map
|
||||
packages/pdf-viewer/pdfSource.test.d.ts
|
||||
packages/pdf-viewer/pdfSource.test.js
|
||||
packages/pdf-viewer/pdfSource.test.js.map
|
||||
packages/pdf-viewer/types.d.ts
|
||||
packages/pdf-viewer/types.js
|
||||
packages/pdf-viewer/types.js.map
|
||||
packages/pdf-viewer/ui/GotoPage.d.ts
|
||||
packages/pdf-viewer/ui/GotoPage.js
|
||||
packages/pdf-viewer/ui/GotoPage.js.map
|
||||
packages/pdf-viewer/ui/IconButtons.d.ts
|
||||
packages/pdf-viewer/ui/IconButtons.js
|
||||
packages/pdf-viewer/ui/IconButtons.js.map
|
||||
packages/pdf-viewer/ui/ZoomControls.d.ts
|
||||
packages/pdf-viewer/ui/ZoomControls.js
|
||||
packages/pdf-viewer/ui/ZoomControls.js.map
|
||||
packages/plugin-repo-cli/commands/updateRelease.d.ts
|
||||
packages/plugin-repo-cli/commands/updateRelease.js
|
||||
packages/plugin-repo-cli/commands/updateRelease.js.map
|
||||
@@ -2130,9 +1936,6 @@ packages/plugins/ToggleSidebars/api/types.js.map
|
||||
packages/plugins/ToggleSidebars/src/index.d.ts
|
||||
packages/plugins/ToggleSidebars/src/index.js
|
||||
packages/plugins/ToggleSidebars/src/index.js.map
|
||||
packages/react-native-saf-x/src/index.d.ts
|
||||
packages/react-native-saf-x/src/index.js
|
||||
packages/react-native-saf-x/src/index.js.map
|
||||
packages/renderer/HtmlToHtml.d.ts
|
||||
packages/renderer/HtmlToHtml.js
|
||||
packages/renderer/HtmlToHtml.js.map
|
||||
@@ -2229,12 +2032,6 @@ packages/tools/buildServerDocker.js.map
|
||||
packages/tools/buildServerDocker.test.d.ts
|
||||
packages/tools/buildServerDocker.test.js
|
||||
packages/tools/buildServerDocker.test.js.map
|
||||
packages/tools/bundleDefaultPlugins.d.ts
|
||||
packages/tools/bundleDefaultPlugins.js
|
||||
packages/tools/bundleDefaultPlugins.js.map
|
||||
packages/tools/bundleDefaultPlugins.test.d.ts
|
||||
packages/tools/bundleDefaultPlugins.test.js
|
||||
packages/tools/bundleDefaultPlugins.test.js.map
|
||||
packages/tools/checkLibPaths.d.ts
|
||||
packages/tools/checkLibPaths.js
|
||||
packages/tools/checkLibPaths.js.map
|
||||
|
||||
11
.eslintrc.js
11
.eslintrc.js
@@ -29,7 +29,6 @@ module.exports = {
|
||||
|
||||
// React Native variables
|
||||
'__DEV__': 'readonly',
|
||||
'JSX': 'readonly',
|
||||
|
||||
// Clipper variables
|
||||
'browserSupportsPromises_': true,
|
||||
@@ -77,16 +76,13 @@ module.exports = {
|
||||
|
||||
'no-array-constructor': ['error'],
|
||||
'radix': ['error'],
|
||||
'eqeqeq': ['error', 'always'],
|
||||
|
||||
// Warn only for now because fixing everything would take too much
|
||||
// refactoring, but new code should try to stick to it.
|
||||
// 'complexity': ['warn', { max: 10 }],
|
||||
|
||||
// Checks rules of Hooks
|
||||
'@seiyab/react-hooks/rules-of-hooks': 'error',
|
||||
'@seiyab/react-hooks/exhaustive-deps': ['error', { 'ignoreThisDependency': 'props' }],
|
||||
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
// Checks effect dependencies
|
||||
// Disable because of this: https://github.com/facebook/react/issues/16265
|
||||
// "react-hooks/exhaustive-deps": "warn",
|
||||
@@ -137,10 +133,7 @@ module.exports = {
|
||||
'plugins': [
|
||||
'react',
|
||||
'@typescript-eslint',
|
||||
// Need to use a fork of the official rules of hooks because of this bug:
|
||||
// https://github.com/facebook/react/issues/16265
|
||||
'@seiyab/eslint-plugin-react-hooks',
|
||||
// 'react-hooks',
|
||||
'react-hooks',
|
||||
'import',
|
||||
],
|
||||
'overrides': [
|
||||
|
||||
8
.github/scripts/run_ci.sh
vendored
8
.github/scripts/run_ci.sh
vendored
@@ -57,11 +57,6 @@ echo "Yarn $( yarn -v )"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
yarn install
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
echo "Yarn installation failed. Search for 'exit code 1' in the log for more information."
|
||||
exit $testResult
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Run test units. Only do it for pull requests and dev branch because we don't
|
||||
@@ -175,9 +170,6 @@ cd "$ROOT_DIR/packages/app-desktop"
|
||||
|
||||
if [[ $GIT_TAG_NAME = v* ]]; then
|
||||
echo "Step: Building and publishing desktop application..."
|
||||
# cd "$ROOT_DIR/packages/tools"
|
||||
# node bundleDefaultPlugins.js
|
||||
cd "$ROOT_DIR/packages/app-desktop"
|
||||
USE_HARD_LINKS=false yarn run dist
|
||||
elif [[ $IS_LINUX = 1 ]] && [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
|
||||
echo "Step: Building Docker Image..."
|
||||
|
||||
222
.gitignore
vendored
222
.gitignore
vendored
@@ -105,9 +105,6 @@ packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js.map
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.d.ts
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
|
||||
packages/app-cli/tests/services/plugins/defaultPluginsUtils.d.ts
|
||||
packages/app-cli/tests/services/plugins/defaultPluginsUtils.js
|
||||
packages/app-cli/tests/services/plugins/defaultPluginsUtils.js.map
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
|
||||
@@ -321,9 +318,6 @@ packages/app-desktop/gui/MainScreen/commands/openItem.js.map
|
||||
packages/app-desktop/gui/MainScreen/commands/openNote.d.ts
|
||||
packages/app-desktop/gui/MainScreen/commands/openNote.js
|
||||
packages/app-desktop/gui/MainScreen/commands/openNote.js.map
|
||||
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.d.ts
|
||||
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.js
|
||||
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.js.map
|
||||
packages/app-desktop/gui/MainScreen/commands/openTag.d.ts
|
||||
packages/app-desktop/gui/MainScreen/commands/openTag.js
|
||||
packages/app-desktop/gui/MainScreen/commands/openTag.js.map
|
||||
@@ -417,6 +411,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js.map
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.d.ts
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js.map
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/tables.d.ts
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/tables.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/tables.js.map
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.d.ts
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js.map
|
||||
@@ -588,9 +585,6 @@ packages/app-desktop/gui/OneDriveLoginScreen.js.map
|
||||
packages/app-desktop/gui/PasswordInput/PasswordInput.d.ts
|
||||
packages/app-desktop/gui/PasswordInput/PasswordInput.js
|
||||
packages/app-desktop/gui/PasswordInput/PasswordInput.js.map
|
||||
packages/app-desktop/gui/PdfViewer.d.ts
|
||||
packages/app-desktop/gui/PdfViewer.js
|
||||
packages/app-desktop/gui/PdfViewer.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
|
||||
@@ -687,6 +681,9 @@ packages/app-desktop/gui/StyleSheets/StyleSheetContainer.js.map
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.d.ts
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js.map
|
||||
packages/app-desktop/gui/TableEditorDialog/Dialog.d.ts
|
||||
packages/app-desktop/gui/TableEditorDialog/Dialog.js
|
||||
packages/app-desktop/gui/TableEditorDialog/Dialog.js.map
|
||||
packages/app-desktop/gui/TagList.d.ts
|
||||
packages/app-desktop/gui/TagList.js
|
||||
packages/app-desktop/gui/TagList.js.map
|
||||
@@ -840,15 +837,6 @@ packages/app-mobile/components/BackButtonDialogBox.js.map
|
||||
packages/app-mobile/components/CameraView.d.ts
|
||||
packages/app-mobile/components/CameraView.js
|
||||
packages/app-mobile/components/CameraView.js.map
|
||||
packages/app-mobile/components/CustomButton.d.ts
|
||||
packages/app-mobile/components/CustomButton.js
|
||||
packages/app-mobile/components/CustomButton.js.map
|
||||
packages/app-mobile/components/Dropdown.d.ts
|
||||
packages/app-mobile/components/Dropdown.js
|
||||
packages/app-mobile/components/Dropdown.js.map
|
||||
packages/app-mobile/components/ExtendedWebView.d.ts
|
||||
packages/app-mobile/components/ExtendedWebView.js
|
||||
packages/app-mobile/components/ExtendedWebView.js.map
|
||||
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.d.ts
|
||||
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
|
||||
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js.map
|
||||
@@ -861,114 +849,15 @@ packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js.ma
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.d.ts
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/createEditor.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/createEditor.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/createEditor.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/theme.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/types.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/types.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/types.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.js.map
|
||||
packages/app-mobile/components/NoteEditor/EditLinkDialog.d.ts
|
||||
packages/app-mobile/components/NoteEditor/EditLinkDialog.js
|
||||
packages/app-mobile/components/NoteEditor/EditLinkDialog.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleOverflowButton.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToggleSpaceButton.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.js.map
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.d.ts
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js.map
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.d.ts
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror.js.map
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.d.ts
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js.map
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.d.ts
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js.map
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.d.ts
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.js
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.js.map
|
||||
packages/app-mobile/components/NoteEditor/types.d.ts
|
||||
packages/app-mobile/components/NoteEditor/types.js
|
||||
packages/app-mobile/components/NoteEditor/types.js.map
|
||||
packages/app-mobile/components/NotesBar.d.ts
|
||||
packages/app-mobile/components/NotesBar.js
|
||||
packages/app-mobile/components/NotesBar.js.map
|
||||
packages/app-mobile/components/NotesBarListItem.d.ts
|
||||
packages/app-mobile/components/NotesBarListItem.js
|
||||
packages/app-mobile/components/NotesBarListItem.js.map
|
||||
packages/app-mobile/components/ScreenHeader.d.ts
|
||||
packages/app-mobile/components/ScreenHeader.js
|
||||
packages/app-mobile/components/ScreenHeader.js.map
|
||||
packages/app-mobile/components/SelectDateTimeDialog.d.ts
|
||||
packages/app-mobile/components/SelectDateTimeDialog.js
|
||||
packages/app-mobile/components/SelectDateTimeDialog.js.map
|
||||
packages/app-mobile/components/SideMenu.d.ts
|
||||
packages/app-mobile/components/SideMenu.js
|
||||
packages/app-mobile/components/SideMenu.js.map
|
||||
packages/app-mobile/components/checkbox.d.ts
|
||||
packages/app-mobile/components/checkbox.js
|
||||
packages/app-mobile/components/checkbox.js.map
|
||||
packages/app-mobile/components/getResponsiveValue.d.ts
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/getResponsiveValue.js.map
|
||||
packages/app-mobile/components/getResponsiveValue.test.d.ts
|
||||
packages/app-mobile/components/getResponsiveValue.test.js
|
||||
packages/app-mobile/components/getResponsiveValue.test.js.map
|
||||
packages/app-mobile/components/global-style.d.ts
|
||||
packages/app-mobile/components/global-style.js
|
||||
packages/app-mobile/components/global-style.js.map
|
||||
packages/app-mobile/components/screens/ConfigScreen.d.ts
|
||||
packages/app-mobile/components/screens/ConfigScreen.js
|
||||
packages/app-mobile/components/screens/ConfigScreen.js.map
|
||||
@@ -981,12 +870,6 @@ packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js.map
|
||||
packages/app-mobile/components/screens/encryption-config.d.ts
|
||||
packages/app-mobile/components/screens/encryption-config.js
|
||||
packages/app-mobile/components/screens/encryption-config.js.map
|
||||
packages/app-mobile/components/useStyles.d.ts
|
||||
packages/app-mobile/components/useStyles.js
|
||||
packages/app-mobile/components/useStyles.js.map
|
||||
packages/app-mobile/gulpfile.d.ts
|
||||
packages/app-mobile/gulpfile.js
|
||||
packages/app-mobile/gulpfile.js.map
|
||||
packages/app-mobile/root.d.ts
|
||||
packages/app-mobile/root.js
|
||||
packages/app-mobile/root.js.map
|
||||
@@ -1002,9 +885,6 @@ packages/app-mobile/services/e2ee/RSA.react-native.js.map
|
||||
packages/app-mobile/setupQuickActions.d.ts
|
||||
packages/app-mobile/setupQuickActions.js
|
||||
packages/app-mobile/setupQuickActions.js.map
|
||||
packages/app-mobile/tools/buildInjectedJs.d.ts
|
||||
packages/app-mobile/tools/buildInjectedJs.js
|
||||
packages/app-mobile/tools/buildInjectedJs.js.map
|
||||
packages/app-mobile/utils/ShareExtension.d.ts
|
||||
packages/app-mobile/utils/ShareExtension.js
|
||||
packages/app-mobile/utils/ShareExtension.js.map
|
||||
@@ -1104,12 +984,6 @@ packages/lib/ClipperServer.js.map
|
||||
packages/lib/CssUtils.d.ts
|
||||
packages/lib/CssUtils.js
|
||||
packages/lib/CssUtils.js.map
|
||||
packages/lib/EventDispatcher.d.ts
|
||||
packages/lib/EventDispatcher.js
|
||||
packages/lib/EventDispatcher.js.map
|
||||
packages/lib/EventDispatcher.test.d.ts
|
||||
packages/lib/EventDispatcher.test.js
|
||||
packages/lib/EventDispatcher.test.js.map
|
||||
packages/lib/HtmlToMd.d.ts
|
||||
packages/lib/HtmlToMd.js
|
||||
packages/lib/HtmlToMd.js.map
|
||||
@@ -1590,9 +1464,6 @@ packages/lib/services/interop/InteropService_Importer_Md_frontmatter.test.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.js.map
|
||||
packages/lib/services/interop/types.d.ts
|
||||
packages/lib/services/interop/types.js
|
||||
packages/lib/services/interop/types.js.map
|
||||
@@ -1614,9 +1485,6 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js.map
|
||||
packages/lib/services/keychain/KeychainServiceDriverBase.d.ts
|
||||
packages/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
packages/lib/services/keychain/KeychainServiceDriverBase.js.map
|
||||
packages/lib/services/plugins/BasePlatformImplementation.d.ts
|
||||
packages/lib/services/plugins/BasePlatformImplementation.js
|
||||
packages/lib/services/plugins/BasePlatformImplementation.js.map
|
||||
packages/lib/services/plugins/BasePluginRunner.d.ts
|
||||
packages/lib/services/plugins/BasePluginRunner.js
|
||||
packages/lib/services/plugins/BasePluginRunner.js.map
|
||||
@@ -1701,12 +1569,6 @@ packages/lib/services/plugins/api/JoplinWorkspace.js.map
|
||||
packages/lib/services/plugins/api/types.d.ts
|
||||
packages/lib/services/plugins/api/types.js
|
||||
packages/lib/services/plugins/api/types.js.map
|
||||
packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.d.ts
|
||||
packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js
|
||||
packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js.map
|
||||
packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.d.ts
|
||||
packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js
|
||||
packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js.map
|
||||
packages/lib/services/plugins/reducer.d.ts
|
||||
packages/lib/services/plugins/reducer.js
|
||||
packages/lib/services/plugins/reducer.js.map
|
||||
@@ -2013,60 +1875,6 @@ packages/lib/uuid.js.map
|
||||
packages/lib/versionInfo.d.ts
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.js.map
|
||||
packages/pdf-viewer/FullViewer.d.ts
|
||||
packages/pdf-viewer/FullViewer.js
|
||||
packages/pdf-viewer/FullViewer.js.map
|
||||
packages/pdf-viewer/Page.d.ts
|
||||
packages/pdf-viewer/Page.js
|
||||
packages/pdf-viewer/Page.js.map
|
||||
packages/pdf-viewer/PdfDocument.d.ts
|
||||
packages/pdf-viewer/PdfDocument.js
|
||||
packages/pdf-viewer/PdfDocument.js.map
|
||||
packages/pdf-viewer/VerticalPages.d.ts
|
||||
packages/pdf-viewer/VerticalPages.js
|
||||
packages/pdf-viewer/VerticalPages.js.map
|
||||
packages/pdf-viewer/hooks/useIsFocused.d.ts
|
||||
packages/pdf-viewer/hooks/useIsFocused.js
|
||||
packages/pdf-viewer/hooks/useIsFocused.js.map
|
||||
packages/pdf-viewer/hooks/useIsVisible.d.ts
|
||||
packages/pdf-viewer/hooks/useIsVisible.js
|
||||
packages/pdf-viewer/hooks/useIsVisible.js.map
|
||||
packages/pdf-viewer/hooks/usePdfDocument.d.ts
|
||||
packages/pdf-viewer/hooks/usePdfDocument.js
|
||||
packages/pdf-viewer/hooks/usePdfDocument.js.map
|
||||
packages/pdf-viewer/hooks/useScaledSize.d.ts
|
||||
packages/pdf-viewer/hooks/useScaledSize.js
|
||||
packages/pdf-viewer/hooks/useScaledSize.js.map
|
||||
packages/pdf-viewer/hooks/useScrollSaver.d.ts
|
||||
packages/pdf-viewer/hooks/useScrollSaver.js
|
||||
packages/pdf-viewer/hooks/useScrollSaver.js.map
|
||||
packages/pdf-viewer/hooks/useVisibleOnSelect.d.ts
|
||||
packages/pdf-viewer/hooks/useVisibleOnSelect.js
|
||||
packages/pdf-viewer/hooks/useVisibleOnSelect.js.map
|
||||
packages/pdf-viewer/main.d.ts
|
||||
packages/pdf-viewer/main.js
|
||||
packages/pdf-viewer/main.js.map
|
||||
packages/pdf-viewer/messageService.d.ts
|
||||
packages/pdf-viewer/messageService.js
|
||||
packages/pdf-viewer/messageService.js.map
|
||||
packages/pdf-viewer/miniViewer.d.ts
|
||||
packages/pdf-viewer/miniViewer.js
|
||||
packages/pdf-viewer/miniViewer.js.map
|
||||
packages/pdf-viewer/pdfSource.test.d.ts
|
||||
packages/pdf-viewer/pdfSource.test.js
|
||||
packages/pdf-viewer/pdfSource.test.js.map
|
||||
packages/pdf-viewer/types.d.ts
|
||||
packages/pdf-viewer/types.js
|
||||
packages/pdf-viewer/types.js.map
|
||||
packages/pdf-viewer/ui/GotoPage.d.ts
|
||||
packages/pdf-viewer/ui/GotoPage.js
|
||||
packages/pdf-viewer/ui/GotoPage.js.map
|
||||
packages/pdf-viewer/ui/IconButtons.d.ts
|
||||
packages/pdf-viewer/ui/IconButtons.js
|
||||
packages/pdf-viewer/ui/IconButtons.js.map
|
||||
packages/pdf-viewer/ui/ZoomControls.d.ts
|
||||
packages/pdf-viewer/ui/ZoomControls.js
|
||||
packages/pdf-viewer/ui/ZoomControls.js.map
|
||||
packages/plugin-repo-cli/commands/updateRelease.d.ts
|
||||
packages/plugin-repo-cli/commands/updateRelease.js
|
||||
packages/plugin-repo-cli/commands/updateRelease.js.map
|
||||
@@ -2118,9 +1926,6 @@ packages/plugins/ToggleSidebars/api/types.js.map
|
||||
packages/plugins/ToggleSidebars/src/index.d.ts
|
||||
packages/plugins/ToggleSidebars/src/index.js
|
||||
packages/plugins/ToggleSidebars/src/index.js.map
|
||||
packages/react-native-saf-x/src/index.d.ts
|
||||
packages/react-native-saf-x/src/index.js
|
||||
packages/react-native-saf-x/src/index.js.map
|
||||
packages/renderer/HtmlToHtml.d.ts
|
||||
packages/renderer/HtmlToHtml.js
|
||||
packages/renderer/HtmlToHtml.js.map
|
||||
@@ -2217,12 +2022,6 @@ packages/tools/buildServerDocker.js.map
|
||||
packages/tools/buildServerDocker.test.d.ts
|
||||
packages/tools/buildServerDocker.test.js
|
||||
packages/tools/buildServerDocker.test.js.map
|
||||
packages/tools/bundleDefaultPlugins.d.ts
|
||||
packages/tools/bundleDefaultPlugins.js
|
||||
packages/tools/bundleDefaultPlugins.js.map
|
||||
packages/tools/bundleDefaultPlugins.test.d.ts
|
||||
packages/tools/bundleDefaultPlugins.test.js
|
||||
packages/tools/bundleDefaultPlugins.test.js.map
|
||||
packages/tools/checkLibPaths.d.ts
|
||||
packages/tools/checkLibPaths.js
|
||||
packages/tools/checkLibPaths.js.map
|
||||
@@ -2317,6 +2116,3 @@ packages/tools/website/utils/types.d.ts
|
||||
packages/tools/website/utils/types.js
|
||||
packages/tools/website/utils/types.js.map
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
packages/app-mobile/components/get-responsive-value.test.js
|
||||
packages/app-mobile/components/get-responsive-value.test.js
|
||||
packages/app-mobile/components/get-responsive-value.test.js
|
||||
|
||||
@@ -9,13 +9,11 @@ import PluginManager from 'tinymce/core/api/PluginManager';
|
||||
import * as Api from './api/Api';
|
||||
import * as Commands from './api/Commands';
|
||||
import * as Keyboard from './core/Keyboard';
|
||||
import * as Mouse from './core/Mouse'
|
||||
import * as Buttons from './ui/Buttons';
|
||||
|
||||
export default function () {
|
||||
PluginManager.add('joplinLists', function (editor) {
|
||||
Keyboard.setup(editor);
|
||||
Mouse.setup(editor);
|
||||
Buttons.register(editor);
|
||||
Commands.register(editor);
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { isJoplinChecklistItem } from '../listModel/JoplinListUtil';
|
||||
|
||||
|
||||
const setup = function (editor) {
|
||||
const editorClickHandler = (event) => {
|
||||
if (!isJoplinChecklistItem(event.target)) return;
|
||||
|
||||
// We only process the click if it's within the checkbox itself (and not the label).
|
||||
// That checkbox, based on
|
||||
// the current styling is in the negative margin, so offsetX is negative when clicking
|
||||
// on the checkbox itself, and positive when clicking on the label. This is strongly
|
||||
// dependent on how the checkbox is styled, so if the style is changed, this might need
|
||||
// to be updated too.
|
||||
// For the styling, see:
|
||||
// packages/renderer/MdToHtml/rules/checkbox.ts
|
||||
//
|
||||
// The previous solution was to use "pointer-event: none", which mostly work, however
|
||||
// it means that links are no longer clickable when they are within the checkbox label.
|
||||
if (event.offsetX >= 0) return;
|
||||
|
||||
editor.execCommand('ToggleJoplinChecklistItem', false, { element: event.target });
|
||||
}
|
||||
editor.on('click', editorClickHandler);
|
||||
};
|
||||
|
||||
export { setup };
|
||||
@@ -10,7 +10,7 @@ import * as Settings from '../api/Settings';
|
||||
import * as NodeType from '../core/NodeType';
|
||||
import Editor from 'tinymce/core/api/Editor';
|
||||
import { isCustomList } from '../core/Util';
|
||||
import { findContainerListTypeFromEvent } from '../listModel/JoplinListUtil';
|
||||
import { findContainerListTypeFromEvent, isJoplinChecklistItem } from '../listModel/JoplinListUtil';
|
||||
|
||||
const findIndex = function (list, predicate) {
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
@@ -38,11 +38,37 @@ const listState = function (editor: Editor, listName, options:any = {}) {
|
||||
buttonApi.setActive(listType === options.listType && lists.length > 0 && lists[0].nodeName === listName && !isCustomList(lists[0]));
|
||||
};
|
||||
|
||||
const editorClickHandler = (event) => {
|
||||
if (!isJoplinChecklistItem(event.target)) return;
|
||||
|
||||
// We only process the click if it's within the checkbox itself (and not the label).
|
||||
// That checkbox, based on
|
||||
// the current styling is in the negative margin, so offsetX is negative when clicking
|
||||
// on the checkbox itself, and positive when clicking on the label. This is strongly
|
||||
// dependent on how the checkbox is styled, so if the style is changed, this might need
|
||||
// to be updated too.
|
||||
// For the styling, see:
|
||||
// packages/renderer/MdToHtml/rules/checkbox.ts
|
||||
//
|
||||
// The previous solution was to use "pointer-event: none", which mostly work, however
|
||||
// it means that links are no longer clickable when they are within the checkbox label.
|
||||
if (event.offsetX >= 0) return;
|
||||
|
||||
editor.execCommand('ToggleJoplinChecklistItem', false, { element: event.target });
|
||||
}
|
||||
|
||||
if (options.listType === 'joplinChecklist') {
|
||||
editor.on('click', editorClickHandler);
|
||||
}
|
||||
|
||||
editor.on('NodeChange', nodeChangeHandler);
|
||||
|
||||
return () => {
|
||||
if (options.listType === 'joplinChecklist') {
|
||||
editor.off('click', editorClickHandler);
|
||||
}
|
||||
editor.off('NodeChange', nodeChangeHandler);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Tue, 06 Sep 2022 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Tue, 06 Sep 2022 00:00:00 GMT</pubDate><item><title><![CDATA[Joplin interview on Website Planet]]></title><description><![CDATA[<p>Website Planet has recently conducted an interview about Joplin - it may give you some insight on the current status of the project, our priorities, and future plans! More on the article page - <a href="https://www.websiteplanet.com/blog/interview-joplin/">Organise Your Thoughts with Open Source Note-Taking App, Joplin</a></p>
|
||||
]]></description><link>https://joplinapp.org/news/20220906-interview-websiteplanet/</link><guid isPermaLink="false">20220906-interview-websiteplanet</guid><pubDate>Tue, 06 Sep 2022 00:00:00 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Joplin first meetup on 30 August!]]></title><description><![CDATA[<p>We are glad to announce <a href="https://www.meetup.com/joplin/events/287611873/">the first Joplin Meetup</a> that will take place on 30 August 2022 in London!</p>
|
||||
<p>This is an opportunity to meet other Joplin users as well as some of the main contributors, to discuss the apps, or to ask questions and exchange tips and tricks on how to use the app, develop plugins or contribute to the application. Everybody, technical or not, is welcome!</p>
|
||||
<p>We will meet at the Old Thameside Inn next to London Bridge. If the weather allows we will be on the terrace outside, if not inside.</p>
|
||||
<p>More information on the official Meetup page:</p>
|
||||
<p><a href="https://www.meetup.com/joplin/events/287611873/">https://www.meetup.com/joplin/events/287611873/</a></p>
|
||||
]]></description><link>https://joplinapp.org/news/20220808-first-meetup/</link><guid isPermaLink="false">20220808-first-meetup</guid><pubDate>Mon, 08 Aug 2022 00:00:00 GMT</pubDate><twitter-text>Joplin will have its first Meetup on 30 August! Come and join us at the Old Thameside Inn next to London Bridge! https://www.meetup.com/joplin/events/287611873/</twitter-text></item><item><title><![CDATA[Joplin 2.8 is available!]]></title><description><![CDATA[<p>As always a lot of changes and new features in this new version available on both desktop and mobile.</p>
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Mon, 06 Jun 2022 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Mon, 06 Jun 2022 00:00:00 GMT</pubDate><item><title><![CDATA[Joplin 2.8 is available!]]></title><description><![CDATA[<p>As always a lot of changes and new features in this new version available on both desktop and mobile.</p>
|
||||
<h1>Multiple profile support<a name="multiple-profile-support" href="#multiple-profile-support" class="heading-anchor">🔗</a></h1>
|
||||
<p>Perhaps the most visible change in this version is the support for multiple profiles. You can now create as many application profile as you wish, each with their own settings, and easily switch from one to another. The main use case is to support for example a "work" profile and a "personal" profile, to allow you to keep things independent, and each profile can sync with a different sync target.</p>
|
||||
<p>To create a new profile, open <strong>File > Switch profile</strong> and select <strong>Create new profile</strong>, enter the profile name and press OK. The app will automatically switch to this new profile, which you can now configure.</p>
|
||||
@@ -259,4 +253,9 @@
|
||||
<p>Also many thanks to everyone who voted and contributed to the tagline discussion! It helped narrow down what the tagline should be, along with the equally important description below. If you have any question or notice any issue with the website let me know!</p>
|
||||
]]></description><link>https://joplinapp.org/news/20210711-095626/</link><guid isPermaLink="false">20210711-095626</guid><pubDate>Sun, 11 Jul 2021 09:56:26 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Poll: What should Joplin tagline be?]]></title><description><![CDATA[<p>Thanks everyone for your tagline suggestions - there were lots of good ideas in there. I've compiled a few of them and create a poll in the forum, so please cast your vote! And if you have any other suggestions on what would make a good tagline, feel free to post over there or here.</p>
|
||||
<p><a href="https://discourse.joplinapp.org/t/poll-what-should-joplin-tagline-be/18487">https://discourse.joplinapp.org/t/poll-what-should-joplin-tagline-be/18487</a></p>
|
||||
]]></description><link>https://joplinapp.org/news/20210706-140228/</link><guid isPermaLink="false">20210706-140228</guid><pubDate>Tue, 06 Jul 2021 14:02:28 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
|
||||
]]></description><link>https://joplinapp.org/news/20210706-140228/</link><guid isPermaLink="false">20210706-140228</guid><pubDate>Tue, 06 Jul 2021 14:02:28 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Any ideas for a Joplin tagline?]]></title><description><![CDATA[<p>I'm going to update the website front page to better showcase the application. I have most of the sections right, but the part I'm still not sure about is the top tagline, so I'm wondering if anyone had any suggestion about it?</p>
|
||||
<p>From what I can see on Google Keep or Evernote for example it should be something like "Use our app to get X or Y benefit", it should be a sentence that directly speaks to the user essentially.</p>
|
||||
<p>So far I have "Your notes, anywhere you are" but I'm not certain that's particularly inspiring. Any other idea about what tagline could be used?</p>
|
||||
]]></description><link>https://joplinapp.org/news/20210705-094247/</link><guid isPermaLink="false">20210705-094247</guid><pubDate>Mon, 05 Jul 2021 09:42:47 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Poll: What's the size of your note collection?]]></title><description><![CDATA[<p>Poll is on the forum:</p>
|
||||
<p><a href="https://discourse.joplinapp.org/t/poll-whats-the-size-of-your-note-collection/18191">https://discourse.joplinapp.org/t/poll-whats-the-size-of-your-note-collection/18191</a></p>
|
||||
]]></description><link>https://joplinapp.org/news/20210624-171844/</link><guid isPermaLink="false">20210624-171844</guid><pubDate>Thu, 24 Jun 2021 17:18:44 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
|
||||
@@ -1,7 +1,5 @@
|
||||
<!-- Monthly/Yearly plan A/B testing -->
|
||||
<!--
|
||||
<script src="https://www.googleoptimize.com/optimize.js?id=OPT-PW3ZPK3"></script>
|
||||
-->
|
||||
|
||||
<!-- Donate button A/B testing -->
|
||||
<!--
|
||||
|
||||
@@ -116,21 +116,16 @@
|
||||
});
|
||||
};
|
||||
|
||||
const applyPeriod = (period) => {
|
||||
subscriptionPeriod = period;
|
||||
$('.plan-group').removeClass(period === 'monthly' ? 'plan-prices-yearly' : 'plan-prices-monthly');
|
||||
$('.plan-group').addClass('plan-prices-' + period);
|
||||
$("#pay-" + period + '-radio').prop('checked', true);
|
||||
}
|
||||
|
||||
$(() => {
|
||||
$("input[name='pay-radio']").change(function() {
|
||||
const period = $("input[type='radio'][name='pay-radio']:checked").val();
|
||||
applyPeriod(period);
|
||||
subscriptionPeriod = period;
|
||||
|
||||
$('.plan-group').removeClass(period === 'monthly' ? 'plan-prices-yearly' : 'plan-prices-monthly');
|
||||
$('.plan-group').addClass('plan-prices-' + period);
|
||||
});
|
||||
|
||||
setupBetaHandling(urlQuery);
|
||||
applyPeriod('yearly');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
@@ -45,11 +45,11 @@ Building the apps is relatively easy - please [see the build instructions](https
|
||||
|
||||
## Coding style
|
||||
|
||||
Please see [readme/coding_style.md](readme/coding_style.md).
|
||||
Coding style is enforced by a pre-commit hook that runs eslint. This hook is installed whenever running `yarn install` on any of the application directory. If for some reason the pre-commit hook didn't get installed, you can manually install it by running `yarn install` at the root of the repository.
|
||||
|
||||
## GUI style
|
||||
For new React components, please use [React Hooks](https://reactjs.org/docs/hooks-intro.html). For new code in general, please use TypeScript. Even if you are modifying a file that was originally in JavaScript you should ideally convert it first to TypeScript before modifying it. Doing so is relatively easy and it helps maintain code quality.
|
||||
|
||||
For changes made to the Desktop and mobile clients that affect the user interface, refer to `packages/lib/theme.ts` for all styling information. The goal is to create a consistent user interface to allow for easy navigation of Joplin's various features and improve the overall user experience.
|
||||
For changes made to the Desktop client that affect the user interface, refer to `packages/app-desktop/theme.ts` for all styling information. The goal is to create a consistent user interface to allow for easy navigation of Joplin's various features and improve the overall user experience.
|
||||
|
||||
## Automated tests
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -4,10 +4,6 @@
|
||||
|
||||
* * *
|
||||
|
||||
Joplin will have [its first Meetup on 30 August 2022](https://discourse.joplinapp.org/t/joplin-first-meetup-on-30-august/26808)! Come and join us at the Old Thameside Inn next to London Bridge!
|
||||
|
||||
* * *
|
||||
|
||||
🌞 Joplin participates in **Google Summer of Code 2022**! More info on [the announcement post](https://github.com/laurent22/joplin/blob/dev/readme/news/20220308-gsoc2022-start.md). 🌞
|
||||
|
||||
* * *
|
||||
@@ -87,9 +83,8 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1439535?s=96&v=4"/></br>[fbloise](https://github.com/fbloise) | <img width="50" src="https://avatars2.githubusercontent.com/u/49439044?s=96&v=4"/></br>[fourstepper](https://github.com/fourstepper) | <img width="50" src="https://avatars2.githubusercontent.com/u/38898566?s=96&v=4"/></br>[h4sh5](https://github.com/h4sh5) | <img width="50" src="https://avatars2.githubusercontent.com/u/3266447?s=96&v=4"/></br>[iamwillbar](https://github.com/iamwillbar) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/37297218?s=96&v=4"/></br>[Jesssullivan](https://github.com/Jesssullivan) | <img width="50" src="https://avatars2.githubusercontent.com/u/1248504?s=96&v=4"/></br>[joesfer](https://github.com/joesfer) | <img width="50" src="https://avatars2.githubusercontent.com/u/5588131?s=96&v=4"/></br>[kianenigma](https://github.com/kianenigma) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/29300939?s=96&v=4"/></br>[mcejp](https://github.com/mcejp) | <img width="50" src="https://avatars2.githubusercontent.com/u/1168659?s=96&v=4"/></br>[nicholashead](https://github.com/nicholashead) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/5782817?s=96&v=4"/></br>[piccobit](https://github.com/piccobit) | <img width="50" src="https://avatars2.githubusercontent.com/u/77214738?s=96&v=4"/></br>[Polymathic-Company](https://github.com/Polymathic-Company) | <img width="50" src="https://avatars2.githubusercontent.com/u/47742?s=96&v=4"/></br>[ravenscroftj](https://github.com/ravenscroftj) | <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/54626606?s=96&v=4"/></br>[skyrunner15](https://github.com/skyrunner15) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) | <img width="50" src="https://avatars2.githubusercontent.com/u/73081837?s=96&v=4"/></br>[thismarty](https://github.com/thismarty) | <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[thomasbroussard](https://github.com/thomasbroussard) |
|
||||
| | | | |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/5782817?s=96&v=4"/></br>[piccobit](https://github.com/piccobit) | <img width="50" src="https://avatars2.githubusercontent.com/u/77214738?s=96&v=4"/></br>[Polymathic-Company](https://github.com/Polymathic-Company) | <img width="50" src="https://avatars2.githubusercontent.com/u/47742?s=96&v=4"/></br>[ravenscroftj](https://github.com/ravenscroftj) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/73081837?s=96&v=4"/></br>[thismarty](https://github.com/thismarty) | <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[thomasbroussard](https://github.com/thomasbroussard) | | |
|
||||
<!-- SPONSORS-GITHUB -->
|
||||
|
||||
<!-- TOC -->
|
||||
@@ -346,8 +341,8 @@ If you provide a configuration and you receive "success!" on the "check config"
|
||||
- Force Path Style: unchecked
|
||||
|
||||
### Linode
|
||||
- URL: https://regionName.linodeobjects.com (regionName is the region on the URL provided by Linode; this URL is also the same as the URL provided by Linode with the bucket name removed)
|
||||
- Region: Anything you want to type, can't be left empty
|
||||
- URL: https://<region>.linodeobjects.com
|
||||
- Region: empty
|
||||
- Force Path Style: unchecked
|
||||
|
||||
### UpCloud
|
||||
|
||||
@@ -325,7 +325,6 @@
|
||||
"homenote",
|
||||
"hotfolder",
|
||||
"Howver",
|
||||
"hpagent",
|
||||
"Hrvatska",
|
||||
"htmlentities",
|
||||
"htmlfile",
|
||||
@@ -951,4 +950,4 @@
|
||||
"မြန်မာ",
|
||||
"កម្ពុជា"
|
||||
]
|
||||
}
|
||||
}
|
||||
27
package.json
27
package.json
@@ -12,8 +12,8 @@
|
||||
"node": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 --topological run build && yarn run tsc",
|
||||
"buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
|
||||
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 run build && yarn run tsc",
|
||||
"buildSequential": "yarn workspaces foreach --verbose --interlaced run build && yarn run tsc",
|
||||
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
|
||||
"buildCommandIndex": "gulp buildCommandIndex",
|
||||
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../joplin-website/docs/api/references/plugin_api packages/lib/services/plugins/api/",
|
||||
@@ -31,7 +31,6 @@
|
||||
"linter-ci": "eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter-precommit": "eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter": "eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter-interactive": "eslint-interactive --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"postinstall": "gulp build",
|
||||
"publishAll": "git pull && yarn run buildParallel && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
|
||||
"releaseAndroid": "PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" node packages/tools/release-android.js",
|
||||
@@ -62,14 +61,13 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@seiyab/eslint-plugin-react-hooks": "^4.5.1-alpha.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.6.0",
|
||||
"@typescript-eslint/parser": "^4.6.0",
|
||||
"cspell": "^5.20.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-interactive": "^10.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-react": "^7.18.0",
|
||||
"eslint-plugin-react-hooks": "^2.4.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"glob": "^7.1.6",
|
||||
"gulp": "^4.0.2",
|
||||
@@ -78,17 +76,12 @@
|
||||
"lint-staged": "^9.2.1",
|
||||
"madge": "^4.0.2",
|
||||
"typedoc": "^0.17.8",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"http-server": "^0.12.3",
|
||||
"node-gyp": "^8.4.1",
|
||||
"nodemon": "^2.0.9"
|
||||
},
|
||||
"packageManager": "yarn@3.1.1",
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-dom": "17.0.14"
|
||||
}
|
||||
"packageManager": "yarn@3.1.1"
|
||||
}
|
||||
|
||||
@@ -375,11 +375,6 @@ class AppGui {
|
||||
this.showNoteMetadata(!this.widget('noteMetadata').shown);
|
||||
}
|
||||
|
||||
toggleFolderIds() {
|
||||
this.widget('folderList').toggleShowIds();
|
||||
this.widget('noteList').toggleShowIds();
|
||||
}
|
||||
|
||||
widget(name) {
|
||||
if (name === 'root') return this.rootWidget_;
|
||||
return this.rootWidget_.childByName(name);
|
||||
@@ -417,7 +412,7 @@ class AppGui {
|
||||
const widget = this.widget('mainWindow').focusedWidget;
|
||||
if (!widget) return null;
|
||||
|
||||
if (widget.name === 'noteList' || widget.name === 'folderList') {
|
||||
if (widget.name == 'noteList' || widget.name == 'folderList') {
|
||||
return widget.currentItem;
|
||||
}
|
||||
|
||||
@@ -503,8 +498,6 @@ class AppGui {
|
||||
}
|
||||
} else if (cmd === 'toggle_metadata') {
|
||||
this.toggleNoteMetadata();
|
||||
} else if (cmd === 'toggle_ids') {
|
||||
this.toggleFolderIds();
|
||||
} else if (cmd === 'enter_command_line_mode') {
|
||||
const cmd = await this.widget('statusBar').prompt();
|
||||
if (!cmd) return;
|
||||
@@ -528,11 +521,11 @@ class AppGui {
|
||||
const args = splitCommandString(cmd);
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '$n') {
|
||||
if (args[i] == '$n') {
|
||||
args[i] = note ? note.id : '';
|
||||
} else if (args[i] === '$b') {
|
||||
} else if (args[i] == '$b') {
|
||||
args[i] = folder ? folder.id : '';
|
||||
} else if (args[i] === '$c') {
|
||||
} else if (args[i] == '$c') {
|
||||
const item = this.activeListItem();
|
||||
args[i] = item ? item.id : '';
|
||||
}
|
||||
|
||||
@@ -81,21 +81,21 @@ class Application extends BaseApplication {
|
||||
|
||||
pattern = pattern ? pattern.toString() : '';
|
||||
|
||||
if (type === BaseModel.TYPE_FOLDER && (pattern === Folder.conflictFolderTitle() || pattern === Folder.conflictFolderId())) return [Folder.conflictFolder()];
|
||||
if (type == BaseModel.TYPE_FOLDER && (pattern == Folder.conflictFolderTitle() || pattern == Folder.conflictFolderId())) return [Folder.conflictFolder()];
|
||||
|
||||
if (!options) options = {};
|
||||
|
||||
const parent = options.parent ? options.parent : app().currentFolder();
|
||||
const ItemClass = BaseItem.itemClass(type);
|
||||
|
||||
if (type === BaseModel.TYPE_NOTE && pattern.indexOf('*') >= 0) {
|
||||
if (type == BaseModel.TYPE_NOTE && pattern.indexOf('*') >= 0) {
|
||||
// Handle it as pattern
|
||||
if (!parent) throw new Error(_('No notebook selected.'));
|
||||
return await Note.previews(parent.id, { titlePattern: pattern });
|
||||
} else {
|
||||
// Single item
|
||||
let item = null;
|
||||
if (type === BaseModel.TYPE_NOTE) {
|
||||
if (type == BaseModel.TYPE_NOTE) {
|
||||
if (!parent) throw new Error(_('No notebook has been specified.'));
|
||||
item = await ItemClass.loadFolderNoteByField(parent.id, 'title', pattern);
|
||||
} else {
|
||||
@@ -137,7 +137,7 @@ class Application extends BaseApplication {
|
||||
if (!options.booleanAnswerDefault) options.booleanAnswerDefault = 'y';
|
||||
if (!options.answers) options.answers = options.booleanAnswerDefault === 'y' ? [_('Y'), _('n')] : [_('N'), _('y')];
|
||||
|
||||
if (options.type === 'boolean') {
|
||||
if (options.type == 'boolean') {
|
||||
message += ` (${options.answers.join('/')})`;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ class Application extends BaseApplication {
|
||||
if (options.type === 'boolean') {
|
||||
if (answer === null) return false; // Pressed ESCAPE
|
||||
if (!answer) answer = options.answers[0];
|
||||
const positiveIndex = options.booleanAnswerDefault === 'y' ? 0 : 1;
|
||||
const positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
|
||||
return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase();
|
||||
} else {
|
||||
return answer;
|
||||
@@ -181,7 +181,7 @@ class Application extends BaseApplication {
|
||||
fs.readdirSync(__dirname).forEach(path => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path);
|
||||
if (ext !== 'js') return;
|
||||
if (ext != 'js') return;
|
||||
|
||||
const CommandClass = require(`./${path}`);
|
||||
let cmd = new CommandClass();
|
||||
@@ -332,7 +332,6 @@ class Application extends BaseApplication {
|
||||
{ keys: [' '], command: 'todo toggle $n' },
|
||||
{ keys: ['tc'], type: 'function', command: 'toggle_console' },
|
||||
{ keys: ['tm'], type: 'function', command: 'toggle_metadata' },
|
||||
{ keys: ['ti'], type: 'function', command: 'toggle_ids' },
|
||||
{ keys: ['/'], type: 'prompt', command: 'search ""', cursorPosition: -2 },
|
||||
{ keys: ['mn'], type: 'prompt', command: 'mknote ""', cursorPosition: -2 },
|
||||
{ keys: ['mt'], type: 'prompt', command: 'mktodo ""', cursorPosition: -2 },
|
||||
|
||||
@@ -12,7 +12,7 @@ async function handleAutocompletionPromise(line) {
|
||||
const words = getArguments(line);
|
||||
// If there is only one word and it is not already a command name then you
|
||||
// should look for commands it could be
|
||||
if (words.length === 1) {
|
||||
if (words.length == 1) {
|
||||
if (names.indexOf(words[0]) === -1) {
|
||||
const x = names.filter(n => n.indexOf(words[0]) === 0);
|
||||
if (x.length === 1) {
|
||||
@@ -78,38 +78,38 @@ async function handleAutocompletionPromise(line) {
|
||||
|
||||
const currentFolder = app().currentFolder();
|
||||
|
||||
if (argName === 'note' || argName === 'note-pattern') {
|
||||
if (argName == 'note' || argName == 'note-pattern') {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: `${next}*` }) : [];
|
||||
l.push(...notes.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName === 'notebook') {
|
||||
if (argName == 'notebook') {
|
||||
const folders = await Folder.search({ titlePattern: `${next}*` });
|
||||
l.push(...folders.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName === 'item') {
|
||||
if (argName == 'item') {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: `${next}*` }) : [];
|
||||
const folders = await Folder.search({ titlePattern: `${next}*` });
|
||||
l.push(...notes.map(n => n.title), folders.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName === 'tag') {
|
||||
if (argName == 'tag') {
|
||||
const tags = await Tag.search({ titlePattern: `${next}*` });
|
||||
l.push(...tags.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName === 'file') {
|
||||
if (argName == 'file') {
|
||||
const files = await fs.readdir('.');
|
||||
l.push(...files);
|
||||
}
|
||||
|
||||
if (argName === 'tag-command') {
|
||||
if (argName == 'tag-command') {
|
||||
const c = filterList(['add', 'remove', 'list', 'notetags'], next);
|
||||
l.push(...c);
|
||||
}
|
||||
|
||||
if (argName === 'todo-command') {
|
||||
if (argName == 'todo-command') {
|
||||
const c = filterList(['toggle', 'clear'], next);
|
||||
l.push(...c);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ function getCommands() {
|
||||
fs.readdirSync(__dirname).forEach(path => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path);
|
||||
if (ext !== 'js') return;
|
||||
if (ext != 'js') return;
|
||||
|
||||
const CommandClass = require(`./${path}`);
|
||||
const cmd = new CommandClass();
|
||||
|
||||
@@ -222,7 +222,7 @@ async function main() {
|
||||
|
||||
for (const n in testUnits) {
|
||||
if (!testUnits.hasOwnProperty(n)) continue;
|
||||
if (onlyThisTest && n !== onlyThisTest) continue;
|
||||
if (onlyThisTest && n != onlyThisTest) continue;
|
||||
|
||||
await clearDatabase();
|
||||
const testName = n.substr(4).toLowerCase();
|
||||
|
||||
@@ -21,7 +21,7 @@ cliUtils.printArray = function(logFunction, rows) {
|
||||
for (let j = 0; j < row.length; j++) {
|
||||
const item = row[j];
|
||||
const width = item ? item.toString().length : 0;
|
||||
const align = typeof item === 'number' ? ALIGN_RIGHT : ALIGN_LEFT;
|
||||
const align = typeof item == 'number' ? ALIGN_RIGHT : ALIGN_LEFT;
|
||||
if (!colWidths[j] || colWidths[j] < width) colWidths[j] = width;
|
||||
if (colAligns.length <= j) colAligns[j] = align;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ cliUtils.printArray = function(logFunction, rows) {
|
||||
for (let col = 0; col < colWidths.length; col++) {
|
||||
const item = rows[row][col];
|
||||
const width = colWidths[col];
|
||||
const dir = colAligns[col] === ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT;
|
||||
const dir = colAligns[col] == ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT;
|
||||
line.push(stringPadding(item, width, ' ', dir));
|
||||
}
|
||||
logFunction(line.join(' '));
|
||||
@@ -45,13 +45,13 @@ cliUtils.parseFlags = function(flags) {
|
||||
for (let i = 0; i < flags.length; i++) {
|
||||
let f = flags[i].trim();
|
||||
|
||||
if (f.substr(0, 2) === '--') {
|
||||
if (f.substr(0, 2) == '--') {
|
||||
f = f.split(' ');
|
||||
output.long = f[0].substr(2).trim();
|
||||
if (f.length === 2) {
|
||||
if (f.length == 2) {
|
||||
output.arg = cliUtils.parseCommandArg(f[1].trim());
|
||||
}
|
||||
} else if (f.substr(0, 1) === '-') {
|
||||
} else if (f.substr(0, 1) == '-') {
|
||||
output.short = f.substr(1);
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,9 @@ cliUtils.parseCommandArg = function(arg) {
|
||||
const c2 = arg[arg.length - 1];
|
||||
const name = arg.substr(1, arg.length - 2);
|
||||
|
||||
if (c1 === '<' && c2 === '>') {
|
||||
if (c1 == '<' && c2 == '>') {
|
||||
return { required: true, name: name };
|
||||
} else if (c1 === '[' && c2 === ']') {
|
||||
} else if (c1 == '[' && c2 == ']') {
|
||||
return { required: false, name: name };
|
||||
} else {
|
||||
throw new Error(`Invalid command arg: ${arg}`);
|
||||
@@ -83,7 +83,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
const booleanFlags = [];
|
||||
const aliases = {};
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].length !== 2) throw new Error(`Invalid options: ${options[i]}`);
|
||||
if (options[i].length != 2) throw new Error(`Invalid options: ${options[i]}`);
|
||||
let flags = options[i][0];
|
||||
|
||||
flags = cliUtils.parseFlags(flags);
|
||||
@@ -117,7 +117,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
const argOptions = {};
|
||||
for (const key in args) {
|
||||
if (!args.hasOwnProperty(key)) continue;
|
||||
if (key === '_') continue;
|
||||
if (key == '_') continue;
|
||||
argOptions[key] = args[key];
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ cliUtils.promptConfirm = function(message, answers = null) {
|
||||
|
||||
return new Promise((resolve) => {
|
||||
rl.question(`${message} `, answer => {
|
||||
const ok = !answer || answer.toLowerCase() === answers[0].toLowerCase();
|
||||
const ok = !answer || answer.toLowerCase() == answers[0].toLowerCase();
|
||||
rl.close();
|
||||
resolve(ok);
|
||||
});
|
||||
|
||||
@@ -122,7 +122,7 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
|
||||
if (args.name === 'locale') {
|
||||
if (args.name == 'locale') {
|
||||
setLocale(Setting.value('locale'));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class Command extends BaseCommand {
|
||||
queryOptions.orderBy = options.sort;
|
||||
queryOptions.orderByDir = 'ASC';
|
||||
}
|
||||
if (options.reverse === true) queryOptions.orderByDir = queryOptions.orderByDir === 'ASC' ? 'DESC' : 'ASC';
|
||||
if (options.reverse === true) queryOptions.orderByDir = queryOptions.orderByDir == 'ASC' ? 'DESC' : 'ASC';
|
||||
queryOptions.caseInsensitive = true;
|
||||
if (options.type) {
|
||||
queryOptions.itemTypes = [];
|
||||
@@ -55,7 +55,7 @@ class Command extends BaseCommand {
|
||||
queryOptions.uncompletedTodosOnTop = Setting.value('uncompletedTodosOnTop');
|
||||
|
||||
let modelType = null;
|
||||
if (pattern === '/' || !app().currentFolder()) {
|
||||
if (pattern == '/' || !app().currentFolder()) {
|
||||
queryOptions.includeConflictFolder = true;
|
||||
items = await Folder.all(queryOptions);
|
||||
modelType = Folder.modelType();
|
||||
@@ -65,7 +65,7 @@ class Command extends BaseCommand {
|
||||
modelType = Note.modelType();
|
||||
}
|
||||
|
||||
if (options.format && options.format === 'json') {
|
||||
if (options.format && options.format == 'json') {
|
||||
this.stdout(JSON.stringify(items));
|
||||
} else {
|
||||
let hasTodos = false;
|
||||
@@ -88,7 +88,7 @@ class Command extends BaseCommand {
|
||||
row.push(BaseModel.shortId(item.id));
|
||||
shortIdShown = true;
|
||||
|
||||
if (modelType === Folder.modelType()) {
|
||||
if (modelType == Folder.modelType()) {
|
||||
row.push(await Folder.noteCount(item.id));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,45 +7,25 @@ const Note = require('@joplin/lib/models/Note').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
return 'mv <item> [notebook]';
|
||||
return 'mv <note> [notebook]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _('Moves the given <item> (notes matching pattern in current notebook or one notebook) to [notebook]. If <item> is subnotebook and [notebook] is "root", will make <item> parent notebook');
|
||||
return _('Moves the notes matching <note> to [notebook].');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const pattern = args['item'];
|
||||
const pattern = args['note'];
|
||||
const destination = args['notebook'];
|
||||
let folder = null;
|
||||
|
||||
if (destination !== 'root') {
|
||||
folder = await app().loadItem(BaseModel.TYPE_FOLDER, destination);
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', destination));
|
||||
}
|
||||
const folder = await Folder.loadByField('title', destination);
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', destination));
|
||||
|
||||
const destinationDuplicates = await Folder.search({ titlePattern: destination, limit: 2 });
|
||||
if (destinationDuplicates.length > 1) {
|
||||
throw new Error(_('Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id' , destination));
|
||||
}
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', pattern));
|
||||
|
||||
const itemFolder = await app().loadItem(BaseModel.TYPE_FOLDER, pattern);
|
||||
if (itemFolder) {
|
||||
const sourceDuplicates = await Folder.search({ titlePattern: pattern, limit: 2 });
|
||||
if (sourceDuplicates.length > 1) {
|
||||
throw new Error(_('Ambiguous notebook "%s". Please use notebook id instead - press "ti" to see the short notebook id or use $b for current selected notebook', pattern));
|
||||
}
|
||||
if (destination === 'root') {
|
||||
await Folder.moveToFolder(itemFolder.id, '');
|
||||
} else {
|
||||
await Folder.moveToFolder(itemFolder.id, folder.id);
|
||||
}
|
||||
} else {
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
|
||||
if (notes.length === 0) throw new Error(_('Cannot find "%s".', pattern));
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await Note.moveToFolder(notes[i].id, folder.id);
|
||||
}
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await Note.moveToFolder(notes[i].id, folder.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ class Command extends BaseCommand {
|
||||
|
||||
this.releaseLockFn_ = await Command.lockFile(lockFilePath);
|
||||
} catch (error) {
|
||||
if (error.code === 'ELOCKED') {
|
||||
if (error.code == 'ELOCKED') {
|
||||
const msg = _('Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at "%s" and resume the operation.', error.file);
|
||||
this.stdout(msg);
|
||||
return;
|
||||
@@ -222,7 +222,7 @@ class Command extends BaseCommand {
|
||||
const newContext = await sync.start(options);
|
||||
Setting.setValue(contextKey, JSON.stringify(newContext));
|
||||
} catch (error) {
|
||||
if (error.code === 'alreadyStarted') {
|
||||
if (error.code == 'alreadyStarted') {
|
||||
this.stdout(error.message);
|
||||
} else {
|
||||
throw error;
|
||||
|
||||
@@ -30,21 +30,21 @@ class Command extends BaseCommand {
|
||||
|
||||
const command = args['tag-command'];
|
||||
|
||||
if (command === 'remove' && !tag) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
if (command == 'remove' && !tag) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
|
||||
if (command === 'add') {
|
||||
if (command == 'add') {
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
||||
if (!tag) tag = await Tag.save({ title: args.tag }, { userSideValidation: true });
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await Tag.addNote(tag.id, notes[i].id);
|
||||
}
|
||||
} else if (command === 'remove') {
|
||||
} else if (command == 'remove') {
|
||||
if (!tag) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await Tag.removeNote(tag.id, notes[i].id);
|
||||
}
|
||||
} else if (command === 'list') {
|
||||
} else if (command == 'list') {
|
||||
if (tag) {
|
||||
const notes = await Tag.notes(tag.id);
|
||||
notes.map(note => {
|
||||
@@ -75,7 +75,7 @@ class Command extends BaseCommand {
|
||||
this.stdout(tag.title);
|
||||
});
|
||||
}
|
||||
} else if (command === 'notetags') {
|
||||
} else if (command == 'notetags') {
|
||||
if (args.tag) {
|
||||
const note = await app().loadItem(BaseModel.TYPE_NOTE, args.tag);
|
||||
if (!note) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
|
||||
@@ -29,13 +29,13 @@ class Command extends BaseCommand {
|
||||
id: note.id,
|
||||
};
|
||||
|
||||
if (action === 'toggle') {
|
||||
if (action == 'toggle') {
|
||||
if (!note.is_todo) {
|
||||
toSave = Note.toggleIsTodo(note);
|
||||
} else {
|
||||
toSave.todo_completed = note.todo_completed ? 0 : time.unixMs();
|
||||
}
|
||||
} else if (action === 'clear') {
|
||||
} else if (action == 'clear') {
|
||||
toSave.is_todo = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2071,7 +2071,7 @@ function execCommand(client, command, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const childProcess = exec(cmd, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
if (error.signal === 'SIGTERM') {
|
||||
if (error.signal == 'SIGTERM') {
|
||||
resolve('Process was killed');
|
||||
} else {
|
||||
logger.error(stderr);
|
||||
@@ -2103,7 +2103,7 @@ async function clientItems(client) {
|
||||
function randomTag(items) {
|
||||
const tags = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].type_ !== 5) continue;
|
||||
if (items[i].type_ != 5) continue;
|
||||
tags.push(items[i]);
|
||||
}
|
||||
|
||||
@@ -2113,7 +2113,7 @@ function randomTag(items) {
|
||||
function randomNote(items) {
|
||||
const notes = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].type_ !== 1) continue;
|
||||
if (items[i].type_ != 1) continue;
|
||||
notes.push(items[i]);
|
||||
}
|
||||
|
||||
@@ -2131,11 +2131,11 @@ async function execRandomCommand(client) {
|
||||
const item = randomElement(items);
|
||||
if (!item) return;
|
||||
|
||||
if (item.type_ === 1) {
|
||||
if (item.type_ == 1) {
|
||||
return execCommand(client, `rm -f ${item.id}`);
|
||||
} else if (item.type_ === 2) {
|
||||
} else if (item.type_ == 2) {
|
||||
return execCommand(client, `rm -r -f ${item.id}`);
|
||||
} else if (item.type_ === 5) {
|
||||
} else if (item.type_ == 5) {
|
||||
// tag
|
||||
} else {
|
||||
throw new Error(`Unknown type: ${item.type_}`);
|
||||
@@ -2213,7 +2213,7 @@ function randomNextCheckTime() {
|
||||
|
||||
function findItem(items, itemId) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].id === itemId) return items[i];
|
||||
if (items[i].id == itemId) return items[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -2225,7 +2225,7 @@ function compareItems(item1, item2) {
|
||||
const p1 = item1[n];
|
||||
const p2 = item2[n];
|
||||
|
||||
if (n === 'notes_') {
|
||||
if (n == 'notes_') {
|
||||
p1.sort();
|
||||
p2.sort();
|
||||
if (JSON.stringify(p1) !== JSON.stringify(p2)) {
|
||||
@@ -2246,7 +2246,7 @@ function findMissingItems_(items1, items2) {
|
||||
let found = false;
|
||||
for (let j = 0; j < items2.length; j++) {
|
||||
const item2 = items2[j];
|
||||
if (item1.id === item2.id) {
|
||||
if (item1.id == item2.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -2340,9 +2340,9 @@ async function main() {
|
||||
let state = 'commands';
|
||||
|
||||
setInterval(async () => {
|
||||
if (state === 'waitForSyncCheck') return;
|
||||
if (state == 'waitForSyncCheck') return;
|
||||
|
||||
if (state === 'syncCheck') {
|
||||
if (state == 'syncCheck') {
|
||||
state = 'waitForSyncCheck';
|
||||
const clientItems = [];
|
||||
// Up to 3 sync operations must be performed by each clients in order for them
|
||||
@@ -2371,7 +2371,7 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === 'waitForClients') {
|
||||
if (state == 'waitForClients') {
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
if (clients[i].activeCommandCount > 0) return;
|
||||
}
|
||||
@@ -2380,7 +2380,7 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === 'commands') {
|
||||
if (state == 'commands') {
|
||||
if (nextSyncCheckTime <= time.unixMs()) {
|
||||
state = 'waitForClients';
|
||||
return;
|
||||
|
||||
@@ -2,7 +2,6 @@ const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const Tag = require('@joplin/lib/models/Tag').default;
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const _ = require('@joplin/lib/locale')._;
|
||||
|
||||
class FolderListWidget extends ListWidget {
|
||||
@@ -19,32 +18,13 @@ class FolderListWidget extends ListWidget {
|
||||
this.updateIndexFromSelectedFolderId_ = false;
|
||||
this.updateItems_ = false;
|
||||
this.trimItemTitle = false;
|
||||
this.showIds = false;
|
||||
|
||||
this.itemRenderer = item => {
|
||||
const output = [];
|
||||
if (item === '-') {
|
||||
output.push('-'.repeat(this.innerWidth));
|
||||
} else if (item.type_ === Folder.modelType()) {
|
||||
output.push(' '.repeat(this.folderDepth(this.folders, item.id)));
|
||||
|
||||
if (this.showIds) {
|
||||
output.push(Folder.shortId(item.id));
|
||||
}
|
||||
output.push(Folder.displayTitle(item));
|
||||
|
||||
if (Setting.value('showNoteCounts')) {
|
||||
let noteCount = item.note_count;
|
||||
// Subtract children note_count from parent folder.
|
||||
if (this.folderHasChildren_(this.folders,item.id)) {
|
||||
for (let i = 0; i < this.folders.length; i++) {
|
||||
if (this.folders[i].parent_id === item.id) {
|
||||
noteCount -= this.folders[i].note_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
output.push(noteCount);
|
||||
}
|
||||
output.push(' '.repeat(this.folderDepth(this.folders, item.id)) + Folder.displayTitle(item));
|
||||
} else if (item.type_ === Tag.modelType()) {
|
||||
output.push(`[${Folder.displayTitle(item)}]`);
|
||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||
@@ -139,11 +119,6 @@ class FolderListWidget extends ListWidget {
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
toggleShowIds() {
|
||||
this.showIds = !this.showIds;
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
folderHasChildren_(folders, folderId) {
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
const folder = folders[i];
|
||||
|
||||
@@ -5,15 +5,11 @@ class NoteListWidget extends ListWidget {
|
||||
constructor() {
|
||||
super();
|
||||
this.selectedNoteId_ = 0;
|
||||
this.showIds = false;
|
||||
|
||||
this.updateIndexFromSelectedNoteId_ = false;
|
||||
|
||||
this.itemRenderer = note => {
|
||||
let label = Note.displayTitle(note);
|
||||
if (this.showIds) {
|
||||
label = `${Note.shortId(note.id)} ${Note.displayTitle(note)}`;
|
||||
}
|
||||
let label = Note.displayTitle(note); // + ' ' + note.id;
|
||||
if (note.is_todo) {
|
||||
label = `[${note.todo_completed ? 'X' : ' '}] ${label}`;
|
||||
}
|
||||
@@ -26,11 +22,6 @@ class NoteListWidget extends ListWidget {
|
||||
this.selectedNoteId_ = v;
|
||||
}
|
||||
|
||||
toggleShowIds() {
|
||||
this.showIds = !this.showIds;
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.updateIndexFromSelectedNoteId_) {
|
||||
const index = this.itemIndexByKey('id', this.selectedNoteId_);
|
||||
|
||||
@@ -26,7 +26,7 @@ const sharp = require('sharp');
|
||||
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local');
|
||||
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local.js');
|
||||
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
|
||||
const envFromArgs = require('@joplin/lib/envFromArgs');
|
||||
const nodeSqlite = require('sqlite3');
|
||||
@@ -82,13 +82,13 @@ if (process.platform === 'win32') {
|
||||
|
||||
process.stdout.on('error', function(err) {
|
||||
// https://stackoverflow.com/questions/12329816/error-write-epipe-when-piping-node-output-to-head#15884508
|
||||
if (err.code === 'EPIPE') {
|
||||
if (err.code == 'EPIPE') {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
application.start(process.argv).catch(error => {
|
||||
if (error.code === 'flagError') {
|
||||
if (error.code == 'flagError') {
|
||||
console.error(error.message);
|
||||
console.error(_('Type `joplin help` for usage information.'));
|
||||
} else {
|
||||
|
||||
@@ -33,14 +33,14 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "2.9.0",
|
||||
"version": "2.8.1",
|
||||
"bin": "./main.js",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~2.9",
|
||||
"@joplin/renderer": "~2.9",
|
||||
"@joplin/lib": "~2.8",
|
||||
"@joplin/renderer": "~2.8",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"chalk": "^4.1.0",
|
||||
"compare-version": "^0.1.2",
|
||||
@@ -67,7 +67,7 @@
|
||||
"yargs-parser": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.9",
|
||||
"@joplin/tools": "~2.8",
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^14.14.6",
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('feature_NoteHistory', function() {
|
||||
});
|
||||
|
||||
afterEach(async (done) => {
|
||||
if (testApp) await testApp.destroy();
|
||||
if (testApp !== null) await testApp.destroy();
|
||||
testApp = null;
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
import { installDefaultPlugins, getDefaultPluginsInstallState, setSettingsForDefaultPlugins, checkPreInstalledDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
||||
import PluginRunner from '../../../app/services/plugins/PluginRunner';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import { setupDatabaseAndSynchronizer, supportDir, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import PluginService, { defaultPluginSetting, DefaultPluginsInfo, PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
const testPluginDir = `${supportDir}/plugins`;
|
||||
|
||||
function newPluginService(appVersion: string = '1.4') {
|
||||
const runner = new PluginRunner();
|
||||
const service = new PluginService();
|
||||
service.initialize(
|
||||
appVersion,
|
||||
{
|
||||
joplin: {},
|
||||
},
|
||||
runner,
|
||||
{
|
||||
dispatch: () => {},
|
||||
getState: () => {},
|
||||
}
|
||||
);
|
||||
return service;
|
||||
}
|
||||
|
||||
describe('defaultPluginsUtils', function() {
|
||||
|
||||
const pluginsId = ['joplin.plugin.ambrt.backlinksToNote', 'org.joplinapp.plugins.ToggleSidebars'];
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should install default plugins with no previous default plugins installed', (async () => {
|
||||
const testPluginDir = `${supportDir}/pluginRepo/plugins`;
|
||||
Setting.setValue('installedDefaultPlugins', []);
|
||||
|
||||
const service = newPluginService('2.1');
|
||||
|
||||
const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states'));
|
||||
|
||||
const newPluginsSettings = await installDefaultPlugins(service, testPluginDir, pluginsId, pluginSettings);
|
||||
|
||||
const installedPluginPath1 = `${Setting.value('pluginDir')}/${pluginsId[0]}.jpl`;
|
||||
const installedPluginPath2 = `${Setting.value('pluginDir')}/${pluginsId[1]}.jpl`;
|
||||
|
||||
expect(await pathExists(installedPluginPath1)).toBe(true);
|
||||
expect(await pathExists(installedPluginPath2)).toBe(true);
|
||||
|
||||
expect(newPluginsSettings[pluginsId[0]]).toMatchObject(defaultPluginSetting());
|
||||
expect(newPluginsSettings[pluginsId[1]]).toMatchObject(defaultPluginSetting());
|
||||
|
||||
}));
|
||||
|
||||
it('should install default plugins with previous default plugins installed', (async () => {
|
||||
|
||||
const testPluginDir = `${supportDir}/pluginRepo/plugins`;
|
||||
Setting.setValue('installedDefaultPlugins', ['org.joplinapp.plugins.ToggleSidebars']);
|
||||
|
||||
const service = newPluginService('2.1');
|
||||
|
||||
const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states'));
|
||||
|
||||
const newPluginsSettings = await installDefaultPlugins(service, testPluginDir, pluginsId, pluginSettings);
|
||||
|
||||
const installedPluginPath1 = `${Setting.value('pluginDir')}/${pluginsId[0]}.jpl`;
|
||||
const installedPluginPath2 = `${Setting.value('pluginDir')}/${pluginsId[1]}.jpl`;
|
||||
|
||||
expect(await pathExists(installedPluginPath1)).toBe(true);
|
||||
expect(await pathExists(installedPluginPath2)).toBe(false);
|
||||
|
||||
expect(newPluginsSettings[pluginsId[0]]).toMatchObject(defaultPluginSetting());
|
||||
expect(newPluginsSettings[pluginsId[1]]).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should get default plugins install state', (async () => {
|
||||
const testCases = [
|
||||
{
|
||||
'installedDefaultPlugins': [''],
|
||||
'loadingPlugins': [`${testPluginDir}/simple`, `${testPluginDir}/jpl_test/org.joplinapp.FirstJplPlugin.jpl`],
|
||||
'plugin1DefaultState': defaultPluginSetting(),
|
||||
'plugin2DefaultState': defaultPluginSetting(),
|
||||
'installedDefaultPlugins1': true,
|
||||
'installedDefaultPlugins2': true,
|
||||
},
|
||||
{
|
||||
'installedDefaultPlugins': [''],
|
||||
'loadingPlugins': [`${testPluginDir}/simple`],
|
||||
'plugin1DefaultState': defaultPluginSetting(),
|
||||
'plugin2DefaultState': undefined,
|
||||
'installedDefaultPlugins1': true,
|
||||
'installedDefaultPlugins2': false,
|
||||
},
|
||||
{
|
||||
'installedDefaultPlugins': ['org.joplinapp.plugins.Simple'],
|
||||
'loadingPlugins': [`${testPluginDir}/simple`, `${testPluginDir}/jpl_test/org.joplinapp.FirstJplPlugin.jpl`],
|
||||
'plugin1DefaultState': undefined,
|
||||
'plugin2DefaultState': defaultPluginSetting(),
|
||||
'installedDefaultPlugins1': true,
|
||||
'installedDefaultPlugins2': true,
|
||||
},
|
||||
{
|
||||
'installedDefaultPlugins': ['org.joplinapp.plugins.Simple'],
|
||||
'loadingPlugins': [`${testPluginDir}/simple`],
|
||||
'plugin1DefaultState': undefined,
|
||||
'plugin2DefaultState': undefined,
|
||||
'installedDefaultPlugins1': true,
|
||||
'installedDefaultPlugins2': false,
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const service = newPluginService();
|
||||
const pluginsId = ['org.joplinapp.plugins.Simple', 'org.joplinapp.FirstJplPlugin'];
|
||||
|
||||
Setting.setValue('installedDefaultPlugins', testCase.installedDefaultPlugins);
|
||||
await service.loadAndRunPlugins(testCase.loadingPlugins, {});
|
||||
|
||||
// setting installedDefaultPlugins state
|
||||
const defaultInstallStates: PluginSettings = getDefaultPluginsInstallState(service, pluginsId);
|
||||
|
||||
expect(defaultInstallStates[pluginsId[0]]).toStrictEqual(testCase.plugin1DefaultState);
|
||||
expect(defaultInstallStates[pluginsId[1]]).toStrictEqual(testCase.plugin2DefaultState);
|
||||
|
||||
|
||||
const installedDefaultPlugins = Setting.value('installedDefaultPlugins');
|
||||
expect(installedDefaultPlugins.includes(pluginsId[0])).toBe(testCase.installedDefaultPlugins1);
|
||||
expect(installedDefaultPlugins.includes(pluginsId[1])).toBe(testCase.installedDefaultPlugins2);
|
||||
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
it('should check pre-installed default plugins', (async () => {
|
||||
// with previous pre-installed default plugins
|
||||
Setting.setValue('installedDefaultPlugins', ['']);
|
||||
let pluginSettings, installedDefaultPlugins;
|
||||
|
||||
pluginSettings = { [pluginsId[0]]: defaultPluginSetting() };
|
||||
checkPreInstalledDefaultPlugins(pluginsId, pluginSettings);
|
||||
|
||||
installedDefaultPlugins = Setting.value('installedDefaultPlugins');
|
||||
expect(installedDefaultPlugins.includes(pluginsId[0])).toBe(true);
|
||||
expect(installedDefaultPlugins.includes(pluginsId[1])).toBe(false);
|
||||
|
||||
|
||||
// with no previous pre-installed default plugins
|
||||
Setting.setValue('installedDefaultPlugins', ['not-a-default-plugin']);
|
||||
pluginSettings = {};
|
||||
checkPreInstalledDefaultPlugins(pluginsId, pluginSettings);
|
||||
|
||||
installedDefaultPlugins = Setting.value('installedDefaultPlugins');
|
||||
expect(installedDefaultPlugins.includes(pluginsId[0])).toBe(false);
|
||||
expect(installedDefaultPlugins.includes(pluginsId[1])).toBe(false);
|
||||
|
||||
}));
|
||||
|
||||
it('should set initial settings for default plugins', async () => {
|
||||
const service = newPluginService();
|
||||
|
||||
const pluginScript = `
|
||||
/* joplin-manifest:
|
||||
{
|
||||
"id": "io.github.jackgruber.backup",
|
||||
"manifest_version": 1,
|
||||
"app_min_version": "1.4",
|
||||
"name": "JS Bundle test",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
*/
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.settings.registerSettings({
|
||||
path: {
|
||||
value: "initial-path",
|
||||
type: 2,
|
||||
section: "backupSection",
|
||||
public: true,
|
||||
label: "Backup path",
|
||||
},
|
||||
})
|
||||
},
|
||||
});`;
|
||||
|
||||
const plugin = await service.loadPluginFromJsBundle('', pluginScript);
|
||||
await service.runPlugin(plugin);
|
||||
|
||||
const defaultPluginsInfo: DefaultPluginsInfo = {
|
||||
'io.github.jackgruber.backup': {
|
||||
version: '1.0.2',
|
||||
settings: {
|
||||
'path': `${Setting.value('profileDir')}`,
|
||||
},
|
||||
},
|
||||
'plugin.calebjohn.rich-markdown': {
|
||||
version: '0.8.3',
|
||||
},
|
||||
};
|
||||
|
||||
// with pre-installed default plugin
|
||||
Setting.setValue('installedDefaultPlugins', ['io.github.jackgruber.backup']);
|
||||
setSettingsForDefaultPlugins(defaultPluginsInfo);
|
||||
expect(Setting.value('plugin-io.github.jackgruber.backup.path')).toBe('initial-path');
|
||||
await service.destroy();
|
||||
|
||||
// with no pre-installed default plugin
|
||||
Setting.setValue('installedDefaultPlugins', ['']);
|
||||
setSettingsForDefaultPlugins(defaultPluginsInfo);
|
||||
expect(Setting.value('plugin-io.github.jackgruber.backup.path')).toBe(`${Setting.value('profileDir')}`);
|
||||
await service.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
Binary file not shown.
@@ -32,15 +32,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return s
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function pageTitle() {
|
||||
const titleElements = document.getElementsByTagName('title');
|
||||
if (titleElements.length) return titleElements[0].text.trim();
|
||||
@@ -213,16 +204,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeName === 'embed') {
|
||||
const src = absoluteUrl(node.src);
|
||||
node.setAttribute('src', src);
|
||||
}
|
||||
|
||||
if (nodeName === 'object') {
|
||||
const data = absoluteUrl(node.data);
|
||||
node.setAttribute('data', data);
|
||||
}
|
||||
|
||||
cleanUpElement(convertToMarkup, node, imageSizes, imageIndexes);
|
||||
}
|
||||
}
|
||||
@@ -336,9 +317,6 @@
|
||||
}
|
||||
|
||||
function readabilityProcess() {
|
||||
|
||||
if (isPagePdf()) throw new Error('Could not parse PDF document with Readability');
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const readability = new Readability(documentForReadability());
|
||||
const article = readability.parse();
|
||||
@@ -351,14 +329,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
function isPagePdf() {
|
||||
return document.contentType === 'application/pdf';
|
||||
}
|
||||
|
||||
function embedPageUrl() {
|
||||
return `<embed src="${escapeHtml(window.location.href)}" type="${escapeHtml(document.contentType)}" />`;
|
||||
}
|
||||
|
||||
async function prepareCommandResponse(command) {
|
||||
console.info(`Got command: ${command.name}`);
|
||||
const shouldSendToJoplin = !!command.shouldSendToJoplin;
|
||||
@@ -405,10 +375,6 @@
|
||||
|
||||
} else if (command.name === 'completePageHtml') {
|
||||
|
||||
if (isPagePdf()) {
|
||||
return clippedContentResponse(pageTitle(), embedPageUrl(), getImageSizes(document), getAnchorNames(document));
|
||||
}
|
||||
|
||||
hardcodePreStyles(document);
|
||||
addSvgClass(document);
|
||||
preProcessDocument(document);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "2.9.0",
|
||||
"version": "2.8.1",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'",
|
||||
|
||||
@@ -16,8 +16,6 @@ function getAdditionalModulePaths(options = {}) {
|
||||
|
||||
// We need to explicitly check for null and undefined (and not a falsy value) because
|
||||
// TypeScript treats an empty string as `.`.
|
||||
//
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (baseUrl == null) {
|
||||
// If there's no baseUrl set we respect NODE_PATH
|
||||
// Note that NODE_PATH is deprecated and will be removed
|
||||
|
||||
19
packages/app-clipper/popup/package-lock.json
generated
19
packages/app-clipper/popup/package-lock.json
generated
@@ -20253,19 +20253,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "3.9.10",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
|
||||
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||
@@ -38008,12 +37995,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.10",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
|
||||
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
|
||||
"peer": true
|
||||
},
|
||||
"unicode-canonical-property-names-ecmascript": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||
|
||||
@@ -67,7 +67,7 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
return choosePort(HOST, DEFAULT_PORT);
|
||||
})
|
||||
.then(port => {
|
||||
if (!port) {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import sidebarCommands from './gui/Sidebar/commands/index';
|
||||
import appCommands from './commands/index';
|
||||
import libCommands from '@joplin/lib/commands/index';
|
||||
import { homedir } from 'os';
|
||||
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||
const electronContextMenu = require('./services/electron-context-menu');
|
||||
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
|
||||
|
||||
@@ -64,8 +63,6 @@ import checkForUpdates from './checkForUpdates';
|
||||
import { AppState } from './app.reducer';
|
||||
import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog';
|
||||
import eventManager from '@joplin/lib/eventManager';
|
||||
import path = require('path');
|
||||
import { checkPreInstalledDefaultPlugins, installDefaultPlugins, setSettingsForDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
||||
// import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
|
||||
|
||||
const pluginClasses = [
|
||||
@@ -107,22 +104,22 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
protected async generalMiddleware(store: any, next: any, action: any) {
|
||||
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'locale' || action.type === 'SETTING_UPDATE_ALL') {
|
||||
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'locale' || action.type == 'SETTING_UPDATE_ALL') {
|
||||
setLocale(Setting.value('locale'));
|
||||
// The bridge runs within the main process, with its own instance of locale.js
|
||||
// so it needs to be set too here.
|
||||
bridge().setLocale(Setting.value('locale'));
|
||||
}
|
||||
|
||||
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'showTrayIcon' || action.type === 'SETTING_UPDATE_ALL') {
|
||||
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'showTrayIcon' || action.type == 'SETTING_UPDATE_ALL') {
|
||||
this.updateTray();
|
||||
}
|
||||
|
||||
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'style.editor.fontFamily' || action.type === 'SETTING_UPDATE_ALL') {
|
||||
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'style.editor.fontFamily' || action.type == 'SETTING_UPDATE_ALL') {
|
||||
this.updateEditorFont();
|
||||
}
|
||||
|
||||
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'windowContentZoomFactor' || action.type === 'SETTING_UPDATE_ALL') {
|
||||
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'windowContentZoomFactor' || action.type == 'SETTING_UPDATE_ALL') {
|
||||
webFrame.setZoomFactor(Setting.value('windowContentZoomFactor') / 100);
|
||||
}
|
||||
|
||||
@@ -145,7 +142,7 @@ class Application extends BaseApplication {
|
||||
await Folder.expandTree(newState.folders, action.folderId);
|
||||
}
|
||||
|
||||
if (this.hasGui() && ((action.type === 'SETTING_UPDATE_ONE' && ['themeAutoDetect', 'theme', 'preferredLightTheme', 'preferredDarkTheme'].includes(action.key)) || action.type === 'SETTING_UPDATE_ALL')) {
|
||||
if (this.hasGui() && ((action.type == 'SETTING_UPDATE_ONE' && ['themeAutoDetect', 'theme', 'preferredLightTheme', 'preferredDarkTheme'].includes(action.key)) || action.type == 'SETTING_UPDATE_ALL')) {
|
||||
this.handleThemeAutoDetect();
|
||||
}
|
||||
|
||||
@@ -263,9 +260,9 @@ class Application extends BaseApplication {
|
||||
const pluginRunner = new PluginRunner();
|
||||
service.initialize(packageInfo.version, PlatformImplementation.instance(), pluginRunner, this.store());
|
||||
service.isSafeMode = Setting.value('isSafeMode');
|
||||
const defaultPluginsId = Object.keys(getDefaultPluginsInfo());
|
||||
|
||||
let pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states'));
|
||||
const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states'));
|
||||
|
||||
{
|
||||
// Users can add and remove plugins from the config screen at any
|
||||
// time, however we only effectively uninstall the plugin the next
|
||||
@@ -275,11 +272,7 @@ class Application extends BaseApplication {
|
||||
Setting.setValue('plugins.states', newSettings);
|
||||
}
|
||||
|
||||
checkPreInstalledDefaultPlugins(defaultPluginsId, pluginSettings);
|
||||
|
||||
try {
|
||||
const pluginsDir = path.join(bridge().buildDir(), 'defaultPlugins');
|
||||
pluginSettings = await installDefaultPlugins(service, pluginsDir, defaultPluginsId, pluginSettings);
|
||||
if (await shim.fsDriver().exists(Setting.value('pluginDir'))) {
|
||||
await service.loadAndRunPlugins(Setting.value('pluginDir'), pluginSettings);
|
||||
}
|
||||
@@ -327,7 +320,6 @@ class Application extends BaseApplication {
|
||||
type: 'STARTUP_PLUGINS_LOADED',
|
||||
value: true,
|
||||
});
|
||||
setSettingsForDefaultPlugins(getDefaultPluginsInfo());
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
@@ -395,7 +387,7 @@ class Application extends BaseApplication {
|
||||
|
||||
PerFolderSortOrderService.initialize();
|
||||
|
||||
CommandService.instance().initialize(this.store(), Setting.value('env') === 'dev', stateToWhenClauseContext);
|
||||
CommandService.instance().initialize(this.store(), Setting.value('env') == 'dev', stateToWhenClauseContext);
|
||||
|
||||
for (const command of commands) {
|
||||
CommandService.instance().registerDeclaration(command.declaration);
|
||||
|
||||
@@ -86,7 +86,7 @@ async function fetchLatestRelease(options: CheckForUpdateOptions) {
|
||||
const ext = fileExtension(asset.name);
|
||||
if (platform === 'win32' && ext === 'exe') {
|
||||
if (shim.isPortable()) {
|
||||
found = asset.name === 'JoplinPortable.exe';
|
||||
found = asset.name == 'JoplinPortable.exe';
|
||||
} else {
|
||||
found = !!asset.name.match(/^Joplin-Setup-[\d.]+\.exe$/);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ const os = require('os');
|
||||
const sha512 = require('js-sha512');
|
||||
|
||||
const generateChecksumFile = () => {
|
||||
if (os.platform() !== 'linux') {
|
||||
if (os.platform() != 'linux') {
|
||||
return []; // SHA-512 is only for AppImage
|
||||
}
|
||||
const distDirName = 'dist';
|
||||
@@ -18,7 +18,7 @@ const generateChecksumFile = () => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (appImageName === '') {
|
||||
if (appImageName == '') {
|
||||
throw 'AppImage not found!';
|
||||
}
|
||||
const appImagePath = path.join(distPath, appImageName);
|
||||
|
||||
@@ -15,9 +15,6 @@ import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
||||
import ClipperConfigScreen from '../ClipperConfigScreen';
|
||||
import restart from '../../services/restart';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import { getDefaultPluginsInstallState, updateDefaultPluginsInstallState } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
||||
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
|
||||
const settingKeyToControl: any = {
|
||||
@@ -69,7 +66,6 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
this.switchSection(this.props.defaultSection);
|
||||
});
|
||||
}
|
||||
updateDefaultPluginsInstallState(getDefaultPluginsInstallState(PluginService.instance(), Object.keys(getDefaultPluginsInfo())), this);
|
||||
}
|
||||
|
||||
private async handleSettingButton(key: string) {
|
||||
@@ -488,19 +484,13 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
} else {
|
||||
const paths = await bridge().showOpenDialog();
|
||||
if (!paths || !paths.length) return;
|
||||
|
||||
if (md.subType === 'file_path') {
|
||||
updateSettingValue(key, paths[0]);
|
||||
} else {
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
cmd[0] = paths[0];
|
||||
updateSettingValue(key, joinCmd(cmd));
|
||||
}
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
cmd[0] = paths[0];
|
||||
updateSettingValue(key, joinCmd(cmd));
|
||||
}
|
||||
};
|
||||
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
const path = md.subType === 'file_path_and_args' ? cmd[0] : this.state.settings[key];
|
||||
|
||||
const argComp = md.subType !== 'file_path_and_args' ? null : (
|
||||
<div style={{ ...rowStyle, marginBottom: 5 }}>
|
||||
@@ -536,7 +526,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
onChange={(event: any) => {
|
||||
onPathChange(event);
|
||||
}}
|
||||
value={path}
|
||||
value={cmd[0]}
|
||||
spellCheck={false}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -101,7 +101,6 @@ export default function(props: Props) {
|
||||
|
||||
const pluginSettings = useMemo(() => {
|
||||
return pluginService.unserializePluginSettings(props.value);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.value]);
|
||||
|
||||
const pluginItems = usePluginItems(pluginService.plugins, pluginSettings);
|
||||
@@ -168,7 +167,6 @@ export default function(props: Props) {
|
||||
});
|
||||
|
||||
props.onChange({ value: pluginService.serializePluginSettings(newSettings) });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [pluginSettings, props.onChange]);
|
||||
|
||||
const onToggle = useCallback((event: ItemEvent) => {
|
||||
@@ -180,7 +178,6 @@ export default function(props: Props) {
|
||||
});
|
||||
|
||||
props.onChange({ value: pluginService.serializePluginSettings(newSettings) });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [pluginSettings, props.onChange]);
|
||||
|
||||
const onInstall = useCallback(async () => {
|
||||
@@ -198,7 +195,6 @@ export default function(props: Props) {
|
||||
});
|
||||
|
||||
props.onChange({ value: pluginService.serializePluginSettings(newSettings) });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [pluginSettings, props.onChange]);
|
||||
|
||||
const onBrowsePlugins = useCallback(() => {
|
||||
@@ -207,7 +203,6 @@ export default function(props: Props) {
|
||||
|
||||
const onPluginSettingsChange = useCallback((event: OnPluginSettingChangeEvent) => {
|
||||
props.onChange({ value: pluginService.serializePluginSettings(event.value) });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const onUpdate = useOnInstallHandler(setUpdatingPluginIds, pluginSettings, repoApi, onPluginSettingsChange, true);
|
||||
@@ -234,7 +229,6 @@ export default function(props: Props) {
|
||||
|
||||
const onSearchPluginSettingsChange = useCallback((event: any) => {
|
||||
props.onChange({ value: pluginService.serializePluginSettings(event.value) });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.onChange]);
|
||||
|
||||
function renderCells(items: PluginItem[]) {
|
||||
|
||||
@@ -60,7 +60,6 @@ export default function(props: Props) {
|
||||
setSearchResultCount(r.length);
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.searchQuery]);
|
||||
|
||||
const onChange = useCallback((event: OnChangeEvent) => {
|
||||
@@ -71,7 +70,6 @@ export default function(props: Props) {
|
||||
const onSearchButtonClick = useCallback(() => {
|
||||
setSearchStarted(false);
|
||||
props.onSearchQueryChange({ value: '' });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
function installState(pluginId: string): InstallState {
|
||||
|
||||
@@ -57,6 +57,5 @@ export default function(setInstallingPluginIds: Function, pluginSettings: Plugin
|
||||
});
|
||||
|
||||
if (installError) alert(_('Could not install plugin: %s', installError.message));
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [pluginSettings, onPluginSettingsChange]);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,8 @@ export default (props: Props) => {
|
||||
globalKeydownHandlersRef.current.push(elementId);
|
||||
return () => {
|
||||
const idx = globalKeydownHandlersRef.current.findIndex(e => e === elementId);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
globalKeydownHandlersRef.current.splice(idx, 1);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const isTopDialog = () => {
|
||||
@@ -51,7 +49,6 @@ export default (props: Props) => {
|
||||
} else if (event.keyCode === 27) {
|
||||
props.onCancelButtonClick();
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.onOkButtonClick, props.onCancelButtonClick]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -81,7 +81,6 @@ export default function(props: Props) {
|
||||
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [onClose, folderTitle, folderIcon, props.folderId, props.parentId]);
|
||||
|
||||
const onFolderTitleChange = useCallback((event: any) => {
|
||||
|
||||
@@ -2,19 +2,15 @@ import { FolderIcon, FolderIconType } from '@joplin/lib/services/database/types'
|
||||
|
||||
interface Props {
|
||||
folderIcon: FolderIcon;
|
||||
opacity?: number;
|
||||
}
|
||||
|
||||
export default function(props: Props) {
|
||||
const folderIcon = props.folderIcon;
|
||||
const opacity = 'opacity' in props ? props.opacity : 1;
|
||||
|
||||
if (folderIcon.type === FolderIconType.Emoji) {
|
||||
return <span style={{ fontSize: 20, opacity }}>{folderIcon.emoji}</span>;
|
||||
return <span style={{ fontSize: 20 }}>{folderIcon.emoji}</span>;
|
||||
} else if (folderIcon.type === FolderIconType.DataUrl) {
|
||||
return <img style={{ width: 20, height: 20, opacity }} src={folderIcon.dataUrl} />;
|
||||
} else if (folderIcon.type === FolderIconType.FontAwesome) {
|
||||
return <i style={{ fontSize: 18, width: 20, opacity }} className={folderIcon.name}></i>;
|
||||
return <img style={{ width: 20, height: 20 }} src={folderIcon.dataUrl} />;
|
||||
} else {
|
||||
throw new Error(`Unsupported folder icon type: ${folderIcon.type}`);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ export const ShortcutRecorder = ({ onSave, onReset, onCancel, onError, initialAc
|
||||
onError({ recorderError });
|
||||
setSaveAllowed(false);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [accelerator]);
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
|
||||
@@ -15,7 +15,6 @@ import * as openFolder from './openFolder';
|
||||
import * as openFolderDialog from './openFolderDialog';
|
||||
import * as openItem from './openItem';
|
||||
import * as openNote from './openNote';
|
||||
import * as openPdfViewer from './openPdfViewer';
|
||||
import * as openTag from './openTag';
|
||||
import * as print from './print';
|
||||
import * as renameFolder from './renameFolder';
|
||||
@@ -56,7 +55,6 @@ const index:any[] = [
|
||||
openFolderDialog,
|
||||
openItem,
|
||||
openNote,
|
||||
openPdfViewer,
|
||||
openTag,
|
||||
print,
|
||||
renameFolder,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import Resource from '@joplin/lib/models/Resource';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openPdfViewer',
|
||||
label: () => _('Open PDF viewer'),
|
||||
};
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext, resourceId: string, pageNo: number) => {
|
||||
|
||||
const resource = await Resource.load(resourceId);
|
||||
if (!resource) throw new Error(`No such resource: ${resourceId}`);
|
||||
if (resource.mime !== 'application/pdf') throw new Error(`Not a PDF: ${resource.mime}`);
|
||||
console.log('Opening PDF', resource);
|
||||
context.dispatch({
|
||||
type: 'DIALOG_OPEN',
|
||||
name: 'pdfViewer',
|
||||
props: {
|
||||
resource,
|
||||
pageNo: pageNo,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -14,24 +14,21 @@ export const declaration: CommandDeclaration = {
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext, selectedLanguages: string[] = null, useSpellChecker: boolean = null) => {
|
||||
selectedLanguages = selectedLanguages === null ? context.state.settings['spellChecker.languages'] : selectedLanguages;
|
||||
execute: async (context: CommandContext, selectedLanguage: string = null, useSpellChecker: boolean = null) => {
|
||||
selectedLanguage = selectedLanguage === null ? context.state.settings['spellChecker.language'] : selectedLanguage;
|
||||
useSpellChecker = useSpellChecker === null ? context.state.settings['spellChecker.enabled'] : useSpellChecker;
|
||||
|
||||
const menuItems = SpellCheckerService.instance().spellCheckerConfigMenuItems(selectedLanguages, useSpellChecker);
|
||||
const menuItems = SpellCheckerService.instance().spellCheckerConfigMenuItems(selectedLanguage, useSpellChecker);
|
||||
const menu = Menu.buildFromTemplate(menuItems as any);
|
||||
menu.popup(bridge().window());
|
||||
},
|
||||
|
||||
mapStateToTitle(state: AppState): string {
|
||||
if (!state.settings['spellChecker.enabled']) return null;
|
||||
const languages = state.settings['spellChecker.languages'];
|
||||
if (languages.length === 0) return null;
|
||||
const s: string[] = [];
|
||||
languages.forEach((language: string) => {
|
||||
s.push(language.split('-')[0]);
|
||||
});
|
||||
return s.join(', ');
|
||||
const language = state.settings['spellChecker.language'];
|
||||
if (!language) return null;
|
||||
const s = language.split('-');
|
||||
return s[0];
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -38,7 +38,6 @@ export default function(props: Props) {
|
||||
if ([MasterPasswordStatus.NotSet, MasterPasswordStatus.Invalid].includes(status)) return false;
|
||||
if (mode === Mode.Reset) return false;
|
||||
return true;
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [status]);
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
@@ -85,7 +84,6 @@ export default function(props: Props) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [currentPassword, password1, onClose, mode]);
|
||||
|
||||
const needToRepeatPassword = useMemo(() => {
|
||||
|
||||
@@ -122,7 +122,7 @@ interface Props {
|
||||
pluginMenuItems: any[];
|
||||
pluginMenus: any[];
|
||||
['spellChecker.enabled']: boolean;
|
||||
['spellChecker.languages']: string[];
|
||||
['spellChecker.language']: string;
|
||||
plugins: PluginStates;
|
||||
customCss: string;
|
||||
locale: string;
|
||||
@@ -174,7 +174,7 @@ function useMenuStates(menu: any, props: Props) {
|
||||
menuItemSetChecked(`sort:${type}:${field}`, (props as any)[`${type}.sortOrder.field`] === field);
|
||||
}
|
||||
|
||||
const id = type === 'notes' ? 'toggleNotesSortOrderReverse' : `sort:${type}:reverse`;
|
||||
const id = type == 'notes' ? 'toggleNotesSortOrderReverse' : `sort:${type}:reverse`;
|
||||
menuItemSetChecked(id, (props as any)[`${type}.sortOrder.reverse`]);
|
||||
}
|
||||
|
||||
@@ -192,17 +192,12 @@ function useMenuStates(menu: any, props: Props) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [
|
||||
props.menuItemProps,
|
||||
props.layoutButtonSequence,
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['notes.sortOrder.field'],
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['folders.sortOrder.field'],
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['notes.sortOrder.reverse'],
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['folders.sortOrder.reverse'],
|
||||
props.showNoteCounts,
|
||||
props.uncompletedTodosOnTop,
|
||||
@@ -281,7 +276,6 @@ function useMenu(props: Props) {
|
||||
}
|
||||
|
||||
void CommandService.instance().execute('hideModalMessage');
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.selectedFolderId]);
|
||||
|
||||
const onMenuItemClickRef = useRef(null);
|
||||
@@ -298,7 +292,6 @@ function useMenu(props: Props) {
|
||||
(commandName: string) => onMenuItemClickRef.current(commandName),
|
||||
props.locale
|
||||
);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [commandNames, pluginCommandNames, props.locale]);
|
||||
|
||||
const switchProfileMenuItems: any[] = useSwitchProfileMenuItems(props.profileConfig, menuItemDic);
|
||||
@@ -339,7 +332,7 @@ function useMenu(props: Props) {
|
||||
|
||||
sortItems.push({ type: 'separator' });
|
||||
|
||||
if (type === 'notes') {
|
||||
if (type == 'notes') {
|
||||
sortItems.push(
|
||||
{ ...menuItemDic.toggleNotesSortOrderReverse, type: 'checkbox' },
|
||||
{ ...menuItemDic.toggleNotesSortOrderField, visible: false }
|
||||
@@ -478,7 +471,7 @@ function useMenu(props: Props) {
|
||||
}
|
||||
toolsItems = toolsItems.concat(toolsItemsAll);
|
||||
|
||||
toolsItems.push(SpellCheckerService.instance().spellCheckerConfigMenuItem(props['spellChecker.languages'], props['spellChecker.enabled']));
|
||||
toolsItems.push(SpellCheckerService.instance().spellCheckerConfigMenuItem(props['spellChecker.language'], props['spellChecker.enabled']));
|
||||
|
||||
function _checkForUpdates() {
|
||||
void checkForUpdates(false, bridge().window(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||
@@ -912,16 +905,13 @@ function useMenu(props: Props) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [
|
||||
props.routeName,
|
||||
props.pluginMenuItems,
|
||||
props.pluginMenus,
|
||||
keymapLastChangeTime,
|
||||
modulesLastChangeTime,
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['spellChecker.languages'],
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['spellChecker.language'],
|
||||
props['spellChecker.enabled'],
|
||||
props.customCss,
|
||||
props.locale,
|
||||
@@ -983,7 +973,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
showCompletedTodos: state.settings.showCompletedTodos,
|
||||
pluginMenuItems: stateUtils.selectArrayShallow({ array: pluginUtils.viewsByType(state.pluginService.plugins, 'menuItem') }, 'menuBar.pluginMenuItems'),
|
||||
pluginMenus: stateUtils.selectArrayShallow({ array: pluginUtils.viewsByType(state.pluginService.plugins, 'menu') }, 'menuBar.pluginMenus'),
|
||||
['spellChecker.languages']: state.settings['spellChecker.languages'],
|
||||
['spellChecker.language']: state.settings['spellChecker.language'],
|
||||
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
|
||||
plugins: state.pluginService.plugins,
|
||||
customCss: state.customCss,
|
||||
|
||||
@@ -70,7 +70,6 @@ export default function NoteContentPropertiesDialog(props: NoteContentProperties
|
||||
useEffect(() => {
|
||||
const strippedText: string = markupToHtml().stripMarkup(props.markupLanguage, props.text);
|
||||
countElements(strippedText, setStrippedWords, setStrippedCharacters, setStrippedCharactersNoSpace, setStrippedLines);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.text]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -38,6 +38,7 @@ import ErrorBoundary from '../../../ErrorBoundary';
|
||||
import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
|
||||
import eventManager from '@joplin/lib/eventManager';
|
||||
import { EditContextMenuFilterObject } from '@joplin/lib/services/plugins/api/JoplinWorkspace';
|
||||
import { checkTableIsUnderCursor, readTableAroundCursor } from './utils/tables';
|
||||
|
||||
const menuUtils = new MenuUtils(CommandService.instance());
|
||||
|
||||
@@ -259,7 +260,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return commandOutput;
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.content, props.visiblePanes, addListItem, wrapSelectionWithStrings, setEditorPercentScroll, setViewerPercentScroll, resetScroll]);
|
||||
|
||||
const onEditorPaste = useCallback(async (event: any = null) => {
|
||||
@@ -566,7 +566,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return () => {
|
||||
document.head.removeChild(element);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.themeId, props.contentMaxWidth]);
|
||||
|
||||
const webview_domReady = useCallback(() => {
|
||||
@@ -594,7 +593,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
} else {
|
||||
props.onMessage(event);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.onMessage, props.content, setEditorPercentScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -617,10 +615,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
resourceInfos: props.resourceInfos,
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
mapsToLine: true,
|
||||
// Always using useCustomPdfViewer for now, we can add a new setting for it in future if we need to.
|
||||
useCustomPdfViewer: true,
|
||||
noteId: props.noteId,
|
||||
vendorDir: bridge().vendorDir(),
|
||||
}));
|
||||
|
||||
if (cancelled) return;
|
||||
@@ -640,7 +634,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
cancelled = true;
|
||||
shim.clearTimeout(timeoutId);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.content, props.contentKey, renderedBodyContentKey, props.contentMarkupLanguage, props.visiblePanes, props.resourceInfos, props.markupToHtml]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -666,7 +659,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
} else {
|
||||
console.error('Trying to set HTML on an undefined webview ref');
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [renderedBody, webviewReady]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -690,7 +682,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
props.setLocalSearchResultCount(matches);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.searchMarkers, previousSearchMarkers, props.setLocalSearchResultCount, props.content, previousContent, renderedBody, previousRenderedBody, renderedBody]);
|
||||
|
||||
const cellEditorStyle = useMemo(() => {
|
||||
@@ -716,8 +707,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return output;
|
||||
}, [styles.cellViewer, props.visiblePanes]);
|
||||
|
||||
const editorPaneVisible = props.visiblePanes.indexOf('editor') >= 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorRef.current) return;
|
||||
|
||||
@@ -725,10 +714,10 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
// we should focus the editor
|
||||
// The intuition is that a panel toggle (with editor in view) is the equivalent of
|
||||
// an editor interaction so users should expect the editor to be focused
|
||||
if (editorPaneVisible) {
|
||||
if (props.visiblePanes.indexOf('editor') >= 0) {
|
||||
editorRef.current.focus();
|
||||
}
|
||||
}, [editorPaneVisible]);
|
||||
}, [props.visiblePanes]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorRef.current) return;
|
||||
@@ -765,7 +754,14 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
const hasSelectedText = editorRef.current && !!editorRef.current.getSelection() ;
|
||||
const cm = editorRef.current;
|
||||
|
||||
const hasSelectedText = cm && !!cm.getSelection() ;
|
||||
|
||||
const tableIsUnderCursor = checkTableIsUnderCursor(cm);
|
||||
let tableUnderCursor: string = null;
|
||||
|
||||
if (tableIsUnderCursor) tableUnderCursor = readTableAroundCursor(cm);
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
@@ -797,6 +793,27 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
})
|
||||
);
|
||||
|
||||
if (tableUnderCursor) {
|
||||
menu.append(
|
||||
new MenuItem({ type: 'separator' })
|
||||
);
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: _('Edit table...'),
|
||||
click: async () => {
|
||||
props.dispatch({
|
||||
type: 'DIALOG_OPEN',
|
||||
name: 'tableEditor',
|
||||
props: {
|
||||
markdownTable: tableUnderCursor,
|
||||
},
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(params.misspelledWord, params.dictionarySuggestions);
|
||||
|
||||
for (const item of spellCheckerMenuItems) {
|
||||
@@ -843,7 +860,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return () => {
|
||||
bridge().window().webContents.off('context-menu', onContextMenu);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.plugins]);
|
||||
|
||||
function renderEditor() {
|
||||
|
||||
@@ -219,11 +219,9 @@ function Editor(props: EditorProps, ref: any) {
|
||||
cm.off('dragover', editor_drag);
|
||||
cm.off('refresh', editor_resize);
|
||||
cm.off('update', editor_update);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
editorParent.current.removeChild(cm.getWrapperElement());
|
||||
setEditor(null);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -236,42 +234,36 @@ function Editor(props: EditorProps, ref: any) {
|
||||
}
|
||||
editor.setOption('screenReaderLabel', props.value);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
editor.setOption('theme', props.codeMirrorTheme);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.codeMirrorTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
editor.setOption('mode', props.mode);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.mode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
editor.setOption('readOnly', props.readOnly);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.readOnly]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
editor.setOption('autoCloseBrackets', props.autoMatchBraces);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.autoMatchBraces]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
editor.setOption('keyMap', props.keyMap ? props.keyMap : 'default');
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.keyMap]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
function findElementWithClass(element: any, className: string): any {
|
||||
if (element.classList && element.classList.contains(className)) return element;
|
||||
|
||||
for (const child of element.childNodes) {
|
||||
const hasClass = findElementWithClass(child, className);
|
||||
if (hasClass) return hasClass;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export const checkTableIsUnderCursor = (cm: any) => {
|
||||
if (!cm) return false;
|
||||
|
||||
const coords = cm.cursorCoords(cm.getCursor());
|
||||
const element = document.elementFromPoint(coords.left, coords.top);
|
||||
if (!element) return false;
|
||||
return !!findElementWithClass(element, 'cm-jn-table-item');
|
||||
};
|
||||
|
||||
export const readTableAroundCursor = (cm: any) => {
|
||||
const idxAtCursor = cm.doc.getCursor().line;
|
||||
const lineCount = cm.lineCount();
|
||||
|
||||
const lines: string[] = [];
|
||||
|
||||
for (let i = idxAtCursor - 1; i >= 0; i--) {
|
||||
const line: string = cm.doc.getLine(i);
|
||||
if (line.startsWith('|')) {
|
||||
lines.splice(0, 0, line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(cm.doc.getLine(idxAtCursor));
|
||||
|
||||
for (let i = idxAtCursor + 1; i < lineCount; i++) {
|
||||
const line: string = cm.doc.getLine(i);
|
||||
if (line.startsWith('|')) {
|
||||
lines.push(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
return { token: function(stream: any) {
|
||||
query.lastIndex = stream.pos;
|
||||
const match = query.exec(stream.string);
|
||||
if (match && match.index === stream.pos) {
|
||||
if (match && match.index == stream.pos) {
|
||||
stream.pos += match[0].length || 1;
|
||||
return 'search-marker';
|
||||
} else if (match) {
|
||||
@@ -126,7 +126,7 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
|
||||
// SEARCHOVERLAY
|
||||
// We only want to highlight all matches when there is only 1 search term
|
||||
if (keywords.length !== 1 || keywords[0].value === '') {
|
||||
if (keywords.length !== 1 || keywords[0].value == '') {
|
||||
clearOverlay(this);
|
||||
const prev = keywords.length > 1 ? keywords[0].value : '';
|
||||
setPreviousKeywordValue(prev);
|
||||
|
||||
@@ -7,8 +7,6 @@ import uuid from '@joplin/lib/uuid';
|
||||
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
|
||||
const loadedPluginIdSet = new Set<string>();
|
||||
|
||||
export default function useExternalPlugins(CodeMirror: any, plugins: PluginStates) {
|
||||
|
||||
const [options, setOptions] = useState({});
|
||||
@@ -19,10 +17,6 @@ export default function useExternalPlugins(CodeMirror: any, plugins: PluginState
|
||||
|
||||
for (const contentScript of contentScripts) {
|
||||
try {
|
||||
if (loadedPluginIdSet.has(contentScript.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mod = contentScript.module;
|
||||
|
||||
if (mod.codeMirrorResources) {
|
||||
@@ -70,14 +64,11 @@ export default function useExternalPlugins(CodeMirror: any, plugins: PluginState
|
||||
if (mod.plugin) {
|
||||
mod.plugin(CodeMirror);
|
||||
}
|
||||
|
||||
loadedPluginIdSet.add(contentScript.id);
|
||||
} catch (error) {
|
||||
reg.logger().error(error.toString());
|
||||
}
|
||||
}
|
||||
setOptions(newOptions);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [plugins]);
|
||||
|
||||
function addInlineCss(cssStrings: string[], id: string) {
|
||||
|
||||
@@ -165,6 +165,7 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
}
|
||||
|
||||
if (isMonospace) { token = `${token} jn-monospace`; }
|
||||
if (state.inTable) { token = `${token} jn-table-item`; }
|
||||
// //////// End Monospace //////////
|
||||
|
||||
return token;
|
||||
|
||||
@@ -184,6 +184,5 @@ export default function useKeymap(CodeMirror: any) {
|
||||
|
||||
setupEmacs();
|
||||
setupVim();
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
if (editorRef.current) {
|
||||
scheduleOnScroll({ percent });
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [scheduleOnScroll]);
|
||||
|
||||
const setViewerPercentScroll = useCallback((percent: number) => {
|
||||
@@ -102,7 +101,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
webviewRef.current.wrappedInstance.send('setPercentScroll', percent);
|
||||
scheduleOnScroll({ percent });
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [scheduleOnScroll]);
|
||||
|
||||
const editor_scroll = useCallback(() => {
|
||||
@@ -128,7 +126,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [setViewerPercentScroll]);
|
||||
|
||||
const resetScroll = useCallback(() => {
|
||||
@@ -137,7 +134,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
editorRef.current.setScrollPercent(0);
|
||||
scrollTopIsUncertain_.current = false;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const editor_resize = useCallback((cm) => {
|
||||
@@ -156,7 +152,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
// When heights of lines are updated in CodeMirror, 'update' events are raised.
|
||||
@@ -178,7 +173,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const getLineScrollPercent = useCallback(() => {
|
||||
@@ -189,7 +183,6 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
} else {
|
||||
return scrollPercent_.current;
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
return {
|
||||
|
||||
@@ -280,7 +280,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [editor, props.contentMarkupLanguage, props.contentOriginalCss]);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
@@ -513,7 +512,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
// style and re-applying it on editorReady gives our styles precedence and prevents any flashing
|
||||
//
|
||||
// tl;dr: editorReady is used here because the css needs to be re-applied after TinyMCE init
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [editorReady, props.themeId]);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
@@ -682,7 +680,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
};
|
||||
|
||||
void loadEditor();
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [scriptLoaded]);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
@@ -832,7 +829,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [editor, props.markupToHtml, props.allAssets, props.content, props.resourceInfos, props.contentKey]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -913,7 +909,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
return () => {
|
||||
void execOnChangeEvent();
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const onChangeHandlerTimeoutRef = useRef<any>(null);
|
||||
@@ -1096,7 +1091,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
console.warn('Error removing events', error);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.onWillChange, props.onChange, props.contentMarkupLanguage, props.contentOriginalCss, editor]);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
@@ -2078,17 +2078,6 @@
|
||||
setup(editor);
|
||||
};
|
||||
|
||||
var setup$2 = function (editor) {
|
||||
var editorClickHandler = function (event) {
|
||||
if (!isJoplinChecklistItem(event.target))
|
||||
return;
|
||||
if (event.offsetX >= 0)
|
||||
return;
|
||||
editor.execCommand('ToggleJoplinChecklistItem', false, { element: event.target });
|
||||
};
|
||||
editor.on('click', editorClickHandler);
|
||||
};
|
||||
|
||||
var findIndex = function (list, predicate) {
|
||||
for (var index = 0; index < list.length; index++) {
|
||||
var element = list[index];
|
||||
@@ -2111,8 +2100,21 @@
|
||||
var listType = findContainerListTypeFromEvent(e);
|
||||
buttonApi.setActive(listType === options.listType && lists.length > 0 && lists[0].nodeName === listName && !isCustomList(lists[0]));
|
||||
};
|
||||
var editorClickHandler = function (event) {
|
||||
if (!isJoplinChecklistItem(event.target))
|
||||
return;
|
||||
if (event.offsetX >= 0)
|
||||
return;
|
||||
editor.execCommand('ToggleJoplinChecklistItem', false, { element: event.target });
|
||||
};
|
||||
if (options.listType === 'joplinChecklist') {
|
||||
editor.on('click', editorClickHandler);
|
||||
}
|
||||
editor.on('NodeChange', nodeChangeHandler);
|
||||
return function () {
|
||||
if (options.listType === 'joplinChecklist') {
|
||||
editor.off('click', editorClickHandler);
|
||||
}
|
||||
editor.off('NodeChange', nodeChangeHandler);
|
||||
};
|
||||
};
|
||||
@@ -2156,7 +2158,6 @@
|
||||
function Plugin () {
|
||||
PluginManager.add('joplinLists', function (editor) {
|
||||
setup$1(editor);
|
||||
setup$2(editor);
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
return get(editor);
|
||||
|
||||
@@ -59,7 +59,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
const formNote_beforeLoad = useCallback(async (event: OnLoadEvent) => {
|
||||
await saveNoteIfWillChange(event.formNote);
|
||||
setShowRevisions(false);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const formNote_afterLoad = useCallback(async () => {
|
||||
@@ -178,7 +177,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
id: formNote.id,
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.isProvisional, formNote.id]);
|
||||
|
||||
const previousNoteId = usePrevious(formNote.id);
|
||||
@@ -196,7 +194,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
});
|
||||
|
||||
void ResourceEditWatcher.instance().stopWatchingAll();
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [formNote.id, previousNoteId]);
|
||||
|
||||
const onFieldChange = useCallback((field: string, value: any, changeId = 0) => {
|
||||
@@ -241,7 +238,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
setFormNote(newNote);
|
||||
scheduleSaveNote(newNote);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [handleProvisionalFlag, formNote, isNewNote, titleHasBeenManuallyChanged]);
|
||||
|
||||
useWindowCommandHandler({
|
||||
@@ -292,7 +288,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
id: formNote.id,
|
||||
status: 'saving',
|
||||
});
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [formNote, handleProvisionalFlag]);
|
||||
|
||||
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote);
|
||||
@@ -307,7 +302,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
setFormNote(newFormNote);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [formNote]);
|
||||
|
||||
const onNotePropertyChange = useCallback((event) => {
|
||||
@@ -323,7 +317,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
return newFormNote;
|
||||
});
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -357,7 +350,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
noteId: formNoteRef.current.id,
|
||||
percent: event.percent,
|
||||
});
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.dispatch, formNote]);
|
||||
|
||||
function renderNoNotes(rootStyle: any) {
|
||||
@@ -421,9 +413,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
fontSize: Setting.value('style.editor.fontSize'),
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
isSafeMode: props.isSafeMode,
|
||||
// We need it to identify the context for which media is rendered.
|
||||
// It is currently used to remember pdf scroll position for each attacments of each note uniquely.
|
||||
noteId: props.noteId,
|
||||
};
|
||||
|
||||
let editor = null;
|
||||
|
||||
@@ -100,7 +100,7 @@ export function menuItems(dispatch: Function): ContextMenuItems {
|
||||
label: _('Save as %s', 'PNG'),
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
// First convert it to png then save
|
||||
if (options.mime !== 'image/svg+xml') {
|
||||
if (options.mime != 'image/svg+xml') {
|
||||
throw new Error(`Unsupported image type: ${options.mime}`);
|
||||
}
|
||||
if (!options.filename) {
|
||||
@@ -151,14 +151,14 @@ export function menuItems(dispatch: Function): ContextMenuItems {
|
||||
handleCopyToClipboard(options);
|
||||
options.insertContent('');
|
||||
},
|
||||
isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => itemType !== ContextMenuItemType.Image && (!options.isReadOnly && (!!options.textToCopy || !!options.htmlToCopy)),
|
||||
isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => itemType != ContextMenuItemType.Image && (!options.isReadOnly && (!!options.textToCopy || !!options.htmlToCopy)),
|
||||
},
|
||||
copy: {
|
||||
label: _('Copy'),
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
handleCopyToClipboard(options);
|
||||
},
|
||||
isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => itemType !== ContextMenuItemType.Image && (!!options.textToCopy || !!options.htmlToCopy),
|
||||
isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => itemType != ContextMenuItemType.Image && (!!options.textToCopy || !!options.htmlToCopy),
|
||||
},
|
||||
paste: {
|
||||
label: _('Paste'),
|
||||
|
||||
@@ -75,7 +75,6 @@ export interface NoteBodyEditorProps {
|
||||
fontSize: number;
|
||||
contentMaxWidth: number;
|
||||
isSafeMode: boolean;
|
||||
noteId: string;
|
||||
}
|
||||
|
||||
export interface FormNote {
|
||||
|
||||
@@ -51,6 +51,5 @@ export default function useDropHandler(dependencies: HookDependencies) {
|
||||
},
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
if (syncStarted) return () => {};
|
||||
if (formNote.hasChanged) return () => {};
|
||||
|
||||
reg.logger().info('Sync has finished and note has never been changed - reloading it');
|
||||
reg.logger().debug('Sync has finished and note has never been changed - reloading it');
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
@@ -138,7 +138,6 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [prevSyncStarted, syncStarted, formNote]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -189,7 +188,6 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [noteId, isProvisional, formNote]);
|
||||
|
||||
const onResourceChange = useCallback(async function(event: any = null) {
|
||||
|
||||
@@ -20,9 +20,6 @@ export interface MarkupToHtmlOptions {
|
||||
plugins?: Record<string, any>;
|
||||
bodyOnly?: boolean;
|
||||
mapsToLine?: boolean;
|
||||
useCustomPdfViewer?: boolean;
|
||||
noteId?: string;
|
||||
vendorDir?: string;
|
||||
}
|
||||
|
||||
export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
@@ -33,7 +30,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
customCss: customCss || '',
|
||||
});
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [plugins, customCss]);
|
||||
|
||||
return useCallback(async (markupLanguage: number, md: string, options: MarkupToHtmlOptions = null): Promise<any> => {
|
||||
@@ -65,6 +61,5 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
}, options));
|
||||
|
||||
return result;
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [themeId, customCss, markupToHtml]);
|
||||
}
|
||||
|
||||
@@ -52,12 +52,9 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
|
||||
void CommandService.instance().execute(commandName, ...commandArgs);
|
||||
} else if (msg === 'postMessageService.message') {
|
||||
void PostMessageService.instance().postMessage(arg0);
|
||||
} else if (msg === 'openPdfViewer') {
|
||||
await CommandService.instance().execute('openPdfViewer', arg0.resourceId, arg0.pageNo);
|
||||
} else {
|
||||
await CommandService.instance().execute('openItem', msg);
|
||||
// bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [dispatch, setLocalSearchResultCount, scrollWhenReady, formNote]);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@ export default function usePluginServiceRegistration(ref: any) {
|
||||
return () => {
|
||||
PlatformImplementation.instance().unregisterComponent('textEditor');
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,5 @@ export default function useSearchMarkers(showLocalSearch: boolean, localSearchMa
|
||||
output.keywords = highlightedWords;
|
||||
|
||||
return output;
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [highlightedWords, showLocalSearch, localSearchMarkerOptions, searches, selectedSearchId]);
|
||||
}
|
||||
|
||||
@@ -96,6 +96,5 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
|
||||
CommandService.instance().unregisterRuntime(command.declaration.name);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef]);
|
||||
}
|
||||
|
||||
@@ -273,7 +273,6 @@ const NoteListComponent = (props: Props) => {
|
||||
onTitleClick={noteItem_titleClick}
|
||||
onContextMenu={itemContextMenu}
|
||||
/>;
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [style, props.themeId, width, itemHeight, dragOverTargetNoteIndex, props.provisionalNoteIds, props.selectedNoteIds, props.watchedNoteFiles,
|
||||
props.notes,
|
||||
props.notesParentType,
|
||||
@@ -306,7 +305,6 @@ const NoteListComponent = (props: Props) => {
|
||||
if (previousVisible !== props.visible) {
|
||||
updateSizeState();
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [previousSelectedNoteIds,previousNotes, previousVisible, props.selectedNoteIds, props.notes]);
|
||||
|
||||
const scrollNoteIndex_ = (keyCode: any, ctrlKey: any, metaKey: any, noteIndex: any) => {
|
||||
@@ -344,7 +342,7 @@ const NoteListComponent = (props: Props) => {
|
||||
const keyCode = event.keyCode;
|
||||
const noteIds = props.selectedNoteIds;
|
||||
|
||||
if (noteIds.length > 0 && (keyCode === 40 || keyCode === 38 || keyCode === 33 || keyCode === 34 || keyCode === 35 || keyCode === 36)) {
|
||||
if (noteIds.length > 0 && (keyCode === 40 || keyCode === 38 || keyCode === 33 || keyCode === 34 || keyCode === 35 || keyCode == 36)) {
|
||||
// DOWN / UP / PAGEDOWN / PAGEUP / END / HOME
|
||||
const noteId = noteIds[0];
|
||||
let noteIndex = BaseModel.modelIndexById(props.notes, noteId);
|
||||
@@ -441,7 +439,6 @@ const NoteListComponent = (props: Props) => {
|
||||
return () => {
|
||||
props.resizableLayoutEventEmitter.off('resize', resizableLayout_resize);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.resizableLayoutEventEmitter]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -456,15 +453,9 @@ const NoteListComponent = (props: Props) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
useEffect(() => {
|
||||
// When a note list item is styled by userchrome.css, its height is reflected.
|
||||
// Ref. https://github.com/laurent22/joplin/pull/6542
|
||||
if (dragOverTargetNoteIndex !== null) {
|
||||
// When dragged, its height should not be considered.
|
||||
// Ref. https://github.com/laurent22/joplin/issues/6639
|
||||
return;
|
||||
}
|
||||
const noteItem = Object.values<any>(itemAnchorRefs_.current)[0]?.current;
|
||||
const actualItemHeight = noteItem?.getHeight() ?? 0;
|
||||
if (actualItemHeight >= 8) { // To avoid generating too many narrow items
|
||||
|
||||
@@ -41,7 +41,7 @@ class NotePropertiesDialog extends React.Component {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.state.editedKey === null) {
|
||||
if (this.state.editedKey == null) {
|
||||
this.okButton.current.focus();
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ class NotePropertiesDialog extends React.Component {
|
||||
latLongFromLocation(location) {
|
||||
const o = {};
|
||||
const l = location.split(',');
|
||||
if (l.length === 2) {
|
||||
if (l.length == 2) {
|
||||
o.latitude = l[0].trim();
|
||||
o.longitude = l[1].trim();
|
||||
} else {
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { useCallback, useRef, useEffect } from 'react';
|
||||
import Resource from '@joplin/lib/models/Resource';
|
||||
import bridge from '../services/bridge';
|
||||
import contextMenu from './NoteEditor/utils/contextMenu';
|
||||
import { ContextMenuItemType, ContextMenuOptions } from './NoteEditor/utils/contextMenuUtils';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import styled from 'styled-components';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
|
||||
const Window = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 999;
|
||||
background-color: ${(props: any) => props.theme.backgroundColor};
|
||||
color: ${(props: any) => props.theme.color};
|
||||
`;
|
||||
|
||||
const IFrame = styled.iframe`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: none;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
dispatch: Function;
|
||||
resource: any;
|
||||
pageNo: number;
|
||||
}
|
||||
|
||||
export default function PdfViewer(props: Props) {
|
||||
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
props.dispatch({
|
||||
type: 'DIALOG_CLOSE',
|
||||
name: 'pdfViewer',
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
|
||||
const openExternalViewer = useCallback(async () => {
|
||||
await CommandService.instance().execute('openItem', `joplin://${props.resource.id}`);
|
||||
}, [props.resource.id]);
|
||||
|
||||
const textSelected = useCallback(async (text: string) => {
|
||||
if (!text) return;
|
||||
const itemType = ContextMenuItemType.Text;
|
||||
const menu = await contextMenu({
|
||||
itemType,
|
||||
resourceId: null,
|
||||
filename: null,
|
||||
mime: 'text/plain',
|
||||
textToCopy: text,
|
||||
linkToCopy: null,
|
||||
htmlToCopy: '',
|
||||
insertContent: () => { console.warn('insertContent() not implemented'); },
|
||||
} as ContextMenuOptions, props.dispatch);
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}, [props.dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const onMessage_ = async (event: any) =>{
|
||||
if (!event.data || !event.data.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.name === 'close') {
|
||||
onClose();
|
||||
} else if (event.data.name === 'externalViewer') {
|
||||
await openExternalViewer();
|
||||
} else if (event.data.name === 'textSelected') {
|
||||
await textSelected(event.data.text);
|
||||
} else {
|
||||
console.error('Unknown event received', event.data.name);
|
||||
}
|
||||
};
|
||||
const iframe = iframeRef.current;
|
||||
iframe.contentWindow.addEventListener('message', onMessage_);
|
||||
return () => {
|
||||
iframe.contentWindow.removeEventListener('message', onMessage_);
|
||||
};
|
||||
}, [onClose, openExternalViewer, textSelected]);
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
return (
|
||||
<Window theme={theme}>
|
||||
<IFrame src="./vendor/lib/@joplin/pdf-viewer/index.html" x-url={Resource.fullPath(props.resource)}
|
||||
x-appearance={theme.appearance} ref={iframeRef}
|
||||
x-title={props.resource.title}
|
||||
x-anchorpage={props.pageNo}
|
||||
x-type="full"></IFrame>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
@@ -13,6 +13,5 @@ export default function useWindowResizeEvent(eventEmitter: any) {
|
||||
window_resize.clear();
|
||||
window.removeEventListener('resize', window_resize);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import Dialog from './Dialog';
|
||||
import SyncWizardDialog from './SyncWizard/Dialog';
|
||||
import MasterPasswordDialog from './MasterPasswordDialog/Dialog';
|
||||
import EditFolderDialog from './EditFolderDialog/Dialog';
|
||||
import PdfViewer from './PdfViewer';
|
||||
import TableEditorDialog from './TableEditorDialog/Dialog';
|
||||
import StyleSheetContainer from './StyleSheets/StyleSheetContainer';
|
||||
const { ImportScreen } = require('./ImportScreen.min.js');
|
||||
const { ResourceScreen } = require('./ResourceScreen.js');
|
||||
@@ -39,6 +39,7 @@ interface Props {
|
||||
zoomFactor: number;
|
||||
needApiAuth: boolean;
|
||||
dialogs: AppStateDialog[];
|
||||
dialogContentMaxSize: Size;
|
||||
}
|
||||
|
||||
interface ModalDialogProps {
|
||||
@@ -52,6 +53,7 @@ interface RegisteredDialogProps {
|
||||
themeId: number;
|
||||
key: string;
|
||||
dispatch: Function;
|
||||
dialogContentMaxSize: Size;
|
||||
}
|
||||
|
||||
interface RegisteredDialog {
|
||||
@@ -61,24 +63,25 @@ interface RegisteredDialog {
|
||||
const registeredDialogs: Record<string, RegisteredDialog> = {
|
||||
syncWizard: {
|
||||
render: (props: RegisteredDialogProps, customProps: any) => {
|
||||
return <SyncWizardDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>;
|
||||
return <SyncWizardDialog key={props.key} dispatch={props.dispatch} dialogContentMaxSize={props.dialogContentMaxSize} themeId={props.themeId} {...customProps}/>;
|
||||
},
|
||||
},
|
||||
|
||||
masterPassword: {
|
||||
render: (props: RegisteredDialogProps, customProps: any) => {
|
||||
return <MasterPasswordDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>;
|
||||
return <MasterPasswordDialog key={props.key} dispatch={props.dispatch} dialogContentMaxSize={props.dialogContentMaxSize} themeId={props.themeId} {...customProps}/>;
|
||||
},
|
||||
},
|
||||
|
||||
editFolder: {
|
||||
render: (props: RegisteredDialogProps, customProps: any) => {
|
||||
return <EditFolderDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>;
|
||||
return <EditFolderDialog key={props.key} dispatch={props.dispatch} dialogContentMaxSize={props.dialogContentMaxSize} themeId={props.themeId} {...customProps}/>;
|
||||
},
|
||||
},
|
||||
pdfViewer: {
|
||||
|
||||
tableEditor: {
|
||||
render: (props: RegisteredDialogProps, customProps: any) => {
|
||||
return <PdfViewer key={props.key} dispatch={props.dispatch} themeId={props.themeId} {...customProps}/>;
|
||||
return <TableEditorDialog key={props.key} dispatch={props.dispatch} dialogContentMaxSize={props.dialogContentMaxSize} themeId={props.themeId} {...customProps}/>;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -127,7 +130,7 @@ async function initialize() {
|
||||
|
||||
class RootComponent extends React.Component<Props, any> {
|
||||
public async componentDidMount() {
|
||||
if (this.props.appState === 'starting') {
|
||||
if (this.props.appState == 'starting') {
|
||||
this.props.dispatch({
|
||||
type: 'APP_STATE_SET',
|
||||
state: 'initializing',
|
||||
@@ -201,10 +204,12 @@ class RootComponent extends React.Component<Props, any> {
|
||||
for (const dialog of props.dialogs) {
|
||||
const md = registeredDialogs[dialog.name];
|
||||
if (!md) throw new Error(`Unknown dialog: ${dialog.name}`);
|
||||
|
||||
output.push(md.render({
|
||||
key: dialog.name,
|
||||
themeId: props.themeId,
|
||||
dispatch: props.dispatch,
|
||||
dialogContentMaxSize: props.dialogContentMaxSize,
|
||||
}, dialog.props));
|
||||
}
|
||||
return output;
|
||||
@@ -251,6 +256,11 @@ const mapStateToProps = (state: AppState) => {
|
||||
themeId: state.settings.theme,
|
||||
needApiAuth: state.needApiAuth,
|
||||
dialogs: state.dialogs,
|
||||
dialogContentMaxSize: {
|
||||
// Minus padding, margins and dialog header and button bar.
|
||||
width: state.windowContentSize.width - 36 * 2,
|
||||
height: state.windowContentSize.height - 36 * 2 - 28 - 30 - 20,
|
||||
},
|
||||
profileConfigCurrentProfileId: state.profileConfig.currentProfileId,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -66,7 +66,6 @@ function useRestartOnDone(upgradeResult: SyncTargetUpgradeResult) {
|
||||
if (upgradeResult.done && !upgradeResult.error) {
|
||||
void restart();
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [upgradeResult.done]);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ function SearchBar(props: Props) {
|
||||
return () => {
|
||||
debouncedSearch.clear();
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [query, searchStarted]);
|
||||
|
||||
const onExitSearch = useCallback(async (navigateAway = true) => {
|
||||
@@ -81,7 +80,6 @@ function SearchBar(props: Props) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.selectedNoteId]);
|
||||
|
||||
function onChange(event: any) {
|
||||
@@ -121,7 +119,9 @@ function SearchBar(props: Props) {
|
||||
}, [onExitSearch]);
|
||||
|
||||
const onSearchButtonClick = useCallback(() => {
|
||||
if (props.isFocused || searchStarted) {
|
||||
console.info('isFocused', props.isFocused);
|
||||
|
||||
if (props.isFocused) {
|
||||
void onExitSearch();
|
||||
} else {
|
||||
setSearchStarted(true);
|
||||
@@ -131,8 +131,7 @@ function SearchBar(props: Props) {
|
||||
field: 'globalSearch',
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [onExitSearch, props.isFocused, searchStarted]);
|
||||
}, [onExitSearch, props.isFocused]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.notesParentType !== 'Search') {
|
||||
@@ -153,7 +152,6 @@ function SearchBar(props: Props) {
|
||||
}
|
||||
void onExitSearch(true);
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -140,7 +140,6 @@ function ShareFolderDialog(props: Props) {
|
||||
useEffect(() => {
|
||||
const s = props.shares.find(s => s.folder_id === props.folderId);
|
||||
setShare(s);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.shares]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import { StyledRoot, StyledAddButton, StyledShareIcon, StyledHeader, StyledHeaderIcon, StyledAllNotesIcon, StyledHeaderLabel, StyledListItem, StyledListItemAnchor, StyledExpandLink, StyledNoteCount, StyledSyncReportText, StyledSyncReport, StyledSynchronizeButton } from './styles';
|
||||
import { ButtonLevel } from '../Button/Button';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
@@ -18,14 +17,12 @@ import Folder from '@joplin/lib/models/Folder';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Tag from '@joplin/lib/models/Tag';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { FolderEntity, FolderIcon, FolderIconType } from '@joplin/lib/services/database/types';
|
||||
import { FolderEntity, FolderIcon } from '@joplin/lib/services/database/types';
|
||||
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
|
||||
import { store } from '@joplin/lib/reducer';
|
||||
import PerFolderSortOrderService from '../../services/sortOrder/PerFolderSortOrderService';
|
||||
import { getFolderCallbackUrl, getTagCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||
import FolderIconBox from '../FolderIconBox';
|
||||
import { Theme } from '@joplin/lib/themes/type';
|
||||
import { RuntimeProps } from './commands/focusElementSideBar';
|
||||
const { connect } = require('react-redux');
|
||||
const shared = require('@joplin/lib/components/shared/side-menu-shared.js');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
@@ -53,8 +50,11 @@ interface Props {
|
||||
tags: any[];
|
||||
syncStarted: boolean;
|
||||
plugins: PluginStates;
|
||||
folderHeaderIsExpanded: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
tagHeaderIsExpanded: boolean;
|
||||
folderHeaderIsExpanded: boolean;
|
||||
}
|
||||
|
||||
const commands = [
|
||||
@@ -79,21 +79,13 @@ function ExpandLink(props: any) {
|
||||
}
|
||||
|
||||
const renderFolderIcon = (folderIcon: FolderIcon) => {
|
||||
if (!folderIcon) {
|
||||
const defaultFolderIcon: FolderIcon = {
|
||||
dataUrl: '',
|
||||
emoji: '',
|
||||
name: 'far fa-folder',
|
||||
type: FolderIconType.FontAwesome,
|
||||
};
|
||||
return <div style={{ marginRight: 5, display: 'flex' }}><FolderIconBox opacity={0.7} folderIcon={defaultFolderIcon}/></div>;
|
||||
}
|
||||
if (!folderIcon) return null;
|
||||
|
||||
return <div style={{ marginRight: 5, display: 'flex' }}><FolderIconBox folderIcon={folderIcon}/></div>;
|
||||
};
|
||||
|
||||
function FolderItem(props: any) {
|
||||
const { hasChildren, showFolderIcon, isExpanded, parentId, depth, selected, folderId, folderTitle, folderIcon, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props;
|
||||
const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, folderIcon, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props;
|
||||
|
||||
const noteCountComp = noteCount ? <StyledNoteCount className="note-count-label">{noteCount}</StyledNoteCount> : null;
|
||||
|
||||
@@ -118,7 +110,7 @@ function FolderItem(props: any) {
|
||||
}}
|
||||
onDoubleClick={onFolderToggleClick_}
|
||||
>
|
||||
{showFolderIcon ? renderFolderIcon(folderIcon) : null}<span className="title" style={{ lineHeight: 0 }}>{folderTitle}</span>
|
||||
{renderFolderIcon(folderIcon)}<span className="title" style={{ lineHeight: 0 }}>{folderTitle}</span>
|
||||
{shareIcon} {noteCountComp}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
@@ -127,88 +119,56 @@ function FolderItem(props: any) {
|
||||
|
||||
const menuUtils = new MenuUtils(CommandService.instance());
|
||||
|
||||
const SidebarComponent = (props: Props) => {
|
||||
class SidebarComponent extends React.Component<Props, State> {
|
||||
|
||||
const folderItemsOrder_ = useRef<any[]>();
|
||||
folderItemsOrder_.current = [];
|
||||
const tagItemsOrder_ = useRef<any[]>();
|
||||
tagItemsOrder_.current = [];
|
||||
private folderItemsOrder_: any[] = [];
|
||||
private tagItemsOrder_: any[] = [];
|
||||
private rootRef: any = null;
|
||||
private anchorItemRefs: any = {};
|
||||
private pluginsRef: any;
|
||||
|
||||
const rootRef = useRef(null);
|
||||
const anchorItemRefs = useRef<Record<string, any>>(null);
|
||||
anchorItemRefs.current = {};
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
// This whole component is a bit of a mess and rather than passing
|
||||
// a plugins prop around, not knowing how it's going to affect
|
||||
// re-rendering, we just keep a ref to it. Currently that's enough
|
||||
// as plugins are only accessed from context menus. However if want
|
||||
// to do more complex things with plugins in the sidebar, it will
|
||||
// probably have to be refactored using React Hooks first.
|
||||
const pluginsRef = useRef<PluginStates>(null);
|
||||
pluginsRef.current = props.plugins;
|
||||
CommandService.instance().componentRegisterCommands(this, commands);
|
||||
|
||||
// If at least one of the folder has an icon, then we display icons for all
|
||||
// folders (those without one will get the default icon). This is so that
|
||||
// visual alignment is correct for all folders, otherwise the folder tree
|
||||
// looks messy.
|
||||
const showFolderIcons = useMemo(() => {
|
||||
return !!props.folders.find((f: FolderEntity) => !!f.icon);
|
||||
}, [props.folders]);
|
||||
|
||||
const getSelectedItem = useCallback(() => {
|
||||
if (props.notesParentType === 'Folder' && props.selectedFolderId) {
|
||||
return { type: 'folder', id: props.selectedFolderId };
|
||||
} else if (props.notesParentType === 'Tag' && props.selectedTagId) {
|
||||
return { type: 'tag', id: props.selectedTagId };
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [props.notesParentType, props.selectedFolderId, props.selectedTagId]);
|
||||
|
||||
const getFirstAnchorItemRef = useCallback((type: string) => {
|
||||
const refs = anchorItemRefs.current[type];
|
||||
if (!refs) return null;
|
||||
|
||||
const p = type === 'folder' ? props.folders : props.tags;
|
||||
const item = p && p.length ? p[0] : null;
|
||||
if (!item) return null;
|
||||
|
||||
return refs[item.id];
|
||||
}, [anchorItemRefs, props.folders, props.tags]);
|
||||
|
||||
useEffect(() => {
|
||||
const runtimeProps: RuntimeProps = {
|
||||
getSelectedItem,
|
||||
anchorItemRefs,
|
||||
getFirstAnchorItemRef,
|
||||
this.state = {
|
||||
tagHeaderIsExpanded: Setting.value('tagHeaderIsExpanded'),
|
||||
folderHeaderIsExpanded: Setting.value('folderHeaderIsExpanded'),
|
||||
};
|
||||
|
||||
CommandService.instance().componentRegisterCommands(runtimeProps, commands);
|
||||
// This whole component is a bit of a mess and rather than passing
|
||||
// a plugins prop around, not knowing how it's going to affect
|
||||
// re-rendering, we just keep a ref to it. Currently that's enough
|
||||
// as plugins are only accessed from context menus. However if want
|
||||
// to do more complex things with plugins in the sidebar, it will
|
||||
// probably have to be refactored using React Hooks first.
|
||||
this.pluginsRef = React.createRef();
|
||||
|
||||
return () => {
|
||||
CommandService.instance().componentUnregisterCommands(commands);
|
||||
};
|
||||
}, [
|
||||
getSelectedItem,
|
||||
anchorItemRefs,
|
||||
getFirstAnchorItemRef,
|
||||
]);
|
||||
this.onFolderToggleClick_ = this.onFolderToggleClick_.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onAllNotesClick_ = this.onAllNotesClick_.bind(this);
|
||||
this.header_contextMenu = this.header_contextMenu.bind(this);
|
||||
this.onAddFolderButtonClick = this.onAddFolderButtonClick.bind(this);
|
||||
this.folderItem_click = this.folderItem_click.bind(this);
|
||||
this.itemContextMenu = this.itemContextMenu.bind(this);
|
||||
}
|
||||
|
||||
const onFolderDragStart_ = useCallback((event: any) => {
|
||||
onFolderDragStart_(event: any) {
|
||||
const folderId = event.currentTarget.getAttribute('data-folder-id');
|
||||
if (!folderId) return;
|
||||
|
||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||
event.dataTransfer.clearData();
|
||||
event.dataTransfer.setData('text/x-jop-folder-ids', JSON.stringify([folderId]));
|
||||
}, []);
|
||||
}
|
||||
|
||||
const onFolderDragOver_ = useCallback((event: any) => {
|
||||
onFolderDragOver_(event: any) {
|
||||
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault();
|
||||
if (event.dataTransfer.types.indexOf('text/x-jop-folder-ids') >= 0) event.preventDefault();
|
||||
}, []);
|
||||
}
|
||||
|
||||
const onFolderDrop_ = useCallback(async (event: any) => {
|
||||
async onFolderDrop_(event: any) {
|
||||
const folderId = event.currentTarget.getAttribute('data-folder-id');
|
||||
const dt = event.dataTransfer;
|
||||
if (!dt) return;
|
||||
@@ -239,9 +199,9 @@ const SidebarComponent = (props: Props) => {
|
||||
logger.error(error);
|
||||
alert(error.message);
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
const onTagDrop_ = useCallback(async (event: any) => {
|
||||
async onTagDrop_(event: any) {
|
||||
const tagId = event.currentTarget.getAttribute('data-tag-id');
|
||||
const dt = event.dataTransfer;
|
||||
if (!dt) return;
|
||||
@@ -254,18 +214,22 @@ const SidebarComponent = (props: Props) => {
|
||||
await Tag.addNote(tagId, noteIds[i]);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
const onFolderToggleClick_ = useCallback((event: any) => {
|
||||
async onFolderToggleClick_(event: any) {
|
||||
const folderId = event.currentTarget.getAttribute('data-folder-id');
|
||||
|
||||
props.dispatch({
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_TOGGLE',
|
||||
id: folderId,
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
}
|
||||
|
||||
const header_contextMenu = useCallback(async () => {
|
||||
componentWillUnmount() {
|
||||
CommandService.instance().componentUnregisterCommands(commands);
|
||||
}
|
||||
|
||||
async header_contextMenu() {
|
||||
const menu = new Menu();
|
||||
|
||||
menu.append(
|
||||
@@ -273,9 +237,9 @@ const SidebarComponent = (props: Props) => {
|
||||
);
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}, []);
|
||||
}
|
||||
|
||||
const itemContextMenu = useCallback(async (event: any) => {
|
||||
async itemContextMenu(event: any) {
|
||||
const itemId = event.currentTarget.getAttribute('data-id');
|
||||
if (itemId === Folder.conflictFolderId()) return;
|
||||
|
||||
@@ -301,7 +265,7 @@ const SidebarComponent = (props: Props) => {
|
||||
|
||||
let item = null;
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
item = BaseModel.byId(props.folders, itemId);
|
||||
item = BaseModel.byId(this.props.folders, itemId);
|
||||
}
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
@@ -325,7 +289,7 @@ const SidebarComponent = (props: Props) => {
|
||||
} else if (itemType === BaseModel.TYPE_TAG) {
|
||||
await Tag.untagAll(itemId);
|
||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||
props.dispatch({
|
||||
this.props.dispatch({
|
||||
type: 'SEARCH_DELETE',
|
||||
id: itemId,
|
||||
});
|
||||
@@ -350,7 +314,7 @@ const SidebarComponent = (props: Props) => {
|
||||
new MenuItem({
|
||||
label: module.fullLabel(),
|
||||
click: async () => {
|
||||
await InteropServiceHelper.export(props.dispatch, module, { sourceFolderIds: [itemId], plugins: pluginsRef.current });
|
||||
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId], plugins: this.pluginsRef.current });
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -410,7 +374,7 @@ const SidebarComponent = (props: Props) => {
|
||||
);
|
||||
}
|
||||
|
||||
const pluginViews = pluginUtils.viewsByType(pluginsRef.current, 'menuItem');
|
||||
const pluginViews = pluginUtils.viewsByType(this.pluginsRef.current, 'menuItem');
|
||||
|
||||
for (const view of pluginViews) {
|
||||
const location = view.location;
|
||||
@@ -425,79 +389,80 @@ const SidebarComponent = (props: Props) => {
|
||||
}
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}, [props.folders, props.dispatch, pluginsRef]);
|
||||
}
|
||||
|
||||
const folderItem_click = useCallback((folderId: string) => {
|
||||
props.dispatch({
|
||||
folderItem_click(folderId: string) {
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folderId ? folderId : null,
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
}
|
||||
|
||||
const tagItem_click = useCallback((tag: any) => {
|
||||
props.dispatch({
|
||||
tagItem_click(tag: any) {
|
||||
this.props.dispatch({
|
||||
type: 'TAG_SELECT',
|
||||
id: tag ? tag.id : null,
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
}
|
||||
|
||||
const onHeaderClick_ = useCallback((key: string) => {
|
||||
const isExpanded = key === 'tag' ? props.tagHeaderIsExpanded : props.folderHeaderIsExpanded;
|
||||
Setting.setValue(key === 'tag' ? 'tagHeaderIsExpanded' : 'folderHeaderIsExpanded', !isExpanded);
|
||||
}, [props.folderHeaderIsExpanded, props.tagHeaderIsExpanded]);
|
||||
anchorItemRef(type: string, id: string) {
|
||||
if (!this.anchorItemRefs[type]) this.anchorItemRefs[type] = {};
|
||||
if (this.anchorItemRefs[type][id]) return this.anchorItemRefs[type][id];
|
||||
this.anchorItemRefs[type][id] = React.createRef();
|
||||
return this.anchorItemRefs[type][id];
|
||||
}
|
||||
|
||||
const onAllNotesClick_ = () => {
|
||||
props.dispatch({
|
||||
type: 'SMART_FILTER_SELECT',
|
||||
id: ALL_NOTES_FILTER_ID,
|
||||
});
|
||||
};
|
||||
firstAnchorItemRef(type: string) {
|
||||
const refs = this.anchorItemRefs[type];
|
||||
if (!refs) return null;
|
||||
|
||||
const anchorItemRef = (type: string, id: string) => {
|
||||
if (!anchorItemRefs.current[type]) anchorItemRefs.current[type] = {};
|
||||
if (anchorItemRefs.current[type][id]) return anchorItemRefs.current[type][id];
|
||||
anchorItemRefs.current[type][id] = React.createRef();
|
||||
return anchorItemRefs.current[type][id];
|
||||
};
|
||||
const n = `${type}s`;
|
||||
const p = this.props as any;
|
||||
const item = p[n] && p[n].length ? p[n][0] : null;
|
||||
if (!item) return null;
|
||||
|
||||
const renderNoteCount = (count: number) => {
|
||||
return refs[item.id];
|
||||
}
|
||||
|
||||
renderNoteCount(count: number) {
|
||||
return count ? <StyledNoteCount className="note-count-label">{count}</StyledNoteCount> : null;
|
||||
};
|
||||
}
|
||||
|
||||
const renderExpandIcon = (theme: any, isExpanded: boolean, isVisible: boolean) => {
|
||||
renderExpandIcon(isExpanded: boolean, isVisible: boolean = true) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style: any = { width: 16, maxWidth: 16, opacity: 0.5, fontSize: Math.round(theme.toolbarIconSize * 0.8), display: 'flex', justifyContent: 'center' };
|
||||
if (!isVisible) style.visibility = 'hidden';
|
||||
return <i className={isExpanded ? 'fas fa-caret-down' : 'fas fa-caret-right'} style={style}></i>;
|
||||
};
|
||||
}
|
||||
|
||||
const renderAllNotesItem = (theme: Theme, selected: boolean) => {
|
||||
renderAllNotesItem(selected: boolean) {
|
||||
return (
|
||||
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0 all-notes'} isSpecialItem={true}>
|
||||
<StyledExpandLink>{renderExpandIcon(theme, false, false)}</StyledExpandLink>
|
||||
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
|
||||
<StyledAllNotesIcon className="icon-notes"/>
|
||||
<StyledListItemAnchor
|
||||
className="list-item"
|
||||
isSpecialItem={true}
|
||||
href="#"
|
||||
selected={selected}
|
||||
onClick={onAllNotesClick_}
|
||||
onClick={this.onAllNotesClick_}
|
||||
>
|
||||
{_('All notes')}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const renderFolderItem = (folder: FolderEntity, selected: boolean, hasChildren: boolean, depth: number) =>{
|
||||
const anchorRef = anchorItemRef('folder', folder.id);
|
||||
const isExpanded = props.collapsedFolderIds.indexOf(folder.id) < 0;
|
||||
renderFolderItem(folder: FolderEntity, selected: boolean, hasChildren: boolean, depth: number) {
|
||||
const anchorRef = this.anchorItemRef('folder', folder.id);
|
||||
const isExpanded = this.props.collapsedFolderIds.indexOf(folder.id) < 0;
|
||||
let noteCount = (folder as any).note_count;
|
||||
|
||||
// Thunderbird count: Subtract children note_count from parent folder if it expanded.
|
||||
if (isExpanded) {
|
||||
for (let i = 0; i < props.folders.length; i++) {
|
||||
if (props.folders[i].parent_id === folder.id) {
|
||||
noteCount -= props.folders[i].note_count;
|
||||
for (let i = 0; i < this.props.folders.length; i++) {
|
||||
if (this.props.folders[i].parent_id === folder.id) {
|
||||
noteCount -= this.props.folders[i].note_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,41 +472,40 @@ const SidebarComponent = (props: Props) => {
|
||||
folderId={folder.id}
|
||||
folderTitle={Folder.displayTitle(folder)}
|
||||
folderIcon={Folder.unserializeIcon(folder.icon)}
|
||||
themeId={props.themeId}
|
||||
themeId={this.props.themeId}
|
||||
depth={depth}
|
||||
selected={selected}
|
||||
isExpanded={isExpanded}
|
||||
hasChildren={hasChildren}
|
||||
anchorRef={anchorRef}
|
||||
noteCount={noteCount}
|
||||
onFolderDragStart_={onFolderDragStart_}
|
||||
onFolderDragOver_={onFolderDragOver_}
|
||||
onFolderDrop_={onFolderDrop_}
|
||||
itemContextMenu={itemContextMenu}
|
||||
folderItem_click={folderItem_click}
|
||||
onFolderToggleClick_={onFolderToggleClick_}
|
||||
onFolderDragStart_={this.onFolderDragStart_}
|
||||
onFolderDragOver_={this.onFolderDragOver_}
|
||||
onFolderDrop_={this.onFolderDrop_}
|
||||
itemContextMenu={this.itemContextMenu}
|
||||
folderItem_click={this.folderItem_click}
|
||||
onFolderToggleClick_={this.onFolderToggleClick_}
|
||||
shareId={folder.share_id}
|
||||
parentId={folder.parent_id}
|
||||
showFolderIcon={showFolderIcons}
|
||||
/>;
|
||||
};
|
||||
}
|
||||
|
||||
const renderTag = (tag: any, selected: boolean) => {
|
||||
const anchorRef = anchorItemRef('tag', tag.id);
|
||||
renderTag(tag: any, selected: boolean) {
|
||||
const anchorRef = this.anchorItemRef('tag', tag.id);
|
||||
let noteCount = null;
|
||||
if (Setting.value('showNoteCounts')) {
|
||||
if (Setting.value('showCompletedTodos')) noteCount = renderNoteCount(tag.note_count);
|
||||
else noteCount = renderNoteCount(tag.note_count - tag.todo_completed_count);
|
||||
if (Setting.value('showCompletedTodos')) noteCount = this.renderNoteCount(tag.note_count);
|
||||
else noteCount = this.renderNoteCount(tag.note_count - tag.todo_completed_count);
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledListItem selected={selected}
|
||||
className={`list-item-container ${selected ? 'selected' : ''}`}
|
||||
key={tag.id}
|
||||
onDrop={onTagDrop_}
|
||||
onDrop={this.onTagDrop_}
|
||||
data-tag-id={tag.id}
|
||||
>
|
||||
<StyledExpandLink>{renderExpandIcon(theme, false, false)}</StyledExpandLink>
|
||||
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
|
||||
<StyledListItemAnchor
|
||||
ref={anchorRef}
|
||||
className="list-item"
|
||||
@@ -549,9 +513,9 @@ const SidebarComponent = (props: Props) => {
|
||||
selected={selected}
|
||||
data-id={tag.id}
|
||||
data-type={BaseModel.TYPE_TAG}
|
||||
onContextMenu={itemContextMenu}
|
||||
onContextMenu={this.itemContextMenu}
|
||||
onClick={() => {
|
||||
tagItem_click(tag);
|
||||
this.tagItem_click(tag);
|
||||
}}
|
||||
>
|
||||
<span className="tag-label">{Tag.displayTitle(tag)}</span>
|
||||
@@ -559,12 +523,16 @@ const SidebarComponent = (props: Props) => {
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const renderHeader = (key: string, label: string, iconName: string, contextMenuHandler: Function = null, onPlusButtonClick: Function = null, extraProps: any = {}) => {
|
||||
makeDivider(key: string) {
|
||||
return <div style={{ height: 2, backgroundColor: 'blue' }} key={key} />;
|
||||
}
|
||||
|
||||
renderHeader(key: string, label: string, iconName: string, contextMenuHandler: Function = null, onPlusButtonClick: Function = null, extraProps: any = {}) {
|
||||
const headerClick = extraProps.onClick || null;
|
||||
delete extraProps.onClick;
|
||||
const ref = anchorItemRef('headers', key);
|
||||
const ref = this.anchorItemRef('headers', key);
|
||||
|
||||
return (
|
||||
<div key={key} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
@@ -577,7 +545,7 @@ const SidebarComponent = (props: Props) => {
|
||||
if (headerClick) {
|
||||
headerClick(key, event);
|
||||
}
|
||||
onHeaderClick_(key);
|
||||
this.onHeaderClick_(key);
|
||||
}}
|
||||
>
|
||||
<StyledHeaderIcon className={iconName}/>
|
||||
@@ -586,11 +554,21 @@ const SidebarComponent = (props: Props) => {
|
||||
{ onPlusButtonClick && <StyledAddButton onClick={onPlusButtonClick} iconName="fas fa-plus" level={ButtonLevel.SidebarSecondary}/> }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const onKeyDown = useCallback((event: any) => {
|
||||
selectedItem() {
|
||||
if (this.props.notesParentType === 'Folder' && this.props.selectedFolderId) {
|
||||
return { type: 'folder', id: this.props.selectedFolderId };
|
||||
} else if (this.props.notesParentType === 'Tag' && this.props.selectedTagId) {
|
||||
return { type: 'tag', id: this.props.selectedTagId };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onKeyDown(event: any) {
|
||||
const keyCode = event.keyCode;
|
||||
const selectedItem = getSelectedItem();
|
||||
const selectedItem = this.selectedItem();
|
||||
|
||||
if (keyCode === 40 || keyCode === 38) {
|
||||
// DOWN / UP
|
||||
@@ -598,14 +576,14 @@ const SidebarComponent = (props: Props) => {
|
||||
|
||||
const focusItems = [];
|
||||
|
||||
for (let i = 0; i < folderItemsOrder_.current.length; i++) {
|
||||
const id = folderItemsOrder_.current[i];
|
||||
focusItems.push({ id: id, ref: anchorItemRefs.current['folder'][id], type: 'folder' });
|
||||
for (let i = 0; i < this.folderItemsOrder_.length; i++) {
|
||||
const id = this.folderItemsOrder_[i];
|
||||
focusItems.push({ id: id, ref: this.anchorItemRefs['folder'][id], type: 'folder' });
|
||||
}
|
||||
|
||||
for (let i = 0; i < tagItemsOrder_.current.length; i++) {
|
||||
const id = tagItemsOrder_.current[i];
|
||||
focusItems.push({ id: id, ref: anchorItemRefs.current['tag'][id], type: 'tag' });
|
||||
for (let i = 0; i < this.tagItemsOrder_.length; i++) {
|
||||
const id = this.tagItemsOrder_[i];
|
||||
focusItems.push({ id: id, ref: this.anchorItemRefs['tag'][id], type: 'tag' });
|
||||
}
|
||||
|
||||
let currentIndex = 0;
|
||||
@@ -626,7 +604,7 @@ const SidebarComponent = (props: Props) => {
|
||||
|
||||
const actionName = `${focusItem.type.toUpperCase()}_SELECT`;
|
||||
|
||||
props.dispatch({
|
||||
this.props.dispatch({
|
||||
type: actionName,
|
||||
id: focusItem.id,
|
||||
});
|
||||
@@ -649,7 +627,7 @@ const SidebarComponent = (props: Props) => {
|
||||
// SPACE
|
||||
event.preventDefault();
|
||||
|
||||
props.dispatch({
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_TOGGLE',
|
||||
id: selectedItem.id,
|
||||
});
|
||||
@@ -659,9 +637,24 @@ const SidebarComponent = (props: Props) => {
|
||||
// Ctrl+A key
|
||||
event.preventDefault();
|
||||
}
|
||||
}, [getSelectedItem, props.dispatch]);
|
||||
}
|
||||
|
||||
const renderSynchronizeButton = (type: string) => {
|
||||
onHeaderClick_(key: string) {
|
||||
const toggleKey = `${key}IsExpanded`;
|
||||
const isExpanded = (this.state as any)[toggleKey];
|
||||
const newState: any = { [toggleKey]: !isExpanded };
|
||||
this.setState(newState);
|
||||
Setting.setValue(toggleKey, !isExpanded);
|
||||
}
|
||||
|
||||
onAllNotesClick_() {
|
||||
this.props.dispatch({
|
||||
type: 'SMART_FILTER_SELECT',
|
||||
id: ALL_NOTES_FILTER_ID,
|
||||
});
|
||||
}
|
||||
|
||||
renderSynchronizeButton(type: string) {
|
||||
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
|
||||
const iconAnimation = type !== 'sync' ? 'icon-infinite-rotation 1s linear infinite' : '';
|
||||
|
||||
@@ -677,98 +670,116 @@ const SidebarComponent = (props: Props) => {
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const onAddFolderButtonClick = useCallback(() => {
|
||||
onAddFolderButtonClick() {
|
||||
void CommandService.instance().execute('newFolder');
|
||||
}, []);
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const items = [];
|
||||
|
||||
items.push(
|
||||
renderHeader('folderHeader', _('Notebooks'), 'icon-notebooks', header_contextMenu, onAddFolderButtonClick, {
|
||||
onDrop: onFolderDrop_,
|
||||
['data-folder-id']: '',
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (props.folders.length) {
|
||||
const allNotesSelected = props.notesParentType === 'SmartFilter' && props.selectedSmartFilterId === ALL_NOTES_FILTER_ID;
|
||||
const result = shared.renderFolders(props, renderFolderItem);
|
||||
const folderItems = [renderAllNotesItem(theme, allNotesSelected)].concat(result.items);
|
||||
folderItemsOrder_.current = result.order;
|
||||
items.push(
|
||||
<div
|
||||
className={`folders ${props.folderHeaderIsExpanded ? 'expanded' : ''}`}
|
||||
key="folder_items"
|
||||
style={{ display: props.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}
|
||||
>
|
||||
{folderItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
items.push(
|
||||
renderHeader('tagHeader', _('Tags'), 'icon-tags', null, null, {
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
// componentDidUpdate(prevProps:any, prevState:any) {
|
||||
// for (const n in prevProps) {
|
||||
// if (prevProps[n] !== (this.props as any)[n]) {
|
||||
// console.info('CHANGED PROPS', n);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (props.tags.length) {
|
||||
const result = shared.renderTags(props, renderTag);
|
||||
const tagItems = result.items;
|
||||
tagItemsOrder_.current = result.order;
|
||||
// for (const n in prevState) {
|
||||
// if (prevState[n] !== (this.state as any)[n]) {
|
||||
// console.info('CHANGED STATE', n);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
render() {
|
||||
this.pluginsRef.current = this.props.plugins;
|
||||
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const items = [];
|
||||
|
||||
items.push(
|
||||
<div className="tags" key="tag_items" style={{ display: props.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
{tagItems}
|
||||
</div>
|
||||
this.renderHeader('folderHeader', _('Notebooks'), 'icon-notebooks', this.header_contextMenu, this.onAddFolderButtonClick, {
|
||||
onDrop: this.onFolderDrop_,
|
||||
['data-folder-id']: '',
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (this.props.folders.length) {
|
||||
const allNotesSelected = this.props.notesParentType === 'SmartFilter' && this.props.selectedSmartFilterId === ALL_NOTES_FILTER_ID;
|
||||
const result = shared.renderFolders(this.props, this.renderFolderItem.bind(this));
|
||||
const folderItems = [this.renderAllNotesItem(allNotesSelected)].concat(result.items);
|
||||
this.folderItemsOrder_ = result.order;
|
||||
items.push(
|
||||
<div
|
||||
className={`folders ${this.state.folderHeaderIsExpanded ? 'expanded' : ''}`}
|
||||
key="folder_items"
|
||||
style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}
|
||||
>
|
||||
{folderItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
items.push(
|
||||
this.renderHeader('tagHeader', _('Tags'), 'icon-tags', null, null, {
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (this.props.tags.length) {
|
||||
const result = shared.renderTags(this.props, this.renderTag.bind(this));
|
||||
const tagItems = result.items;
|
||||
this.tagItemsOrder_ = result.order;
|
||||
|
||||
items.push(
|
||||
<div className="tags" key="tag_items" style={{ display: this.state.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
{tagItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let decryptionReportText = '';
|
||||
if (this.props.decryptionWorker && this.props.decryptionWorker.state !== 'idle' && this.props.decryptionWorker.itemCount) {
|
||||
decryptionReportText = _('Decrypting items: %d/%d', this.props.decryptionWorker.itemIndex + 1, this.props.decryptionWorker.itemCount);
|
||||
}
|
||||
|
||||
let resourceFetcherText = '';
|
||||
if (this.props.resourceFetcher && this.props.resourceFetcher.toFetchCount) {
|
||||
resourceFetcherText = _('Fetching resources: %d/%d', this.props.resourceFetcher.fetchingCount, this.props.resourceFetcher.toFetchCount);
|
||||
}
|
||||
|
||||
const lines = Synchronizer.reportToLines(this.props.syncReport);
|
||||
if (resourceFetcherText) lines.push(resourceFetcherText);
|
||||
if (decryptionReportText) lines.push(decryptionReportText);
|
||||
const syncReportText = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
syncReportText.push(
|
||||
<StyledSyncReportText key={i}>
|
||||
{lines[i]}
|
||||
</StyledSyncReportText>
|
||||
);
|
||||
}
|
||||
|
||||
const syncButton = this.renderSynchronizeButton(this.props.syncStarted ? 'cancel' : 'sync');
|
||||
|
||||
const syncReportComp = !syncReportText.length ? null : (
|
||||
<StyledSyncReport key="sync_report">
|
||||
{syncReportText}
|
||||
</StyledSyncReport>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledRoot ref={this.rootRef} onKeyDown={this.onKeyDown} className="sidebar">
|
||||
<div style={{ flex: 1, overflowX: 'hidden', overflowY: 'auto' }}>{items}</div>
|
||||
<div style={{ flex: 0, padding: theme.mainPadding }}>
|
||||
{syncReportComp}
|
||||
{syncButton}
|
||||
</div>
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
|
||||
let decryptionReportText = '';
|
||||
if (props.decryptionWorker && props.decryptionWorker.state !== 'idle' && props.decryptionWorker.itemCount) {
|
||||
decryptionReportText = _('Decrypting items: %d/%d', props.decryptionWorker.itemIndex + 1, props.decryptionWorker.itemCount);
|
||||
}
|
||||
|
||||
let resourceFetcherText = '';
|
||||
if (props.resourceFetcher && props.resourceFetcher.toFetchCount) {
|
||||
resourceFetcherText = _('Fetching resources: %d/%d', props.resourceFetcher.fetchingCount, props.resourceFetcher.toFetchCount);
|
||||
}
|
||||
|
||||
const lines = Synchronizer.reportToLines(props.syncReport);
|
||||
if (resourceFetcherText) lines.push(resourceFetcherText);
|
||||
if (decryptionReportText) lines.push(decryptionReportText);
|
||||
const syncReportText = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
syncReportText.push(
|
||||
<StyledSyncReportText key={i}>
|
||||
{lines[i]}
|
||||
</StyledSyncReportText>
|
||||
);
|
||||
}
|
||||
|
||||
const syncButton = renderSynchronizeButton(props.syncStarted ? 'cancel' : 'sync');
|
||||
|
||||
const syncReportComp = !syncReportText.length ? null : (
|
||||
<StyledSyncReport key="sync_report">
|
||||
{syncReportText}
|
||||
</StyledSyncReport>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledRoot ref={rootRef} onKeyDown={onKeyDown} className="sidebar">
|
||||
<div style={{ flex: 1, overflowX: 'hidden', overflowY: 'auto' }}>{items}</div>
|
||||
<div style={{ flex: 0, padding: theme.mainPadding }}>
|
||||
{syncReportComp}
|
||||
{syncButton}
|
||||
</div>
|
||||
</StyledRoot>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
@@ -788,8 +799,6 @@ const mapStateToProps = (state: AppState) => {
|
||||
decryptionWorker: state.decryptionWorker,
|
||||
resourceFetcher: state.resourceFetcher,
|
||||
plugins: state.pluginService.plugins,
|
||||
tagHeaderIsExpanded: state.settings.tagHeaderIsExpanded,
|
||||
folderHeaderIsExpanded: state.settings.folderHeaderIsExpanded,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -9,24 +9,18 @@ export const declaration: CommandDeclaration = {
|
||||
parentLabel: () => _('Focus'),
|
||||
};
|
||||
|
||||
export interface RuntimeProps {
|
||||
getSelectedItem(): any;
|
||||
getFirstAnchorItemRef(type: string): any;
|
||||
anchorItemRefs: any;
|
||||
}
|
||||
|
||||
export const runtime = (props: RuntimeProps): CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext) => {
|
||||
const sidebarVisible = layoutItemProp((context.state as AppState).mainLayout, 'sideBar', 'visible');
|
||||
|
||||
if (sidebarVisible) {
|
||||
const item = props.getSelectedItem();
|
||||
const item = comp.selectedItem();
|
||||
if (item) {
|
||||
const anchorRef = props.anchorItemRefs.current[item.type][item.id];
|
||||
const anchorRef = comp.anchorItemRefs[item.type][item.id];
|
||||
if (anchorRef) anchorRef.current.focus();
|
||||
} else {
|
||||
const anchorRef = props.getFirstAnchorItemRef('folder');
|
||||
const anchorRef = comp.firstAnchorItemRef('folder');
|
||||
if (anchorRef) anchorRef.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
101
packages/app-desktop/gui/TableEditorDialog/Dialog.tsx
Normal file
101
packages/app-desktop/gui/TableEditorDialog/Dialog.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as React from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import DialogButtonRow, { ClickEvent } from '../DialogButtonRow';
|
||||
import Dialog from '../Dialog';
|
||||
import DialogTitle from '../DialogTitle';
|
||||
import { parseMarkdownTable } from '../../../lib/markdownUtils';
|
||||
import { Size } from '../ResizableLayout/utils/types';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
dispatch: Function;
|
||||
markdownTable: string;
|
||||
dialogContentMaxSize: Size;
|
||||
}
|
||||
|
||||
const markdownTableToObject = (markdownTable: string): any => {
|
||||
const table = parseMarkdownTable(markdownTable);
|
||||
|
||||
return {
|
||||
columns: table.headers.map(h => {
|
||||
return {
|
||||
title: h.label,
|
||||
field: h.name,
|
||||
hozAlign: h.justify,
|
||||
editor: 'input',
|
||||
};
|
||||
}),
|
||||
|
||||
data: table.rows.map(row => {
|
||||
return {
|
||||
...row,
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export default function(props: Props) {
|
||||
const elementId = `tabulator_${Math.floor(Math.random() * 1000000)}`;
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
props.dispatch({
|
||||
type: 'DIALOG_CLOSE',
|
||||
name: 'tableEditor',
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
|
||||
const onButtonRowClick = useCallback(async (event: ClickEvent) => {
|
||||
if (event.buttonName === 'cancel') {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.buttonName === 'ok') {
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
useEffect(() => {
|
||||
const table = markdownTableToObject(props.markdownTable);
|
||||
const Tabulator = (window as any).Tabulator;
|
||||
|
||||
// TODO: probably doesn't need to be called every time
|
||||
// TODO: Load CSS/JS dynamically?
|
||||
// TODO: Clean up on exit
|
||||
Tabulator.extendModule('edit', 'editors', {});
|
||||
|
||||
new Tabulator(`#${elementId}`, {
|
||||
...table,
|
||||
height: props.dialogContentMaxSize.height,
|
||||
});
|
||||
}, []);
|
||||
|
||||
function renderContent() {
|
||||
return (
|
||||
<div className="dialog-content">
|
||||
<div id={elementId}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderDialogWrapper() {
|
||||
return (
|
||||
<div className="dialog-root">
|
||||
<DialogTitle title={_('Edit table')}/>
|
||||
{renderContent()}
|
||||
<DialogButtonRow
|
||||
themeId={props.themeId}
|
||||
onClick={onButtonRowClick}
|
||||
okButtonLabel={_('Save')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog onClose={onClose} renderContent={renderDialogWrapper}/>
|
||||
);
|
||||
}
|
||||
@@ -23,6 +23,5 @@ export default function useEffectDebugger(effectHook: any, dependencies: any, de
|
||||
console.log('[use-effet-debugger] ', changedDeps);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
useEffect(effectHook, dependencies);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,5 @@ export default function useImperativeHandleDebugger(ref: any, effectHook: any, d
|
||||
console.log('[use-imperativeHandler-debugger] ', changedDeps);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
useImperativeHandle(ref, effectHook, dependencies);
|
||||
}
|
||||
|
||||
@@ -579,17 +579,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
ipc.textSelected = function(event) {
|
||||
ipcProxySendToHost('contextMenu', {
|
||||
type: 'text',
|
||||
textToCopy: event.text,
|
||||
});
|
||||
}
|
||||
|
||||
ipc.openPdfViewer = function(event) {
|
||||
ipcProxySendToHost('openPdfViewer', { resourceId: event.resourceId, mime: 'application/pdf', pageNo: event.pageNo || 1 });
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', webviewLib.logEnabledEventHandler(e => {
|
||||
if (!window.location.hash) return;
|
||||
|
||||
@@ -664,17 +653,6 @@
|
||||
e.preventDefault();
|
||||
}));
|
||||
|
||||
document.addEventListener('click', webviewLib.logEnabledEventHandler(e => {
|
||||
document.querySelectorAll('.media-pdf').forEach(element => {
|
||||
if(!!element.contentWindow){
|
||||
element.contentWindow.postMessage({
|
||||
type: 'blur'
|
||||
}, '*');
|
||||
}
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
let lastClientWidth_ = NaN, lastClientHeight_ = NaN, lastScrollTop_ = NaN;
|
||||
|
||||
window.addEventListener('resize', webviewLib.logEnabledEventHandler(() => {
|
||||
|
||||
@@ -40,7 +40,7 @@ scrollmap.get_ = () => {
|
||||
// embedded into elements by the renderer.
|
||||
// See also renderer/MdToHtml/rules/source_map.ts.
|
||||
const elems = document.getElementsByClassName('maps-to-line');
|
||||
if (elems.length === 0) return null;
|
||||
if (elems.length == 0) return null;
|
||||
const map = { line: [0], percent: [0], viewHeight: height, lineCount: 0 };
|
||||
// Each map entry is total-ordered.
|
||||
let last = 0;
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
uses 'eval'.
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval'">
|
||||
-->
|
||||
|
||||
<!--
|
||||
To add files below, then need to be in the "vendor" directory. To make this happen, use copyApplicationAssets.js
|
||||
-->
|
||||
<title>Joplin</title>
|
||||
<link rel="stylesheet" href="style.min.css">
|
||||
<link rel="stylesheet" href="style/icons/style.css">
|
||||
@@ -15,6 +19,9 @@
|
||||
<link rel="stylesheet" href="vendor/lib/smalltalk/css/smalltalk.css">
|
||||
<link rel="stylesheet" href="vendor/lib/roboto-fontface/css/roboto/roboto-fontface.css">
|
||||
<link rel="stylesheet" href="vendor/lib/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="vendor/lib/tabulator-tables/dist/css/tabulator.min.css">
|
||||
|
||||
<script type="text/javascript" src="vendor/lib/tabulator-tables/dist/js/tabulator.min.js"></script>
|
||||
|
||||
<style>
|
||||
.smalltalk {
|
||||
|
||||
@@ -26,7 +26,7 @@ const shim = require('@joplin/lib/shim').default;
|
||||
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const bridge = require('@electron/remote').require('./bridge').default;
|
||||
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
|
||||
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local');
|
||||
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local.js');
|
||||
const React = require('react');
|
||||
const nodeSqlite = require('sqlite3');
|
||||
|
||||
@@ -131,7 +131,7 @@ app().start(bridge().processArgv()).then((result) => {
|
||||
}).catch((error) => {
|
||||
const env = bridge().env();
|
||||
|
||||
if (error.code === 'flagError') {
|
||||
if (error.code == 'flagError') {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
} else {
|
||||
// If something goes wrong at this stage we don't have a console or a log file
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.9.6",
|
||||
"version": "2.8.8",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
@@ -31,8 +31,7 @@
|
||||
"afterSign": "./tools/notarizeMacApp.js",
|
||||
"extraResources": [
|
||||
"build/icons/**",
|
||||
"build/images/**",
|
||||
"build/defaultPlugins/**"
|
||||
"build/images/**"
|
||||
],
|
||||
"afterAllArtifactBuild": "./generateSha512.js",
|
||||
"asar": true,
|
||||
@@ -106,7 +105,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/laurent22/joplin#readme",
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.9",
|
||||
"@joplin/tools": "~2.8",
|
||||
"@testing-library/react-hooks": "^3.4.2",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^14.14.6",
|
||||
@@ -138,9 +137,8 @@
|
||||
"@electron/remote": "^2.0.1",
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"@joeattardi/emoji-button": "^4.6.0",
|
||||
"@joplin/lib": "~2.9",
|
||||
"@joplin/pdf-viewer": "~2.9",
|
||||
"@joplin/renderer": "~2.9",
|
||||
"@joplin/lib": "~2.8",
|
||||
"@joplin/renderer": "~2.8",
|
||||
"async-mutex": "^0.1.3",
|
||||
"codemirror": "^5.56.0",
|
||||
"color": "^3.1.2",
|
||||
@@ -176,6 +174,7 @@
|
||||
"styled-components": "5.1.1",
|
||||
"styled-system": "5.1.5",
|
||||
"taboverride": "^4.0.3",
|
||||
"tabulator-tables": "^5.1.4",
|
||||
"tinymce": "^5.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
modalLayer_onClick(event: any) {
|
||||
if (event.currentTarget === event.target) {
|
||||
if (event.currentTarget == event.target) {
|
||||
this.props.dispatch({
|
||||
pluginName: PLUGIN_NAME,
|
||||
type: 'PLUGINLEGACY_DIALOG_SET',
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import bridge from '../bridge';
|
||||
import { Implementation as WindowImplementation } from '@joplin/lib/services/plugins/api/JoplinWindow';
|
||||
import { injectCustomStyles } from '@joplin/lib/CssUtils';
|
||||
import { VersionInfo } from '@joplin/lib/services/plugins/api/types';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import BasePlatformImplementation, { Joplin } from '@joplin/lib/services/plugins/BasePlatformImplementation';
|
||||
const { clipboard, nativeImage } = require('electron');
|
||||
const packageInfo = require('../../packageInfo');
|
||||
|
||||
interface JoplinViewsDialogs {
|
||||
showMessageBox(message: string): Promise<number>;
|
||||
}
|
||||
|
||||
interface JoplinViews {
|
||||
dialogs: JoplinViewsDialogs;
|
||||
}
|
||||
|
||||
interface Joplin {
|
||||
views: JoplinViews;
|
||||
}
|
||||
|
||||
interface Components {
|
||||
[key: string]: any;
|
||||
@@ -15,7 +22,7 @@ interface Components {
|
||||
// PlatformImplementation provides access to platform specific dependencies,
|
||||
// such as the clipboard, message dialog, etc. It allows having the same plugin
|
||||
// API for all platforms, but with different implementations.
|
||||
export default class PlatformImplementation extends BasePlatformImplementation {
|
||||
export default class PlatformImplementation {
|
||||
|
||||
private static instance_: PlatformImplementation;
|
||||
private joplin_: Joplin;
|
||||
@@ -26,14 +33,6 @@ export default class PlatformImplementation extends BasePlatformImplementation {
|
||||
return this.instance_;
|
||||
}
|
||||
|
||||
public get versionInfo(): VersionInfo {
|
||||
return {
|
||||
version: packageInfo.version,
|
||||
syncVersion: Setting.value('syncVersion'),
|
||||
profileVersion: reg.db().version(),
|
||||
};
|
||||
}
|
||||
|
||||
public get clipboard() {
|
||||
return clipboard;
|
||||
}
|
||||
@@ -49,8 +48,6 @@ export default class PlatformImplementation extends BasePlatformImplementation {
|
||||
}
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.components_ = {};
|
||||
|
||||
this.joplin_ = {
|
||||
|
||||
@@ -69,7 +69,6 @@ function UserWebview(props: Props, ref: any) {
|
||||
|
||||
useEffect(() => {
|
||||
if (isReady && props.onReady) props.onReady();
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [isReady]);
|
||||
|
||||
function frameWindow() {
|
||||
|
||||
@@ -33,7 +33,6 @@ export default function(frameWindow: any, htmlHash: string, minWidth: number, mi
|
||||
|
||||
useEffect(() => {
|
||||
updateContentSize(htmlHash);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [htmlHash]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -56,7 +55,6 @@ export default function(frameWindow: any, htmlHash: string, minWidth: number, mi
|
||||
return () => {
|
||||
clearInterval(updateFrameSizeIID);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [fitToContent, isReady, minWidth, minHeight, htmlHash]);
|
||||
|
||||
return contentSize;
|
||||
|
||||
@@ -45,7 +45,6 @@ export default function(frameWindow: any, isReady: boolean, postMessage: Functio
|
||||
hash: htmlHash,
|
||||
html: html,
|
||||
});
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [html, htmlHash, isReady]);
|
||||
|
||||
return loadedHtmlHash;
|
||||
|
||||
@@ -4,12 +4,10 @@ export default function(postMessage: Function, isReady: boolean, scripts: string
|
||||
useEffect(() => {
|
||||
if (!isReady) return;
|
||||
postMessage('setScripts', { scripts: scripts });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [scripts, isReady]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady || !cssFilePath) return;
|
||||
postMessage('setScript', { script: cssFilePath, key: 'themeCss' });
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [isReady, cssFilePath]);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user