1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-01-17 00:33:59 +02:00

Compare commits

..

4 Commits

Author SHA1 Message Date
Laurent Cozic
23d9ab7a3a update 2021-08-30 12:55:05 +01:00
Laurent Cozic
53f7f3c738 Merge branch 'dev' into master_password 2021-08-29 18:31:41 +01:00
Laurent Cozic
66123fcabb update 2021-08-29 17:58:54 +01:00
Laurent Cozic
cb4a027975 update 2021-08-29 13:12:03 +01:00
127 changed files with 911 additions and 2425 deletions

View File

@@ -64,7 +64,6 @@ packages/tools/PortableAppsLauncher
packages/fork-*
plugin_types/
readme/
**/commands/index.ts
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-cli/app/LinkSelector.d.ts
@@ -118,12 +117,6 @@ packages/app-desktop/InteropServiceHelper.js.map
packages/app-desktop/app.d.ts
packages/app-desktop/app.js
packages/app-desktop/app.js.map
packages/app-desktop/app.reducer.d.ts
packages/app-desktop/app.reducer.js
packages/app-desktop/app.reducer.js.map
packages/app-desktop/app.reducer.test.d.ts
packages/app-desktop/app.reducer.test.js
packages/app-desktop/app.reducer.test.js.map
packages/app-desktop/bridge.d.ts
packages/app-desktop/bridge.js
packages/app-desktop/bridge.js.map
@@ -142,9 +135,6 @@ packages/app-desktop/commands/exportNotes.js.map
packages/app-desktop/commands/focusElement.d.ts
packages/app-desktop/commands/focusElement.js
packages/app-desktop/commands/focusElement.js.map
packages/app-desktop/commands/index.d.ts
packages/app-desktop/commands/index.js
packages/app-desktop/commands/index.js.map
packages/app-desktop/commands/openProfileDirectory.d.ts
packages/app-desktop/commands/openProfileDirectory.js
packages/app-desktop/commands/openProfileDirectory.js.map
@@ -250,9 +240,6 @@ packages/app-desktop/gui/MainScreen/commands/gotoAnything.js.map
packages/app-desktop/gui/MainScreen/commands/hideModalMessage.d.ts
packages/app-desktop/gui/MainScreen/commands/hideModalMessage.js
packages/app-desktop/gui/MainScreen/commands/hideModalMessage.js.map
packages/app-desktop/gui/MainScreen/commands/index.d.ts
packages/app-desktop/gui/MainScreen/commands/index.js
packages/app-desktop/gui/MainScreen/commands/index.js.map
packages/app-desktop/gui/MainScreen/commands/moveToFolder.d.ts
packages/app-desktop/gui/MainScreen/commands/moveToFolder.js
packages/app-desktop/gui/MainScreen/commands/moveToFolder.js.map
@@ -409,24 +396,21 @@ packages/app-desktop/gui/NoteEditor/NoteEditor.js.map
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.d.ts
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js.map
packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.d.ts
packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js
packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js.map
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.d.ts
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js.map
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.d.ts
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js.map
packages/app-desktop/gui/NoteEditor/commands/index.d.ts
packages/app-desktop/gui/NoteEditor/commands/index.js
packages/app-desktop/gui/NoteEditor/commands/index.js.map
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.d.ts
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js.map
packages/app-desktop/gui/NoteEditor/commands/showRevisions.d.ts
packages/app-desktop/gui/NoteEditor/commands/showRevisions.js
packages/app-desktop/gui/NoteEditor/commands/showRevisions.js.map
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.d.ts
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.js
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.js.map
packages/app-desktop/gui/NoteEditor/styles/index.d.ts
packages/app-desktop/gui/NoteEditor/styles/index.js
packages/app-desktop/gui/NoteEditor/styles/index.js.map
@@ -481,18 +465,12 @@ packages/app-desktop/gui/NoteList/NoteList.js.map
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.d.ts
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js.map
packages/app-desktop/gui/NoteList/commands/index.d.ts
packages/app-desktop/gui/NoteList/commands/index.js
packages/app-desktop/gui/NoteList/commands/index.js.map
packages/app-desktop/gui/NoteListControls/NoteListControls.d.ts
packages/app-desktop/gui/NoteListControls/NoteListControls.js
packages/app-desktop/gui/NoteListControls/NoteListControls.js.map
packages/app-desktop/gui/NoteListControls/commands/focusSearch.d.ts
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map
packages/app-desktop/gui/NoteListControls/commands/index.d.ts
packages/app-desktop/gui/NoteListControls/commands/index.js
packages/app-desktop/gui/NoteListControls/commands/index.js.map
packages/app-desktop/gui/NoteListItem.d.ts
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListItem.js.map
@@ -589,18 +567,12 @@ packages/app-desktop/gui/Sidebar/Sidebar.js.map
packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.d.ts
packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.js
packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.js.map
packages/app-desktop/gui/Sidebar/commands/index.d.ts
packages/app-desktop/gui/Sidebar/commands/index.js
packages/app-desktop/gui/Sidebar/commands/index.js.map
packages/app-desktop/gui/Sidebar/styles/index.d.ts
packages/app-desktop/gui/Sidebar/styles/index.js
packages/app-desktop/gui/Sidebar/styles/index.js.map
packages/app-desktop/gui/StatusScreen/StatusScreen.d.ts
packages/app-desktop/gui/StatusScreen/StatusScreen.js
packages/app-desktop/gui/StatusScreen/StatusScreen.js.map
packages/app-desktop/gui/StyleSheets/StyleSheetContainer.d.ts
packages/app-desktop/gui/StyleSheets/StyleSheetContainer.js
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
@@ -889,9 +861,6 @@ packages/lib/JoplinServerApi.js.map
packages/lib/Logger.d.ts
packages/lib/Logger.js
packages/lib/Logger.js.map
packages/lib/ObjectUtils.d.ts
packages/lib/ObjectUtils.js
packages/lib/ObjectUtils.js.map
packages/lib/PoorManIntervals.d.ts
packages/lib/PoorManIntervals.js
packages/lib/PoorManIntervals.js.map
@@ -925,9 +894,6 @@ packages/lib/commands/historyBackward.js.map
packages/lib/commands/historyForward.d.ts
packages/lib/commands/historyForward.js
packages/lib/commands/historyForward.js.map
packages/lib/commands/index.d.ts
packages/lib/commands/index.js
packages/lib/commands/index.js.map
packages/lib/commands/synchronize.d.ts
packages/lib/commands/synchronize.js
packages/lib/commands/synchronize.js.map
@@ -970,9 +936,6 @@ packages/lib/fs-driver-node.js.map
packages/lib/fsDriver.test.d.ts
packages/lib/fsDriver.test.js
packages/lib/fsDriver.test.js.map
packages/lib/hooks/useAsyncEffect.d.ts
packages/lib/hooks/useAsyncEffect.js
packages/lib/hooks/useAsyncEffect.js.map
packages/lib/hooks/useElementSize.d.ts
packages/lib/hooks/useElementSize.js
packages/lib/hooks/useElementSize.js.map
@@ -1027,9 +990,6 @@ packages/lib/models/Folder.test.js.map
packages/lib/models/ItemChange.d.ts
packages/lib/models/ItemChange.js
packages/lib/models/ItemChange.js.map
packages/lib/models/ItemChange.test.d.ts
packages/lib/models/ItemChange.test.js
packages/lib/models/ItemChange.test.js.map
packages/lib/models/MasterKey.d.ts
packages/lib/models/MasterKey.js
packages/lib/models/MasterKey.js.map
@@ -1444,12 +1404,6 @@ packages/lib/services/rest/actionApi.desktop.js.map
packages/lib/services/rest/routes/auth.d.ts
packages/lib/services/rest/routes/auth.js
packages/lib/services/rest/routes/auth.js.map
packages/lib/services/rest/routes/events.d.ts
packages/lib/services/rest/routes/events.js
packages/lib/services/rest/routes/events.js.map
packages/lib/services/rest/routes/events.test.d.ts
packages/lib/services/rest/routes/events.test.js
packages/lib/services/rest/routes/events.test.js.map
packages/lib/services/rest/routes/folders.d.ts
packages/lib/services/rest/routes/folders.js
packages/lib/services/rest/routes/folders.js.map
@@ -1531,21 +1485,6 @@ packages/lib/services/spellChecker/SpellCheckerService.js.map
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.d.ts
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js.map
packages/lib/services/style/cssToTheme.d.ts
packages/lib/services/style/cssToTheme.js
packages/lib/services/style/cssToTheme.js.map
packages/lib/services/style/cssToTheme.test.d.ts
packages/lib/services/style/cssToTheme.test.js
packages/lib/services/style/cssToTheme.test.js.map
packages/lib/services/style/loadCssToTheme.d.ts
packages/lib/services/style/loadCssToTheme.js
packages/lib/services/style/loadCssToTheme.js.map
packages/lib/services/style/themeToCss.d.ts
packages/lib/services/style/themeToCss.js
packages/lib/services/style/themeToCss.js.map
packages/lib/services/style/themeToCss.test.d.ts
packages/lib/services/style/themeToCss.test.js
packages/lib/services/style/themeToCss.test.js.map
packages/lib/services/synchronizer/ItemUploader.d.ts
packages/lib/services/synchronizer/ItemUploader.js
packages/lib/services/synchronizer/ItemUploader.js.map
@@ -1696,12 +1635,6 @@ packages/plugin-repo-cli/lib/gitCompareUrl.js.map
packages/plugin-repo-cli/lib/gitCompareUrl.test.d.ts
packages/plugin-repo-cli/lib/gitCompareUrl.test.js
packages/plugin-repo-cli/lib/gitCompareUrl.test.js.map
packages/plugin-repo-cli/lib/overrideUtils.d.ts
packages/plugin-repo-cli/lib/overrideUtils.js
packages/plugin-repo-cli/lib/overrideUtils.js.map
packages/plugin-repo-cli/lib/overrideUtils.test.d.ts
packages/plugin-repo-cli/lib/overrideUtils.test.js
packages/plugin-repo-cli/lib/overrideUtils.test.js.map
packages/plugin-repo-cli/lib/types.d.ts
packages/plugin-repo-cli/lib/types.js
packages/plugin-repo-cli/lib/types.js.map
@@ -1711,9 +1644,6 @@ packages/plugin-repo-cli/lib/updateReadme.js.map
packages/plugin-repo-cli/lib/updateReadme.test.d.ts
packages/plugin-repo-cli/lib/updateReadme.test.js
packages/plugin-repo-cli/lib/updateReadme.test.js.map
packages/plugin-repo-cli/lib/utils.d.ts
packages/plugin-repo-cli/lib/utils.js
packages/plugin-repo-cli/lib/utils.js.map
packages/plugins/ToggleSidebars/api/index.d.ts
packages/plugins/ToggleSidebars/api/index.js
packages/plugins/ToggleSidebars/api/index.js.map
@@ -1786,9 +1716,6 @@ packages/renderer/MdToHtml/setupLinkify.js.map
packages/renderer/MdToHtml/validateLinks.d.ts
packages/renderer/MdToHtml/validateLinks.js
packages/renderer/MdToHtml/validateLinks.js.map
packages/renderer/headerAnchor.d.ts
packages/renderer/headerAnchor.js
packages/renderer/headerAnchor.js.map
packages/renderer/htmlUtils.d.ts
packages/renderer/htmlUtils.js
packages/renderer/htmlUtils.js.map
@@ -1807,9 +1734,6 @@ packages/renderer/utils.js.map
packages/tools/buildServerDocker.d.ts
packages/tools/buildServerDocker.js
packages/tools/buildServerDocker.js.map
packages/tools/convertThemesToCss.d.ts
packages/tools/convertThemesToCss.js
packages/tools/convertThemesToCss.js.map
packages/tools/generate-database-types.d.ts
packages/tools/generate-database-types.js
packages/tools/generate-database-types.js.map

View File

@@ -35,14 +35,6 @@ jobs:
sudo apt-get update || true
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# the next line enables multi-architecture support for docker, it basically makes it use qemu for non native platforms
# See https://hub.docker.com/r/tonistiigi/binfmt for more info
docker run --privileged --rm tonistiigi/binfmt --install all
# this just prints the info about what platforms are supported in the builder (can help debugging if something isn't working right)
# and also proves the above worked properly
sudo docker buildx ls
- uses: actions/checkout@v2
- uses: olegtarasov/get-tag@v2.1
- uses: actions/setup-node@v2

82
.gitignore vendored
View File

@@ -49,7 +49,6 @@ packages/tools/commit_hook.txt
packages/tools/github_oauth_token.txt
lerna-debug.log
.env
docs/**/*.mustache
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-cli/app/LinkSelector.d.ts
@@ -103,12 +102,6 @@ packages/app-desktop/InteropServiceHelper.js.map
packages/app-desktop/app.d.ts
packages/app-desktop/app.js
packages/app-desktop/app.js.map
packages/app-desktop/app.reducer.d.ts
packages/app-desktop/app.reducer.js
packages/app-desktop/app.reducer.js.map
packages/app-desktop/app.reducer.test.d.ts
packages/app-desktop/app.reducer.test.js
packages/app-desktop/app.reducer.test.js.map
packages/app-desktop/bridge.d.ts
packages/app-desktop/bridge.js
packages/app-desktop/bridge.js.map
@@ -127,9 +120,6 @@ packages/app-desktop/commands/exportNotes.js.map
packages/app-desktop/commands/focusElement.d.ts
packages/app-desktop/commands/focusElement.js
packages/app-desktop/commands/focusElement.js.map
packages/app-desktop/commands/index.d.ts
packages/app-desktop/commands/index.js
packages/app-desktop/commands/index.js.map
packages/app-desktop/commands/openProfileDirectory.d.ts
packages/app-desktop/commands/openProfileDirectory.js
packages/app-desktop/commands/openProfileDirectory.js.map
@@ -235,9 +225,6 @@ packages/app-desktop/gui/MainScreen/commands/gotoAnything.js.map
packages/app-desktop/gui/MainScreen/commands/hideModalMessage.d.ts
packages/app-desktop/gui/MainScreen/commands/hideModalMessage.js
packages/app-desktop/gui/MainScreen/commands/hideModalMessage.js.map
packages/app-desktop/gui/MainScreen/commands/index.d.ts
packages/app-desktop/gui/MainScreen/commands/index.js
packages/app-desktop/gui/MainScreen/commands/index.js.map
packages/app-desktop/gui/MainScreen/commands/moveToFolder.d.ts
packages/app-desktop/gui/MainScreen/commands/moveToFolder.js
packages/app-desktop/gui/MainScreen/commands/moveToFolder.js.map
@@ -394,24 +381,21 @@ packages/app-desktop/gui/NoteEditor/NoteEditor.js.map
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.d.ts
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js.map
packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.d.ts
packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js
packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js.map
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.d.ts
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js.map
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.d.ts
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js.map
packages/app-desktop/gui/NoteEditor/commands/index.d.ts
packages/app-desktop/gui/NoteEditor/commands/index.js
packages/app-desktop/gui/NoteEditor/commands/index.js.map
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.d.ts
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js.map
packages/app-desktop/gui/NoteEditor/commands/showRevisions.d.ts
packages/app-desktop/gui/NoteEditor/commands/showRevisions.js
packages/app-desktop/gui/NoteEditor/commands/showRevisions.js.map
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.d.ts
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.js
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.js.map
packages/app-desktop/gui/NoteEditor/styles/index.d.ts
packages/app-desktop/gui/NoteEditor/styles/index.js
packages/app-desktop/gui/NoteEditor/styles/index.js.map
@@ -466,18 +450,12 @@ packages/app-desktop/gui/NoteList/NoteList.js.map
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.d.ts
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js.map
packages/app-desktop/gui/NoteList/commands/index.d.ts
packages/app-desktop/gui/NoteList/commands/index.js
packages/app-desktop/gui/NoteList/commands/index.js.map
packages/app-desktop/gui/NoteListControls/NoteListControls.d.ts
packages/app-desktop/gui/NoteListControls/NoteListControls.js
packages/app-desktop/gui/NoteListControls/NoteListControls.js.map
packages/app-desktop/gui/NoteListControls/commands/focusSearch.d.ts
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map
packages/app-desktop/gui/NoteListControls/commands/index.d.ts
packages/app-desktop/gui/NoteListControls/commands/index.js
packages/app-desktop/gui/NoteListControls/commands/index.js.map
packages/app-desktop/gui/NoteListItem.d.ts
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListItem.js.map
@@ -574,18 +552,12 @@ packages/app-desktop/gui/Sidebar/Sidebar.js.map
packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.d.ts
packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.js
packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.js.map
packages/app-desktop/gui/Sidebar/commands/index.d.ts
packages/app-desktop/gui/Sidebar/commands/index.js
packages/app-desktop/gui/Sidebar/commands/index.js.map
packages/app-desktop/gui/Sidebar/styles/index.d.ts
packages/app-desktop/gui/Sidebar/styles/index.js
packages/app-desktop/gui/Sidebar/styles/index.js.map
packages/app-desktop/gui/StatusScreen/StatusScreen.d.ts
packages/app-desktop/gui/StatusScreen/StatusScreen.js
packages/app-desktop/gui/StatusScreen/StatusScreen.js.map
packages/app-desktop/gui/StyleSheets/StyleSheetContainer.d.ts
packages/app-desktop/gui/StyleSheets/StyleSheetContainer.js
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
@@ -874,9 +846,6 @@ packages/lib/JoplinServerApi.js.map
packages/lib/Logger.d.ts
packages/lib/Logger.js
packages/lib/Logger.js.map
packages/lib/ObjectUtils.d.ts
packages/lib/ObjectUtils.js
packages/lib/ObjectUtils.js.map
packages/lib/PoorManIntervals.d.ts
packages/lib/PoorManIntervals.js
packages/lib/PoorManIntervals.js.map
@@ -910,9 +879,6 @@ packages/lib/commands/historyBackward.js.map
packages/lib/commands/historyForward.d.ts
packages/lib/commands/historyForward.js
packages/lib/commands/historyForward.js.map
packages/lib/commands/index.d.ts
packages/lib/commands/index.js
packages/lib/commands/index.js.map
packages/lib/commands/synchronize.d.ts
packages/lib/commands/synchronize.js
packages/lib/commands/synchronize.js.map
@@ -955,9 +921,6 @@ packages/lib/fs-driver-node.js.map
packages/lib/fsDriver.test.d.ts
packages/lib/fsDriver.test.js
packages/lib/fsDriver.test.js.map
packages/lib/hooks/useAsyncEffect.d.ts
packages/lib/hooks/useAsyncEffect.js
packages/lib/hooks/useAsyncEffect.js.map
packages/lib/hooks/useElementSize.d.ts
packages/lib/hooks/useElementSize.js
packages/lib/hooks/useElementSize.js.map
@@ -1012,9 +975,6 @@ packages/lib/models/Folder.test.js.map
packages/lib/models/ItemChange.d.ts
packages/lib/models/ItemChange.js
packages/lib/models/ItemChange.js.map
packages/lib/models/ItemChange.test.d.ts
packages/lib/models/ItemChange.test.js
packages/lib/models/ItemChange.test.js.map
packages/lib/models/MasterKey.d.ts
packages/lib/models/MasterKey.js
packages/lib/models/MasterKey.js.map
@@ -1429,12 +1389,6 @@ packages/lib/services/rest/actionApi.desktop.js.map
packages/lib/services/rest/routes/auth.d.ts
packages/lib/services/rest/routes/auth.js
packages/lib/services/rest/routes/auth.js.map
packages/lib/services/rest/routes/events.d.ts
packages/lib/services/rest/routes/events.js
packages/lib/services/rest/routes/events.js.map
packages/lib/services/rest/routes/events.test.d.ts
packages/lib/services/rest/routes/events.test.js
packages/lib/services/rest/routes/events.test.js.map
packages/lib/services/rest/routes/folders.d.ts
packages/lib/services/rest/routes/folders.js
packages/lib/services/rest/routes/folders.js.map
@@ -1516,21 +1470,6 @@ packages/lib/services/spellChecker/SpellCheckerService.js.map
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.d.ts
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js.map
packages/lib/services/style/cssToTheme.d.ts
packages/lib/services/style/cssToTheme.js
packages/lib/services/style/cssToTheme.js.map
packages/lib/services/style/cssToTheme.test.d.ts
packages/lib/services/style/cssToTheme.test.js
packages/lib/services/style/cssToTheme.test.js.map
packages/lib/services/style/loadCssToTheme.d.ts
packages/lib/services/style/loadCssToTheme.js
packages/lib/services/style/loadCssToTheme.js.map
packages/lib/services/style/themeToCss.d.ts
packages/lib/services/style/themeToCss.js
packages/lib/services/style/themeToCss.js.map
packages/lib/services/style/themeToCss.test.d.ts
packages/lib/services/style/themeToCss.test.js
packages/lib/services/style/themeToCss.test.js.map
packages/lib/services/synchronizer/ItemUploader.d.ts
packages/lib/services/synchronizer/ItemUploader.js
packages/lib/services/synchronizer/ItemUploader.js.map
@@ -1681,12 +1620,6 @@ packages/plugin-repo-cli/lib/gitCompareUrl.js.map
packages/plugin-repo-cli/lib/gitCompareUrl.test.d.ts
packages/plugin-repo-cli/lib/gitCompareUrl.test.js
packages/plugin-repo-cli/lib/gitCompareUrl.test.js.map
packages/plugin-repo-cli/lib/overrideUtils.d.ts
packages/plugin-repo-cli/lib/overrideUtils.js
packages/plugin-repo-cli/lib/overrideUtils.js.map
packages/plugin-repo-cli/lib/overrideUtils.test.d.ts
packages/plugin-repo-cli/lib/overrideUtils.test.js
packages/plugin-repo-cli/lib/overrideUtils.test.js.map
packages/plugin-repo-cli/lib/types.d.ts
packages/plugin-repo-cli/lib/types.js
packages/plugin-repo-cli/lib/types.js.map
@@ -1696,9 +1629,6 @@ packages/plugin-repo-cli/lib/updateReadme.js.map
packages/plugin-repo-cli/lib/updateReadme.test.d.ts
packages/plugin-repo-cli/lib/updateReadme.test.js
packages/plugin-repo-cli/lib/updateReadme.test.js.map
packages/plugin-repo-cli/lib/utils.d.ts
packages/plugin-repo-cli/lib/utils.js
packages/plugin-repo-cli/lib/utils.js.map
packages/plugins/ToggleSidebars/api/index.d.ts
packages/plugins/ToggleSidebars/api/index.js
packages/plugins/ToggleSidebars/api/index.js.map
@@ -1771,9 +1701,6 @@ packages/renderer/MdToHtml/setupLinkify.js.map
packages/renderer/MdToHtml/validateLinks.d.ts
packages/renderer/MdToHtml/validateLinks.js
packages/renderer/MdToHtml/validateLinks.js.map
packages/renderer/headerAnchor.d.ts
packages/renderer/headerAnchor.js
packages/renderer/headerAnchor.js.map
packages/renderer/htmlUtils.d.ts
packages/renderer/htmlUtils.js
packages/renderer/htmlUtils.js.map
@@ -1792,9 +1719,6 @@ packages/renderer/utils.js.map
packages/tools/buildServerDocker.d.ts
packages/tools/buildServerDocker.js
packages/tools/buildServerDocker.js.map
packages/tools/convertThemesToCss.d.ts
packages/tools/convertThemesToCss.js
packages/tools/convertThemesToCss.js.map
packages/tools/generate-database-types.d.ts
packages/tools/generate-database-types.js
packages/tools/generate-database-types.js.map

View File

@@ -29,7 +29,7 @@ ol, ul {
#main-container {
position: relative;
min-height: 100vh;
padding-bottom: 127px; /* Needs to be the height of the footer */
padding-bottom: 225px;
}
.help-page-container img {
@@ -583,7 +583,7 @@ div.navbar-mobile-content a.sponsor-button {
/* footer section */
footer {
padding-top: 30px;
padding-top: 50px;
padding-bottom: 30px;
position: absolute;
bottom: 0;
@@ -594,7 +594,6 @@ footer a,
footer p {
color: #90b1d9;
text-decoration: none;
margin-bottom: 0;
}
footer a:hover {
@@ -618,35 +617,6 @@ footer .right-links a {
margin-left: 15px;
}
footer .footer-right {
margin-left: 30px;
}
footer .social-links {
display: flex;
flex-direction: row;
justify-content: center;
padding-bottom: 20px;
border-bottom: 1px solid #315885;
}
footer .social-links a:hover {
color: #e8f3ff;
}
footer .social-links i {
margin-left: 15px;
font-size: 22px;
}
footer .bottom-links-row {
padding-top: 20px;
}
footer .bottom-links-row p {
font-size: 1.1em;
}
/*****************************************************************
IN THE PRESS
The "In the press" section height needs to be adjusted as the
@@ -709,7 +679,7 @@ footer .bottom-links-row p {
#main-container {
position: relative;
min-height: 100vh;
padding-bottom: 130px;
padding-bottom: 260px;
}
.front-page h1 {
@@ -896,7 +866,7 @@ footer .bottom-links-row p {
.help-page-container {
padding-top: 90px;
padding-bottom: 70px;
padding-bottom: 50px;
}
.donate-links {
@@ -910,7 +880,7 @@ footer .bottom-links-row p {
border-radius: 20px;
padding: 30px 20px;
padding-bottom: 30px;
margin-bottom: 20px;
margin-bottom: 50px;
margin-top: 40px;
}
@@ -953,11 +923,6 @@ footer .bottom-links-row p {
justify-content: center;
}
.plan-group .joplin-cloud-login-info {
margin-bottom: 40px;
text-align: center;
}
.plan-group .plan-price-yearly-per-year {
display: flex;
justify-content: flex-end;

View File

@@ -368,7 +368,40 @@
</div>
</div>
{{> footer}}
<footer class="darkblue-bg">
<div class="container">
<div class="row">
<div class="col-3 d-none d-md-block">
<img src="{{imageBaseUrl}}/logo-text.svg" alt="" width="150" />
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<a href="{{baseUrl}}">
<img
src="{{imageBaseUrl}}/logo-text.svg"
width="120"
class="img-center d-block d-md-none"
alt=""
/>
</a>
<br class="d-block d-md-none" />
<p class="text-center-sm">Copyright &copy; 2016-{{yyyy}} Laurent Cozic</p>
</div>
<div class="col-12 col-md-6">
<p class="text-right text-center-sm right-links">
<a href="https://github.com/laurent22/joplin/" class="github-link"><i class="fab fa-github"></i> GitHub Repository</a>
<a href="{{baseUrl}}/privacy/">Privacy Policy</a>
</p>
</div>
</div>
</div>
</footer>
</div>
<script

View File

@@ -75,8 +75,38 @@ https://github.com/laurent22/joplin/blob/dev/{{{sourceMarkdownFile}}}
</div>
</div>
{{> footer}}
<footer class="darkblue-bg">
<div class="container">
<div class="row">
<div class="col-3 d-none d-md-block">
<img src="{{imageBaseUrl}}/logo-text.svg" alt="" width="150" />
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<img
src="{{imageBaseUrl}}/logo-text.svg"
width="120"
class="img-center d-block d-md-none"
alt=""
/>
<br class="d-block d-md-none" />
<p class="text-center-sm">Copyright &copy; 2016-{{yyyy}} Laurent Cozic</p>
</div>
<div class="col-12 col-md-6 right-links">
<p class="text-right text-center-sm">
<a href="https://github.com/laurent22/joplin/" class="github-link"><i class="fab fa-github"></i> GitHub Repository</a>
<a href="{{baseUrl}}/privacy/">Privacy Policy</a>
</p>
</div>
</div>
</div>
</footer>
</div>
<script src="{{jsBaseUrl}}/script.js?t={{buildTime}}"></script>

View File

@@ -1,26 +0,0 @@
<footer class="darkblue-bg">
<div class="container">
<div class="row">
<div class="col-12 col-md-12 social-links">
<a href="https://twitter.com/joplinapp" title="Twitter feed"><i class="fab fa-twitter"></i></a>
<a href="https://github.com/laurent22/joplin/" title="GitHub repository"><i class="fab fa-github"></i></a>
<a href="https://www.patreon.com/joplin" title="Patreon blog"><i class="fab fa-patreon"></i></a>
<a href="https://discordapp.com/invite/d2HMPwE" title="Discord chat"><i class="fab fa-discord"></i></a>
<a href="https://www.reddit.com/r/joplinapp/" title="Subreddit"><i class="fab fa-reddit"></i></a>
</div>
</div>
<div class="row bottom-links-row">
<div class="col-12 col-md-6">
<p class="text-center-sm">Copyright &copy; 2016-{{yyyy}} Laurent Cozic</p>
</div>
<div class="col-12 col-md-6">
<p class="text-right text-center-sm right-links">
<span class="footer-right">
<a href="{{baseUrl}}/privacy/">Privacy Policy</a>
</span>
</p>
</div>
</div>
</div>
</footer>

View File

@@ -6,7 +6,7 @@
Joplin Cloud <span class="frame-bg frame-bg-yellow">plans</span>
</h1>
<p class="text-center sub-title">
<a href="https://joplincloud.com">Joplin Cloud</a> allows you to synchronise your notes across devices. It also lets you publish notes, and collaborate on notebooks with your friends, family or colleagues.
Joplin Cloud allows you to synchronise your notes across devices. It also lets you publish notes, and collaborate on notebooks with your friends, family or colleagues.
</p>
</div>
</div>
@@ -45,8 +45,6 @@
{{#plans.business}}
{{> plan}}
{{/plans.business}}
<p class="joplin-cloud-login-info">Already have a Joplin Cloud account? <a href="https://joplincloud.com">Login now</a></p>
</div>
<div class="row">

View File

@@ -184,7 +184,7 @@ if command -v lsb_release &> /dev/null; then
# Check for "The SUID sandbox helper binary was found, but is not configured correctly" problem.
# It is present in Debian 1X. A (temporary) patch will be applied at .desktop file
# Linux Mint 4 Debbie is based on Debian 10 and requires the same param handling.
if [[ $DISTVER =~ Debian1. ]] || [ "$DISTVER" = "Linuxmint4" ] && [ "$DISTCODENAME" = "debbie" ]
if [ $DISTVER =~ Debian1. ] || [ "$DISTVER" = "Linuxmint4" ] && [ "$DISTCODENAME" = "debbie" ]
then
SANDBOXPARAM=" --no-sandbox"
fi

View File

@@ -5,7 +5,6 @@ const tasks = {
// copyLib: require('./packages/tools/gulp/tasks/copyLib'),
// tsc: require('./packages/tools/gulp/tasks/tsc'),
updateIgnoredTypeScriptBuild: require('./packages/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
buildCommandIndex: require('./packages/tools/gulp/tasks/buildCommandIndex'),
// deleteBuildDirs: require('./packages/tools/gulp/tasks/deleteBuildDirs'),
completePublishAll: {
fn: async () => {

View File

@@ -12,7 +12,6 @@
"bootstrapServerOnly": "lerna bootstrap --force-local --no-ci --include-dependents --include-dependencies --scope @joplin/server",
"build": "lerna run build && npm run tsc",
"buildApiDoc": "npm start --prefix=packages/app-cli -- apidoc ../../readme/api/references/rest_api.md",
"buildCommandIndex": "gulp buildCommandIndex",
"buildDoc": "./packages/tools/build-all.sh",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/api/references/plugin_api packages/lib/services/plugins/api/",
"buildSettingJsonSchema": "npm start --prefix=packages/app-cli -- settingschema ../../docs/schema/settings.json",

View File

@@ -381,30 +381,6 @@ async function fetchAllNotes() {
}
}
{
const tableFields = reg.db().tableFields('item_changes', { includeDescription: true });
lines.push('# Events');
lines.push('');
lines.push('This end point can be used to retrieve the latest note changes. Currently only note changes are tracked.');
lines.push('');
lines.push('## Properties');
lines.push('');
lines.push(this.createPropertiesTable(tableFields));
lines.push('');
lines.push('## GET /events');
lines.push('');
lines.push('Returns a paginated list of recent events. A `cursor` property should be provided, which tells from what point in time the events should be returned. The API will return a `cursor` property, to tell from where to resume retrieving events, as well as an `has_more` (tells if more changes can be retrieved) and `items` property, which will contain the list of events. Events are kept for up to 90 days.');
lines.push('');
lines.push('If no `cursor` property is provided, the API will respond with the latest change ID. That can be used to retrieve future events later on.');
lines.push('');
lines.push('The results are paginated so will need to may multiple calls to retrieve all the events. Use the `has_more` property to know if more can be retrieved.');
lines.push('');
lines.push('## GET /events/:id');
lines.push('');
lines.push('Returns the event with the given ID.');
}
const outFilePath = args['file'];
await shim.fsDriver().writeFile(outFilePath, lines.join('\n'), 'utf8');

View File

@@ -237,7 +237,7 @@ export default class ElectronAppWrapper {
const iid = setInterval(() => {
if (this.electronApp().isReady()) {
clearInterval(iid);
resolve(null);
resolve();
}
}, 10);
});

View File

@@ -1,47 +0,0 @@
import { AppState } from './app.reducer';
import appReducer, { createAppDefaultState } from './app.reducer';
describe('app.reducer', function() {
it('DIALOG_OPEN', async () => {
const state: AppState = createAppDefaultState({}, {});
let newState = appReducer(state, {
type: 'DIALOG_OPEN',
name: 'syncWizard',
});
expect(newState.dialogs.length).toBe(1);
expect(newState.dialogs[0].name).toBe('syncWizard');
expect(() => appReducer(newState, {
type: 'DIALOG_OPEN',
name: 'syncWizard',
})).toThrow();
newState = appReducer(newState, {
type: 'DIALOG_CLOSE',
name: 'syncWizard',
});
expect(newState.dialogs.length).toBe(0);
expect(() => appReducer(newState, {
type: 'DIALOG_CLOSE',
name: 'syncWizard',
})).toThrow();
newState = appReducer(newState, {
type: 'DIALOG_OPEN',
name: 'syncWizard',
});
newState = appReducer(newState, {
type: 'DIALOG_OPEN',
name: 'setPassword',
});
expect(newState.dialogs).toEqual([{ name: 'syncWizard' }, { name: 'setPassword' }]);
});
});

View File

@@ -1,316 +0,0 @@
import produce from 'immer';
import Setting from '@joplin/lib/models/Setting';
import { defaultState, State } from '@joplin/lib/reducer';
import iterateItems from './gui/ResizableLayout/utils/iterateItems';
import { LayoutItem } from './gui/ResizableLayout/utils/types';
import validateLayout from './gui/ResizableLayout/utils/validateLayout';
export interface AppStateRoute {
type: string;
routeName: string;
props: any;
}
export enum AppStateDialogName {
SyncWizard = 'syncWizard',
MasterPassword = 'masterPassword',
}
export interface AppStateDialog {
name: AppStateDialogName;
}
export interface AppState extends State {
route: AppStateRoute;
navHistory: any[];
noteVisiblePanes: string[];
windowContentSize: any;
watchedNoteFiles: string[];
lastEditorScrollPercents: any;
devToolsVisible: boolean;
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: string;
layoutMoveMode: boolean;
startupPluginsLoaded: boolean;
// Extra reducer keys go here
watchedResources: any;
mainLayout: LayoutItem;
dialogs: AppStateDialog[];
}
export function createAppDefaultState(windowContentSize: any, resourceEditWatcherDefaultState: any): AppState {
return {
...defaultState,
route: {
type: 'NAV_GO',
routeName: 'Main',
props: {},
},
navHistory: [],
noteVisiblePanes: ['editor', 'viewer'],
windowContentSize, // bridge().windowContentSize(),
watchedNoteFiles: [],
lastEditorScrollPercents: {},
devToolsVisible: false,
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: null,
layoutMoveMode: false,
mainLayout: null,
startupPluginsLoaded: false,
dialogs: [],
...resourceEditWatcherDefaultState,
};
}
export default function(state: AppState, action: any) {
let newState = state;
try {
switch (action.type) {
case 'NAV_BACK':
case 'NAV_GO':
{
const goingBack = action.type === 'NAV_BACK';
if (goingBack && !state.navHistory.length) break;
const currentRoute = state.route;
newState = Object.assign({}, state);
const newNavHistory = state.navHistory.slice();
if (goingBack) {
let newAction = null;
while (newNavHistory.length) {
newAction = newNavHistory.pop();
if (newAction.routeName !== state.route.routeName) break;
}
if (!newAction) break;
action = newAction;
}
if (!goingBack) newNavHistory.push(currentRoute);
newState.navHistory = newNavHistory;
newState.route = action;
}
break;
case 'STARTUP_PLUGINS_LOADED':
// When all startup plugins have loaded, we also recreate the
// main layout to ensure that it is updated in the UI. There's
// probably a cleaner way to do this, but for now that will do.
if (state.startupPluginsLoaded !== action.value) {
newState = {
...newState,
startupPluginsLoaded: action.value,
mainLayout: JSON.parse(JSON.stringify(newState.mainLayout)),
};
}
break;
case 'WINDOW_CONTENT_SIZE_SET':
newState = Object.assign({}, state);
newState.windowContentSize = action.size;
break;
case 'NOTE_VISIBLE_PANES_TOGGLE':
{
const getNextLayout = (currentLayout: any) => {
currentLayout = panes.length === 2 ? 'both' : currentLayout[0];
let paneOptions;
if (state.settings.layoutButtonSequence === Setting.LAYOUT_EDITOR_VIEWER) {
paneOptions = ['editor', 'viewer'];
} else if (state.settings.layoutButtonSequence === Setting.LAYOUT_EDITOR_SPLIT) {
paneOptions = ['editor', 'both'];
} else if (state.settings.layoutButtonSequence === Setting.LAYOUT_VIEWER_SPLIT) {
paneOptions = ['viewer', 'both'];
} else {
paneOptions = ['editor', 'viewer', 'both'];
}
const currentLayoutIndex = paneOptions.indexOf(currentLayout);
const nextLayoutIndex = currentLayoutIndex === paneOptions.length - 1 ? 0 : currentLayoutIndex + 1;
const nextLayout = paneOptions[nextLayoutIndex];
return nextLayout === 'both' ? ['editor', 'viewer'] : [nextLayout];
};
newState = Object.assign({}, state);
const panes = state.noteVisiblePanes.slice();
newState.noteVisiblePanes = getNextLayout(panes);
}
break;
case 'NOTE_VISIBLE_PANES_SET':
newState = Object.assign({}, state);
newState.noteVisiblePanes = action.panes;
break;
case 'MAIN_LAYOUT_SET':
newState = {
...state,
mainLayout: action.value,
};
break;
case 'MAIN_LAYOUT_SET_ITEM_PROP':
{
let newLayout = produce(state.mainLayout, (draftLayout: LayoutItem) => {
iterateItems(draftLayout, (_itemIndex: number, item: LayoutItem, _parent: LayoutItem) => {
if (item.key === action.itemKey) {
(item as any)[action.propName] = action.propValue;
return false;
}
return true;
});
});
if (newLayout !== state.mainLayout) newLayout = validateLayout(newLayout);
newState = {
...state,
mainLayout: newLayout,
};
}
break;
case 'NOTE_FILE_WATCHER_ADD':
if (newState.watchedNoteFiles.indexOf(action.id) < 0) {
newState = Object.assign({}, state);
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.push(action.id);
newState.watchedNoteFiles = watchedNoteFiles;
}
break;
case 'NOTE_FILE_WATCHER_REMOVE':
{
newState = Object.assign({}, state);
const idx = newState.watchedNoteFiles.indexOf(action.id);
if (idx >= 0) {
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.splice(idx, 1);
newState.watchedNoteFiles = watchedNoteFiles;
}
}
break;
case 'NOTE_FILE_WATCHER_CLEAR':
if (state.watchedNoteFiles.length) {
newState = Object.assign({}, state);
newState.watchedNoteFiles = [];
}
break;
case 'EDITOR_SCROLL_PERCENT_SET':
{
newState = Object.assign({}, state);
const newPercents = Object.assign({}, newState.lastEditorScrollPercents);
newPercents[action.noteId] = action.percent;
newState.lastEditorScrollPercents = newPercents;
}
break;
case 'NOTE_DEVTOOLS_TOGGLE':
newState = Object.assign({}, state);
newState.devToolsVisible = !newState.devToolsVisible;
break;
case 'NOTE_DEVTOOLS_SET':
newState = Object.assign({}, state);
newState.devToolsVisible = action.value;
break;
case 'VISIBLE_DIALOGS_ADD':
newState = Object.assign({}, state);
newState.visibleDialogs = Object.assign({}, newState.visibleDialogs);
newState.visibleDialogs[action.name] = true;
break;
case 'VISIBLE_DIALOGS_REMOVE':
newState = Object.assign({}, state);
newState.visibleDialogs = Object.assign({}, newState.visibleDialogs);
delete newState.visibleDialogs[action.name];
break;
case 'FOCUS_SET':
newState = Object.assign({}, state);
newState.focusedField = action.field;
break;
case 'FOCUS_CLEAR':
// A field can only clear its own state
if (action.field === state.focusedField) {
newState = Object.assign({}, state);
newState.focusedField = null;
}
break;
case 'DIALOG_OPEN':
case 'DIALOG_CLOSE':
{
let isOpen = true;
if (action.type === 'DIALOG_CLOSE') {
isOpen = false;
} else { // DIALOG_OPEN
isOpen = action.isOpen !== false;
}
newState = Object.assign({}, state);
if (isOpen) {
const newDialogs = newState.dialogs.slice();
if (newDialogs.find(d => d.name === action.name)) throw new Error(`Trying to open a dialog is already open: ${action.name}`);
newDialogs.push({
name: action.name,
});
newState.dialogs = newDialogs;
} else {
if (!newState.dialogs.find(d => d.name === action.name)) throw new Error(`Trying to close a dialog that is not open: ${action.name}`);
const newDialogs = newState.dialogs.slice().filter(d => d.name !== action.name);
newState.dialogs = newDialogs;
}
}
break;
case 'LAYOUT_MOVE_MODE_SET':
newState = {
...state,
layoutMoveMode: action.value,
};
break;
}
} catch (error) {
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
throw error;
}
return newState;
}

View File

@@ -3,6 +3,7 @@ import CommandService from '@joplin/lib/services/CommandService';
import KeymapService from '@joplin/lib/services/KeymapService';
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
import resourceEditWatcherReducer, { defaultState as resourceEditWatcherDefaultState } from '@joplin/lib/services/ResourceEditWatcher/reducer';
import { defaultState, State } from '@joplin/lib/reducer';
import PluginRunner from './services/plugins/PluginRunner';
import PlatformImplementation from './services/plugins/PlatformImplementation';
import shim from '@joplin/lib/shim';
@@ -18,10 +19,13 @@ import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerS
import SpellCheckerServiceDriverNative from './services/spellChecker/SpellCheckerServiceDriverNative';
import bridge from './services/bridge';
import menuCommandNames from './gui/menuCommandNames';
import { LayoutItem } from './gui/ResizableLayout/utils/types';
import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext';
import ResourceService from '@joplin/lib/services/ResourceService';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
import appReducer, { createAppDefaultState } from './app.reducer';
import produce from 'immer';
import iterateItems from './gui/ResizableLayout/utils/iterateItems';
import validateLayout from './gui/ResizableLayout/utils/validateLayout';
const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
import Folder from '@joplin/lib/models/Folder';
const fs = require('fs-extra');
@@ -36,38 +40,125 @@ const PluginManager = require('@joplin/lib/services/PluginManager');
import RevisionService from '@joplin/lib/services/RevisionService';
import MigrationService from '@joplin/lib/services/MigrationService';
import { loadCustomCss, injectCustomStyles } from '@joplin/lib/CssUtils';
import mainScreenCommands from './gui/MainScreen/commands/index';
import noteEditorCommands from './gui/NoteEditor/commands/index';
import noteListCommands from './gui/NoteList/commands/index';
import noteListControlsCommands from './gui/NoteListControls/commands/index';
import sidebarCommands from './gui/Sidebar/commands/index';
import appCommands from './commands/index';
import libCommands from '@joplin/lib/commands/index';
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
const commands = mainScreenCommands
.concat(noteEditorCommands)
.concat(noteListCommands)
.concat(noteListControlsCommands)
.concat(sidebarCommands);
const commands = [
require('./gui/MainScreen/commands/editAlarm'),
require('./gui/MainScreen/commands/exportPdf'),
require('./gui/MainScreen/commands/gotoAnything'),
require('./gui/MainScreen/commands/commandPalette'),
require('./gui/MainScreen/commands/hideModalMessage'),
require('./gui/MainScreen/commands/moveToFolder'),
require('./gui/MainScreen/commands/newFolder'),
require('./gui/MainScreen/commands/newNote'),
require('./gui/MainScreen/commands/newSubFolder'),
require('./gui/MainScreen/commands/newTodo'),
require('./gui/MainScreen/commands/openFolder'),
require('./gui/MainScreen/commands/openNote'),
require('./gui/MainScreen/commands/openTag'),
require('./gui/MainScreen/commands/print'),
require('./gui/MainScreen/commands/renameFolder'),
require('./gui/MainScreen/commands/renameTag'),
require('./gui/MainScreen/commands/search'),
require('./gui/MainScreen/commands/setTags'),
require('./gui/MainScreen/commands/showModalMessage'),
require('./gui/MainScreen/commands/showNoteContentProperties'),
require('./gui/MainScreen/commands/showNoteProperties'),
require('./gui/MainScreen/commands/showPrompt'),
require('./gui/MainScreen/commands/showShareFolderDialog'),
require('./gui/MainScreen/commands/showShareNoteDialog'),
require('./gui/MainScreen/commands/showSpellCheckerMenu'),
require('./gui/MainScreen/commands/toggleEditors'),
require('./gui/MainScreen/commands/toggleLayoutMoveMode'),
require('./gui/MainScreen/commands/toggleNoteList'),
require('./gui/MainScreen/commands/toggleSideBar'),
require('./gui/MainScreen/commands/toggleVisiblePanes'),
require('./gui/NoteEditor/commands/focusElementNoteBody'),
require('./gui/NoteEditor/commands/focusElementNoteTitle'),
require('./gui/NoteEditor/commands/showLocalSearch'),
require('./gui/NoteEditor/commands/showRevisions'),
require('./gui/NoteList/commands/focusElementNoteList'),
require('./gui/NoteListControls/commands/focusSearch'),
require('./gui/Sidebar/commands/focusElementSideBar'),
];
// Commands that are not tied to any particular component.
// The runtime for these commands can be loaded when the app starts.
const globalCommands = appCommands.concat(libCommands);
const globalCommands = [
require('./commands/copyDevCommand'),
require('./commands/exportFolders'),
require('./commands/exportNotes'),
require('./commands/focusElement'),
require('./commands/openProfileDirectory'),
require('./commands/replaceMisspelling'),
require('./commands/startExternalEditing'),
require('./commands/stopExternalEditing'),
require('./commands/toggleExternalEditing'),
require('./commands/toggleSafeMode'),
require('./commands/restoreNoteRevision'),
require('@joplin/lib/commands/historyBackward'),
require('@joplin/lib/commands/historyForward'),
require('@joplin/lib/commands/synchronize'),
];
import editorCommandDeclarations from './gui/NoteEditor/editorCommandDeclarations';
import editorCommandDeclarations from './gui/NoteEditor/commands/editorCommandDeclarations';
import ShareService from '@joplin/lib/services/share/ShareService';
import checkForUpdates from './checkForUpdates';
import { AppState } from './app.reducer';
const pluginClasses = [
require('./plugins/GotoAnything').default,
];
const appDefaultState = createAppDefaultState(
bridge().windowContentSize(),
resourceEditWatcherDefaultState
);
interface AppStateRoute {
type: string;
routeName: string;
props: any;
}
export interface AppStateDialog {
name: string;
}
export interface AppState extends State {
route: AppStateRoute;
navHistory: any[];
noteVisiblePanes: string[];
windowContentSize: any;
watchedNoteFiles: string[];
lastEditorScrollPercents: any;
devToolsVisible: boolean;
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: string;
layoutMoveMode: boolean;
startupPluginsLoaded: boolean;
// Extra reducer keys go here
watchedResources: any;
mainLayout: LayoutItem;
dialogs: AppStateDialog[];
}
const appDefaultState: AppState = {
...defaultState,
route: {
type: 'NAV_GO',
routeName: 'Main',
props: {},
},
navHistory: [],
noteVisiblePanes: ['editor', 'viewer'],
windowContentSize: bridge().windowContentSize(),
watchedNoteFiles: [],
lastEditorScrollPercents: {},
devToolsVisible: false,
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: null,
layoutMoveMode: false,
mainLayout: null,
startupPluginsLoaded: false,
dialogs: [],
...resourceEditWatcherDefaultState,
};
class Application extends BaseApplication {
@@ -84,9 +175,249 @@ class Application extends BaseApplication {
}
reducer(state: AppState = appDefaultState, action: any) {
let newState = appReducer(state, action);
let newState = state;
try {
switch (action.type) {
case 'NAV_BACK':
case 'NAV_GO':
{
const goingBack = action.type === 'NAV_BACK';
if (goingBack && !state.navHistory.length) break;
const currentRoute = state.route;
newState = Object.assign({}, state);
const newNavHistory = state.navHistory.slice();
if (goingBack) {
let newAction = null;
while (newNavHistory.length) {
newAction = newNavHistory.pop();
if (newAction.routeName !== state.route.routeName) break;
}
if (!newAction) break;
action = newAction;
}
if (!goingBack) newNavHistory.push(currentRoute);
newState.navHistory = newNavHistory;
newState.route = action;
}
break;
case 'STARTUP_PLUGINS_LOADED':
// When all startup plugins have loaded, we also recreate the
// main layout to ensure that it is updated in the UI. There's
// probably a cleaner way to do this, but for now that will do.
if (state.startupPluginsLoaded !== action.value) {
newState = {
...newState,
startupPluginsLoaded: action.value,
mainLayout: JSON.parse(JSON.stringify(newState.mainLayout)),
};
}
break;
case 'WINDOW_CONTENT_SIZE_SET':
newState = Object.assign({}, state);
newState.windowContentSize = action.size;
break;
case 'NOTE_VISIBLE_PANES_TOGGLE':
{
const getNextLayout = (currentLayout: any) => {
currentLayout = panes.length === 2 ? 'both' : currentLayout[0];
let paneOptions;
if (state.settings.layoutButtonSequence === Setting.LAYOUT_EDITOR_VIEWER) {
paneOptions = ['editor', 'viewer'];
} else if (state.settings.layoutButtonSequence === Setting.LAYOUT_EDITOR_SPLIT) {
paneOptions = ['editor', 'both'];
} else if (state.settings.layoutButtonSequence === Setting.LAYOUT_VIEWER_SPLIT) {
paneOptions = ['viewer', 'both'];
} else {
paneOptions = ['editor', 'viewer', 'both'];
}
const currentLayoutIndex = paneOptions.indexOf(currentLayout);
const nextLayoutIndex = currentLayoutIndex === paneOptions.length - 1 ? 0 : currentLayoutIndex + 1;
const nextLayout = paneOptions[nextLayoutIndex];
return nextLayout === 'both' ? ['editor', 'viewer'] : [nextLayout];
};
newState = Object.assign({}, state);
const panes = state.noteVisiblePanes.slice();
newState.noteVisiblePanes = getNextLayout(panes);
}
break;
case 'NOTE_VISIBLE_PANES_SET':
newState = Object.assign({}, state);
newState.noteVisiblePanes = action.panes;
break;
case 'MAIN_LAYOUT_SET':
newState = {
...state,
mainLayout: action.value,
};
break;
case 'MAIN_LAYOUT_SET_ITEM_PROP':
{
let newLayout = produce(state.mainLayout, (draftLayout: LayoutItem) => {
iterateItems(draftLayout, (_itemIndex: number, item: LayoutItem, _parent: LayoutItem) => {
if (item.key === action.itemKey) {
(item as any)[action.propName] = action.propValue;
return false;
}
return true;
});
});
if (newLayout !== state.mainLayout) newLayout = validateLayout(newLayout);
newState = {
...state,
mainLayout: newLayout,
};
}
break;
case 'NOTE_FILE_WATCHER_ADD':
if (newState.watchedNoteFiles.indexOf(action.id) < 0) {
newState = Object.assign({}, state);
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.push(action.id);
newState.watchedNoteFiles = watchedNoteFiles;
}
break;
case 'NOTE_FILE_WATCHER_REMOVE':
{
newState = Object.assign({}, state);
const idx = newState.watchedNoteFiles.indexOf(action.id);
if (idx >= 0) {
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.splice(idx, 1);
newState.watchedNoteFiles = watchedNoteFiles;
}
}
break;
case 'NOTE_FILE_WATCHER_CLEAR':
if (state.watchedNoteFiles.length) {
newState = Object.assign({}, state);
newState.watchedNoteFiles = [];
}
break;
case 'EDITOR_SCROLL_PERCENT_SET':
{
newState = Object.assign({}, state);
const newPercents = Object.assign({}, newState.lastEditorScrollPercents);
newPercents[action.noteId] = action.percent;
newState.lastEditorScrollPercents = newPercents;
}
break;
case 'NOTE_DEVTOOLS_TOGGLE':
newState = Object.assign({}, state);
newState.devToolsVisible = !newState.devToolsVisible;
break;
case 'NOTE_DEVTOOLS_SET':
newState = Object.assign({}, state);
newState.devToolsVisible = action.value;
break;
case 'VISIBLE_DIALOGS_ADD':
newState = Object.assign({}, state);
newState.visibleDialogs = Object.assign({}, newState.visibleDialogs);
newState.visibleDialogs[action.name] = true;
break;
case 'VISIBLE_DIALOGS_REMOVE':
newState = Object.assign({}, state);
newState.visibleDialogs = Object.assign({}, newState.visibleDialogs);
delete newState.visibleDialogs[action.name];
break;
case 'FOCUS_SET':
newState = Object.assign({}, state);
newState.focusedField = action.field;
break;
case 'FOCUS_CLEAR':
// A field can only clear its own state
if (action.field === state.focusedField) {
newState = Object.assign({}, state);
newState.focusedField = null;
}
break;
case 'DIALOG_OPEN':
{
newState = Object.assign({}, state);
const newDialogs = newState.dialogs.slice();
if (newDialogs.find(d => d.name === action.name)) throw new Error(`This dialog is already opened: ${action.name}`);
newDialogs.push({
name: action.name,
});
newState.dialogs = newDialogs;
}
break;
case 'DIALOG_CLOSE':
{
newState = Object.assign({}, state);
const newDialogs = newState.dialogs.slice().filter(d => d.name !== action.name);
newState.dialogs = newDialogs;
}
break;
case 'LAYOUT_MOVE_MODE_SET':
newState = {
...state,
layoutMoveMode: action.value,
};
break;
}
} catch (error) {
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
throw error;
}
newState = resourceEditWatcherReducer(newState, action);
newState = super.reducer(newState, action);
return newState;
}
@@ -542,16 +873,6 @@ class Application extends BaseApplication {
// });
// }, 2000);
// setTimeout(() => {
// this.dispatch({
// type: 'NAV_GO',
// routeName: 'Config',
// props: {
// defaultSection: 'plugins',
// },
// });
// }, 2000);
return null;
}

View File

@@ -1,29 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as copyDevCommand from './copyDevCommand';
import * as exportFolders from './exportFolders';
import * as exportNotes from './exportNotes';
import * as focusElement from './focusElement';
import * as openProfileDirectory from './openProfileDirectory';
import * as replaceMisspelling from './replaceMisspelling';
import * as restoreNoteRevision from './restoreNoteRevision';
import * as startExternalEditing from './startExternalEditing';
import * as stopExternalEditing from './stopExternalEditing';
import * as toggleExternalEditing from './toggleExternalEditing';
import * as toggleSafeMode from './toggleSafeMode';
const index:any[] = [
copyDevCommand,
exportFolders,
exportNotes,
focusElement,
openProfileDirectory,
replaceMisspelling,
restoreNoteRevision,
startExternalEditing,
stopExternalEditing,
toggleExternalEditing,
toggleSafeMode,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -1,5 +1,5 @@
import CommandService, { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { AppState } from '../app.reducer';
import { AppState } from '../app';
import bridge from '../services/bridge';
export const declaration: CommandDeclaration = {

View File

@@ -8,7 +8,7 @@ import { _ } from '@joplin/lib/locale';
import ClipperServer from '@joplin/lib/ClipperServer';
import Setting from '@joplin/lib/models/Setting';
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import { AppState } from '../app.reducer';
import { AppState } from '../app';
class ClipperConfigScreenComponent extends React.Component {
constructor() {

View File

@@ -133,20 +133,6 @@ const StyledDescription = styled.div`
line-height: 1.6em;
`;
const RecommendedBadge = styled.a`
font-family: ${props => props.theme.fontFamily};
color: ${props => props.theme.colorWarn};
font-size: ${props => props.theme.fontSize}px;
border: 1px solid ${props => props.theme.colorWarn};
padding: 5px;
border-radius: 50px;
opacity: 0.8;
&:hover {
opacity: 1;
}
`;
export default function(props: Props) {
const item = useMemo(() => {
return props.item ? props.item : manifestToItem(props.manifest);
@@ -158,10 +144,6 @@ export default function(props: Props) {
bridge().openExternal(manifest.homepage_url);
}, [item]);
const onRecommendedClick = useCallback(() => {
bridge().openExternal('https://github.com/joplin/plugins/blob/master/readme/recommended.md#recommended-plugins');
}, []);
// For plugins in dev mode things like enabling/disabling or
// uninstalling them doesn't make sense, as that should be done by
// adding/removing them from wherever they were loaded from.
@@ -240,18 +222,11 @@ export default function(props: Props) {
);
}
function renderRecommendedBadge() {
if (props.onToggle) return null;
if (!item.manifest._recommended) return null;
return <RecommendedBadge href="#" title={_('The Joplin team has vetted this plugin and it meets our standards for security and performance.')} onClick={onRecommendedClick}><i className="fas fa-crown"></i></RecommendedBadge>;
}
return (
<CellRoot isCompatible={props.isCompatible}>
<CellTop>
<StyledNameAndVersion mb={'5px'}><StyledName onClick={onNameClick} href="#" style={{ marginRight: 5 }}>{item.manifest.name} {item.deleted ? _('(%s)', 'Deleted') : ''}</StyledName><StyledVersion>v{item.manifest.version}</StyledVersion></StyledNameAndVersion>
{renderToggleButton()}
{renderRecommendedBadge()}
</CellTop>
<CellContent>
<StyledDescription>{item.manifest.description}</StyledDescription>

View File

@@ -30,14 +30,6 @@ interface Props {
disabled: boolean;
}
function sortManifestResults(results: PluginManifest[]): PluginManifest[] {
return results.sort((m1, m2) => {
if (m1._recommended && !m2._recommended) return -1;
if (!m1._recommended && m2._recommended) return +1;
return m1.name.toLowerCase() < m2.name.toLowerCase() ? -1 : +1;
});
}
export default function(props: Props) {
const [searchStarted, setSearchStarted] = useState(false);
const [manifests, setManifests] = useState<PluginManifest[]>([]);
@@ -55,7 +47,7 @@ export default function(props: Props) {
setSearchResultCount(null);
} else {
const r = await props.repoApi().search(props.searchQuery);
setManifests(sortManifestResults(r));
setManifests(r);
setSearchResultCount(r.length);
}
});

View File

@@ -17,7 +17,7 @@ import { stateUtils } from '@joplin/lib/reducer';
import InteropServiceHelper from '../../InteropServiceHelper';
import { _ } from '@joplin/lib/locale';
import NoteListWrapper from '../NoteListWrapper/NoteListWrapper';
import { AppState } from '../../app.reducer';
import { AppState } from '../../app';
import { saveLayout, loadLayout } from '../ResizableLayout/utils/persist';
import Setting from '@joplin/lib/models/Setting';
import produce from 'immer';
@@ -37,7 +37,6 @@ import { reg } from '@joplin/lib/registry';
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
import commands from './commands/index';
const { connect } = require('react-redux');
const { PromptDialog } = require('../PromptDialog.min.js');
@@ -111,6 +110,39 @@ const defaultLayout: LayoutItem = {
],
};
const commands = [
require('./commands/editAlarm'),
require('./commands/exportPdf'),
require('./commands/gotoAnything'),
require('./commands/commandPalette'),
require('./commands/hideModalMessage'),
require('./commands/moveToFolder'),
require('./commands/newFolder'),
require('./commands/newNote'),
require('./commands/newSubFolder'),
require('./commands/newTodo'),
require('./commands/openFolder'),
require('./commands/openNote'),
require('./commands/openTag'),
require('./commands/print'),
require('./commands/renameFolder'),
require('./commands/renameTag'),
require('./commands/search'),
require('./commands/setTags'),
require('./commands/showModalMessage'),
require('./commands/showNoteContentProperties'),
require('./commands/showNoteProperties'),
require('./commands/showPrompt'),
require('./commands/showShareFolderDialog'),
require('./commands/showShareNoteDialog'),
require('./commands/showSpellCheckerMenu'),
require('./commands/toggleEditors'),
require('./commands/toggleLayoutMoveMode'),
require('./commands/toggleNoteList'),
require('./commands/toggleSideBar'),
require('./commands/toggleVisiblePanes'),
];
class MainScreenComponent extends React.Component<Props, State> {
private waitForNotesSavedIID_: any;

View File

@@ -1,67 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as commandPalette from './commandPalette';
import * as editAlarm from './editAlarm';
import * as exportPdf from './exportPdf';
import * as gotoAnything from './gotoAnything';
import * as hideModalMessage from './hideModalMessage';
import * as moveToFolder from './moveToFolder';
import * as newFolder from './newFolder';
import * as newNote from './newNote';
import * as newSubFolder from './newSubFolder';
import * as newTodo from './newTodo';
import * as openFolder from './openFolder';
import * as openNote from './openNote';
import * as openTag from './openTag';
import * as print from './print';
import * as renameFolder from './renameFolder';
import * as renameTag from './renameTag';
import * as search from './search';
import * as setTags from './setTags';
import * as showModalMessage from './showModalMessage';
import * as showNoteContentProperties from './showNoteContentProperties';
import * as showNoteProperties from './showNoteProperties';
import * as showPrompt from './showPrompt';
import * as showShareFolderDialog from './showShareFolderDialog';
import * as showShareNoteDialog from './showShareNoteDialog';
import * as showSpellCheckerMenu from './showSpellCheckerMenu';
import * as toggleEditors from './toggleEditors';
import * as toggleLayoutMoveMode from './toggleLayoutMoveMode';
import * as toggleNoteList from './toggleNoteList';
import * as toggleSideBar from './toggleSideBar';
import * as toggleVisiblePanes from './toggleVisiblePanes';
const index:any[] = [
commandPalette,
editAlarm,
exportPdf,
gotoAnything,
hideModalMessage,
moveToFolder,
newFolder,
newNote,
newSubFolder,
newTodo,
openFolder,
openNote,
openTag,
print,
renameFolder,
renameTag,
search,
setTags,
showModalMessage,
showNoteContentProperties,
showNoteProperties,
showPrompt,
showShareFolderDialog,
showShareNoteDialog,
showSpellCheckerMenu,
toggleEditors,
toggleLayoutMoveMode,
toggleNoteList,
toggleSideBar,
toggleVisiblePanes,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -2,7 +2,7 @@ import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/
import { _ } from '@joplin/lib/locale';
import bridge from '../../../services/bridge';
import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerService';
import { AppState } from '../../../app.reducer';
import { AppState } from '../../../app';
const Menu = bridge().Menu;

View File

@@ -1,6 +1,6 @@
import { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { AppState } from '../../../app.reducer';
import { AppState } from '../../../app';
export const declaration: CommandDeclaration = {
name: 'toggleLayoutMoveMode',

View File

@@ -2,7 +2,7 @@ import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/
import { _ } from '@joplin/lib/locale';
import setLayoutItemProps from '../../ResizableLayout/utils/setLayoutItemProps';
import layoutItemProp from '../../ResizableLayout/utils/layoutItemProp';
import { AppState } from '../../../app.reducer';
import { AppState } from '../../../app';
export const declaration: CommandDeclaration = {
name: 'toggleNoteList',

View File

@@ -2,7 +2,7 @@ import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/
import { _ } from '@joplin/lib/locale';
import setLayoutItemProps from '../../ResizableLayout/utils/setLayoutItemProps';
import layoutItemProp from '../../ResizableLayout/utils/layoutItemProp';
import { AppState } from '../../../app.reducer';
import { AppState } from '../../../app';
export const declaration: CommandDeclaration = {
name: 'toggleSideBar',

View File

@@ -1,5 +1,5 @@
import { useEffect, useState, useRef, useCallback } from 'react';
import { AppState } from '../app.reducer';
import { AppState } from '../app';
import InteropService from '@joplin/lib/services/interop/InteropService';
import { stateUtils } from '@joplin/lib/reducer';
import CommandService from '@joplin/lib/services/CommandService';

View File

@@ -3,7 +3,7 @@ import CommandService from '@joplin/lib/services/CommandService';
import ToolbarBase from '../../../ToolbarBase';
import { utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
import { connect } from 'react-redux';
import { AppState } from '../../../../app.reducer';
import { AppState } from '../../../../app';
import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import stateToWhenClauseContext from '../../../../services/commands/stateToWhenClauseContext';
const { buildStyle } = require('@joplin/lib/theme');

View File

@@ -20,7 +20,7 @@ import CommandService from '@joplin/lib/services/CommandService';
import ToolbarButton from '../ToolbarButton/ToolbarButton';
import Button, { ButtonLevel } from '../Button/Button';
import eventManager from '@joplin/lib/eventManager';
import { AppState } from '../../app.reducer';
import { AppState } from '../../app';
import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils';
import { _ } from '@joplin/lib/locale';
import TagList from '../TagList';

View File

@@ -1,15 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as focusElementNoteBody from './focusElementNoteBody';
import * as focusElementNoteTitle from './focusElementNoteTitle';
import * as showLocalSearch from './showLocalSearch';
import * as showRevisions from './showRevisions';
const index:any[] = [
focusElementNoteBody,
focusElementNoteTitle,
showLocalSearch,
showRevisions,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { FormNote, ScrollOptionTypes } from './types';
import editorCommandDeclarations from '../editorCommandDeclarations';
import editorCommandDeclarations from '../commands/editorCommandDeclarations';
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
import time from '@joplin/lib/time';
import { reg } from '@joplin/lib/registry';

View File

@@ -1,4 +1,4 @@
import { AppState } from '../../app.reducer';
import { AppState } from '../../app';
import eventManager from '@joplin/lib/eventManager';
import NoteListUtils from '../utils/NoteListUtils';
import { _ } from '@joplin/lib/locale';

View File

@@ -1,9 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as focusElementNoteList from './focusElementNoteList';
const index:any[] = [
focusElementNoteList,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -1,9 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as focusSearch from './focusSearch';
const index:any[] = [
focusSearch,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -1,5 +1,4 @@
import app from '../app';
import { AppState, AppStateDialog } from '../app.reducer';
import app, { AppState, AppStateDialog } from '../app';
import MainScreen from './MainScreen/MainScreen';
import ConfigScreen from './ConfigScreen/ConfigScreen';
import StatusScreen from './StatusScreen/StatusScreen';
@@ -20,7 +19,6 @@ import DialogTitle from './DialogTitle';
import DialogButtonRow, { ButtonSpec, ClickEvent, ClickEventHandler } from './DialogButtonRow';
import Dialog from './Dialog';
import SyncWizardDialog from './SyncWizard/Dialog';
import StyleSheetContainer from './StyleSheets/StyleSheetContainer';
const { ImportScreen } = require('./ImportScreen.min.js');
const { ResourceScreen } = require('./ResourceScreen.js');
const { Navigator } = require('./Navigator.min.js');
@@ -209,7 +207,6 @@ class RootComponent extends React.Component<Props, any> {
return (
<StyleSheetManager disableVendorPrefixes>
<ThemeProvider theme={theme}>
<StyleSheetContainer themeId={this.props.themeId}></StyleSheetContainer>
<MenuBar/>
<GlobalStyle/>
<Navigator style={navigatorStyle} screens={screens} />

View File

@@ -16,7 +16,6 @@ import { ShareUserStatus, StateShare, StateShareUser } from '@joplin/lib/service
import { State } from '@joplin/lib/reducer';
import { connect } from 'react-redux';
import { reg } from '@joplin/lib/registry';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
const logger = Logger.create('ShareFolderDialog');
@@ -101,6 +100,20 @@ interface RecipientDeleteEvent {
shareUserId: string;
}
interface AsyncEffectEvent {
cancelled: boolean;
}
function useAsyncEffect(effect: Function, dependencies: any[]) {
useEffect(() => {
const event = { cancelled: false };
effect(event);
return () => {
event.cancelled = true;
};
}, dependencies);
}
enum ShareState {
Idle = 0,
Synchronizing = 1,

View File

@@ -13,7 +13,7 @@ import { StateShare } from '@joplin/lib/services/share/reducer';
import { NoteEntity } from '@joplin/lib/services/database/types';
import Button from './Button/Button';
import { connect } from 'react-redux';
import { AppState } from '../app.reducer';
import { AppState } from '../app';
import { getEncryptionEnabled } from '@joplin/lib/services/synchronizer/syncInfoUtils';
const { clipboard } = require('electron');

View File

@@ -10,7 +10,7 @@ import InteropServiceHelper from '../../InteropServiceHelper';
import { _ } from '@joplin/lib/locale';
import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
import { MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
import { AppState } from '../../app.reducer';
import { AppState } from '../../app';
import { ModelType } from '@joplin/lib/BaseModel';
import BaseModel from '@joplin/lib/BaseModel';
import Folder from '@joplin/lib/models/Folder';

View File

@@ -1,7 +1,7 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import layoutItemProp from '../../ResizableLayout/utils/layoutItemProp';
import { AppState } from '../../../app.reducer';
import { AppState } from '../../../app';
export const declaration: CommandDeclaration = {
name: 'focusElementSideBar',

View File

@@ -1,9 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as focusElementSideBar from './focusElementSideBar';
const index:any[] = [
focusElementSideBar,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -1,41 +0,0 @@
// This component is perhaps a bit of a hack but the approach should be
// reliable. It converts the current (JS) theme to CSS, and add it to the HEAD
// tag. The component itself doesn't render anything where it's located (just an
// empty invisible DIV), so it means it could be put anywhere and would have the
// same effect.
//
// It's still reliable because the lifecyle of adding the CSS and removing on
// unmout is handled properly. There should only be one such component on the
// page.
import { useEffect, useState } from 'react';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import themeToCss from '@joplin/lib/services/style/themeToCss';
import { themeById } from '@joplin/lib/theme';
interface Props {
themeId: any;
}
export default function(props: Props): any {
const [styleSheetContent, setStyleSheetContent] = useState('');
useAsyncEffect(async (event: AsyncEffectEvent) => {
const theme = themeById(props.themeId);
const themeCss = themeToCss(theme);
if (event.cancelled) return;
setStyleSheetContent(themeCss);
}, [props.themeId]);
useEffect(() => {
const element = document.createElement('style');
element.setAttribute('id', 'main-theme-stylesheet-container');
document.head.appendChild(element);
element.appendChild(document.createTextNode(styleSheetContent));
return () => {
document.head.removeChild(element);
};
}, [styleSheetContent]);
return <div style={{ display: 'none' }}></div>;
}

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { useMemo } from 'react';
import { AppState } from '../app.reducer';
import { AppState } from '../app';
const { connect } = require('react-redux');
const { themeStyle } = require('@joplin/lib/theme');

View File

@@ -19,7 +19,6 @@ const tasks = {
},
tsc: require('@joplin/tools/gulp/tasks/tsc'),
updateIgnoredTypeScriptBuild: require('@joplin/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
buildCommandIndex: require('@joplin/tools/gulp/tasks/buildCommandIndex'),
};
utils.registerGulpTasks(gulp, tasks);
@@ -29,8 +28,7 @@ const buildParallel = [
'compilePackageInfo',
'copyPluginAssets',
'copyTinyMceLangs',
'updateIgnoredTypeScriptBuild',
'buildCommandIndex',
// 'updateIgnoredTypeScriptBuild',
];
gulp.task('build', gulp.parallel(...buildParallel));

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/app-desktop",
"version": "2.4.4",
"version": "2.4.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/app-desktop",
"version": "2.4.4",
"version": "2.4.3",
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.13.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.4.4",
"version": "2.4.3",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { AppState } from '../app.reducer';
import { AppState } from '../app';
import CommandService, { SearchResult as CommandSearchResult } from '@joplin/lib/services/CommandService';
import KeymapService from '@joplin/lib/services/KeymapService';
import shim from '@joplin/lib/shim';
@@ -337,10 +337,6 @@ class Dialog extends React.PureComponent<Props, State> {
// @ts-ignore
const notesById = notes.reduce((obj, { id, body, markup_language }) => ((obj[[id]] = { id, body, markup_language }), obj), {});
// Filter out search results that are associated with non-existing notes.
// https://github.com/laurent22/joplin/issues/5417
results = results.filter(r => !!notesById[r.id]);
for (let i = 0; i < results.length; i++) {
const row = results[i];
const path = Folder.folderPathString(this.props.folders, row.parent_id);

View File

@@ -3,7 +3,7 @@
// general, any desktop component should import this file, and not the lib
// one.
import { AppState } from '../../app.reducer';
import { AppState } from '../../app';
import libStateToWhenClauseContext, { WhenClauseContextOptions } from '@joplin/lib/services/commands/stateToWhenClauseContext';
import layoutItemProp from '../../gui/ResizableLayout/utils/layoutItemProp';

View File

@@ -1,4 +1,4 @@
import { AppState } from '../../app.reducer';
import { AppState } from '../../app';
export interface DesktopCommandContext {
state: AppState;

View File

@@ -64,6 +64,28 @@ a {
opacity: 1;
}
/*
.note-list .list-item-container:hover {
background-color: rgba(0,160,255,0.1) !important;
}
*/
/*
.editor-toolbar .button:not(.disabled):hover,
.header .button:not(.disabled):hover {
background-color: rgba(0,160,255,0.1);
border: 1px solid rgba(0,160,255,0.5);
box-sizing: 'border-box';
}
.editor-toolbar .button:not(.disabled):active,
.header .button:not(.disabled):active {
background-color: rgba(0,160,255,0.2);
border: 1px solid rgba(0,160,255,0.7);
box-sizing: 'border-box';
}
*/
.editor-toolbar .button,
.header .button {
border: 1px solid rgba(0,160,255,0);
@@ -141,6 +163,11 @@ a {
to {transform: rotate(360deg);}
}
/* .joplin-tinymce .tox-editor-header {
padding-left: 88px;
padding-right: 150px;
} */
*:focus {
outline: none;
}

View File

@@ -141,8 +141,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097649
versionName "2.4.1"
versionCode 2097648
versionName "2.4.0"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}

View File

@@ -488,7 +488,7 @@ SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
FBReactNativeSpec: 6da2c8ff1ebe6b6cf4510fcca58c24c4d02b16fc
FBReactNativeSpec: d2f54de51f69366bd1f5c1fb9270698dce678f8d
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
JoplinCommonShareExtension: 270b4f8eb4e22828eeda433a04ed689fc1fd09b5
JoplinRNShareExtension: 7137e9787374e1b0797ecbef9103d1588d90e403

View File

@@ -480,7 +480,6 @@ async function initialize(dispatch: Function) {
}
reg.logger().info('Database is ready.');
reg.logger().info('Loading settings...');
await loadKeychainServiceAndSettings(KeychainServiceDriverMobile);
@@ -642,7 +641,7 @@ async function initialize(dispatch: Function) {
class AppComponent extends React.Component {
public constructor() {
constructor() {
super();
this.state = {
@@ -685,7 +684,7 @@ class AppComponent extends React.Component {
// https://github.com/laurent22/joplin/issues/3807
// https://discourse.joplinapp.org/t/webdav-config-encryption-config-randomly-lost-on-android/11364
// https://discourse.joplinapp.org/t/android-keeps-on-resetting-my-sync-and-theme/11443
public async componentDidMount() {
async componentDidMount() {
if (this.props.appState == 'starting') {
this.props.dispatch({
type: 'APP_STATE_SET',
@@ -738,13 +737,13 @@ class AppComponent extends React.Component {
// setTimeout(() => NavService.go('EncryptionConfig'), 2000);
}
public componentWillUnmount() {
componentWillUnmount() {
AppState.removeEventListener('change', this.onAppStateChange_);
Linking.removeEventListener('url', this.handleOpenURL_);
if (this.unsubscribeNetInfoHandler_) this.unsubscribeNetInfoHandler_();
}
public componentDidUpdate(prevProps: any) {
componentDidUpdate(prevProps: any) {
if (this.props.showSideMenu !== prevProps.showSideMenu) {
Animated.timing(this.state.sideMenuContentOpacity, {
toValue: this.props.showSideMenu ? 0.5 : 0,
@@ -753,7 +752,7 @@ class AppComponent extends React.Component {
}
}
private async backButtonHandler() {
async backButtonHandler() {
if (this.props.noteSelectionEnabled) {
this.props.dispatch({ type: 'NOTE_SELECTION_END' });
return true;
@@ -774,7 +773,7 @@ class AppComponent extends React.Component {
return false;
}
private async handleShareData() {
async handleShareData() {
const sharedData = await ShareExtension.data();
if (sharedData) {
reg.logger().info('Received shared data');
@@ -786,14 +785,14 @@ class AppComponent extends React.Component {
}
}
public UNSAFE_componentWillReceiveProps(newProps: any) {
UNSAFE_componentWillReceiveProps(newProps: any) {
if (newProps.syncStarted != this.lastSyncStarted_) {
if (!newProps.syncStarted) FoldersScreenUtils.refreshFolders();
this.lastSyncStarted_ = newProps.syncStarted;
}
}
private sideMenu_change(isOpen: boolean) {
sideMenu_change(isOpen: boolean) {
// Make sure showSideMenu property of state is updated
// when the menu is open/closed.
this.props.dispatch({
@@ -801,7 +800,7 @@ class AppComponent extends React.Component {
});
}
public render() {
render() {
if (this.props.appState != 'ready') return null;
const theme = themeStyle(this.props.themeId);

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-htmlparser2",
"version": "4.1.34",
"version": "4.1.33",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,7 +1,7 @@
{
"name": "@joplin/fork-htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser",
"version": "4.1.34",
"version": "4.1.33",
"author": "Felix Boehm <me@feedic.com>",
"publishConfig": {
"access": "public"

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-sax",
"version": "1.2.38",
"version": "1.2.37",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -2,7 +2,7 @@
"name": "@joplin/fork-sax",
"description": "An evented streaming XML parser in JavaScript",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"version": "1.2.38",
"version": "1.2.37",
"main": "lib/sax.js",
"publishConfig": {
"access": "public"

View File

@@ -259,14 +259,6 @@ export default class JoplinDatabase extends Database {
folders: {},
resources: {},
tags: {},
item_changes: {
type: 'The type of change - either 1 (created), 2 (updated) or 3 (deleted)',
created_time: 'When the event was generated',
item_type: 'The item type (see table above for the list of item types)',
item_id: 'The item ID',
before_change_item: 'Unused',
source: 'Unused',
},
};
const baseItems = ['notes', 'folders', 'tags', 'resources'];

View File

@@ -1,4 +1,6 @@
export function sortByValue(object: any) {
const ObjectUtils = {};
ObjectUtils.sortByValue = function(object) {
const temp = [];
for (const k in object) {
if (!object.hasOwnProperty(k)) continue;
@@ -17,16 +19,16 @@ export function sortByValue(object: any) {
return v1 < v2 ? -1 : +1;
});
const output: any = {};
const output = {};
for (let i = 0; i < temp.length; i++) {
const item = temp[i];
output[item.key] = item.value;
}
return output;
}
};
export function fieldsEqual(o1: any, o2: any) {
ObjectUtils.fieldsEqual = function(o1, o2) {
if ((!o1 || !o2) && o1 !== o2) return false;
for (const k in o1) {
@@ -40,10 +42,10 @@ export function fieldsEqual(o1: any, o2: any) {
if (c1.length !== c2.length) return false;
return true;
}
};
export function convertValuesToFunctions(o: any) {
const output: any = {};
ObjectUtils.convertValuesToFunctions = function(o) {
const output = {};
for (const n in o) {
if (!o.hasOwnProperty(n)) continue;
output[n] = () => {
@@ -51,26 +53,11 @@ export function convertValuesToFunctions(o: any) {
};
}
return output;
}
};
export function isEmpty(o: any) {
ObjectUtils.isEmpty = function(o) {
if (!o) return true;
return Object.keys(o).length === 0 && o.constructor === Object;
}
};
// export function isStringifiable(o:any):boolean {
// if (o === null || o === undefined) return true;
// if (Array.isArray(o)) {
// for (const e of o) {
// if (!isStringifiable(e)) return false;
// }
// return true;
// }
// if (typeof o === 'object') {
// }
// return true;
// }
module.exports = ObjectUtils;

View File

@@ -1,13 +0,0 @@
// AUTO-GENERATED using `gulp buildCommandIndex`
import * as historyBackward from './historyBackward';
import * as historyForward from './historyForward';
import * as synchronize from './synchronize';
const index:any[] = [
historyBackward,
historyForward,
synchronize,
];
export default index;
// AUTO-GENERATED using `gulp buildCommandIndex`

View File

@@ -77,7 +77,7 @@ export default class Database {
throw new Error(`Invalid field format: ${field}`);
}
public escapeFields(fields: string[] | string): string[] | string {
escapeFields(fields: string[] | string): string[] | string {
if (fields == '*') return '*';
const output = [];
@@ -87,16 +87,6 @@ export default class Database {
return output;
}
public escapeFieldsToString(fields: string[] | string): string {
if (fields === '*') return '*';
const output = [];
for (let i = 0; i < fields.length; i++) {
output.push(this.escapeField(fields[i]));
}
return output.join(',');
}
async tryCall(callName: string, inputSql: StringOrSqlQuery, inputParams: SqlParams) {
let sql: string = null;
let params: SqlParams = null;

View File

@@ -21,38 +21,6 @@ export default class FsDriverBase {
throw new Error('Not implemented');
}
public async readFile(_path: string, _encoding: string = 'utf8'): Promise<any> {
throw new Error('Not implemented');
}
public async copy(_source: string, _dest: string) {
throw new Error('Not implemented');
}
public async mkdir(_path: string) {
throw new Error('Not implemented');
}
public async unlink(_path: string) {
throw new Error('Not implemented');
}
public async move(_source: string, _dest: string) {
throw new Error('Not implemented');
}
public async readFileChunk(_handle: any, _length: number, _encoding: string = 'base64'): Promise<string> {
throw new Error('Not implemented');
}
public async open(_path: string, _mode: any): Promise<any> {
throw new Error('Not implemented');
}
public async close(_handle: any): Promise<any> {
throw new Error('Not implemented');
}
public async readDirStats(_path: string, _options: ReadDirStatsOptions = null): Promise<Stat[]> {
throw new Error('Not implemented');
}

View File

@@ -1,18 +0,0 @@
import shim from '../shim';
const { useEffect } = shim.react();
export interface AsyncEffectEvent {
cancelled: boolean;
}
export type EffectFunction = (event: AsyncEffectEvent)=> Promise<void>;
export default function(effect: EffectFunction, dependencies: any[]) {
useEffect(() => {
const event: AsyncEffectEvent = { cancelled: false };
void effect(event);
return () => {
event.cancelled = true;
};
}, dependencies);
}

View File

@@ -1,13 +1,13 @@
import { revisionService, setupDatabaseAndSynchronizer, db, switchClient, msleep } from '../testing/test-utils';
import SearchEngine from '../services/searchengine/SearchEngine';
import ResourceService from '../services/ResourceService';
import ItemChangeUtils from '../services/ItemChangeUtils';
import Note from '../models/Note';
import ItemChange from '../models/ItemChange';
const { revisionService, setupDatabaseAndSynchronizer, db, switchClient } = require('../testing/test-utils.js');
const SearchEngine = require('../services/searchengine/SearchEngine').default;
const ResourceService = require('../services/ResourceService').default;
const ItemChangeUtils = require('../services/ItemChangeUtils').default;
const Note = require('../models/Note').default;
const ItemChange = require('../models/ItemChange').default;
let searchEngine: SearchEngine = null;
let searchEngine = null;
describe('models/ItemChange', function() {
describe('models_ItemChange', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
@@ -27,28 +27,17 @@ describe('models/ItemChange', function() {
const resourceService = new ResourceService();
await searchEngine.syncTables();
// If we run this now, it should not delete any change because
// the resource service has not yet processed the change
await ItemChangeUtils.deleteProcessedChanges(0);
expect(await ItemChange.lastChangeId()).toBe(1);
await resourceService.indexNoteResources();
await ItemChangeUtils.deleteProcessedChanges(0);
expect(await ItemChange.lastChangeId()).toBe(1);
await revisionService().collectRevisions();
// If we don't set a TTL it will default to 90 days so it won't delete
// either.
await ItemChangeUtils.deleteProcessedChanges();
expect(await ItemChange.lastChangeId()).toBe(1);
// All changes should be at least 4 ms old now
await msleep(4);
await resourceService.indexNoteResources();
await ItemChangeUtils.deleteProcessedChanges();
expect(await ItemChange.lastChangeId()).toBe(1);
// Now it should delete all changes older than 3 ms
await ItemChangeUtils.deleteProcessedChanges(3);
await revisionService().collectRevisions();
await ItemChangeUtils.deleteProcessedChanges();
expect(await ItemChange.lastChangeId()).toBe(0);
}));

View File

@@ -1,14 +1,8 @@
import BaseModel, { ModelType } from '../BaseModel';
import shim from '../shim';
import eventManager from '../eventManager';
import { ItemChangeEntity } from '../services/database/types';
const Mutex = require('async-mutex').Mutex;
export interface ChangeSinceIdOptions {
limit?: number;
fields?: string[];
}
export default class ItemChange extends BaseModel {
private static addChangeMutex_: any = new Mutex();
@@ -30,7 +24,7 @@ export default class ItemChange extends BaseModel {
return BaseModel.TYPE_ITEM_CHANGE;
}
public static async add(itemType: ModelType, itemId: string, type: number, changeSource: any = null, beforeChangeItemJson: string = null) {
static async add(itemType: ModelType, itemId: string, type: number, changeSource: any = null, beforeChangeItemJson: string = null) {
if (changeSource === null) changeSource = ItemChange.SOURCE_UNSPECIFIED;
if (!beforeChangeItemJson) beforeChangeItemJson = '';
@@ -63,14 +57,14 @@ export default class ItemChange extends BaseModel {
}
}
public static async lastChangeId() {
static async lastChangeId() {
const row = await this.db().selectOne('SELECT max(id) as max_id FROM item_changes');
return row && row.max_id ? row.max_id : 0;
}
// Because item changes are recorded in the background, this function
// can be used for synchronous code, in particular when unit testing.
public static async waitForAllSaved() {
static async waitForAllSaved() {
return new Promise((resolve) => {
const iid = shim.setInterval(() => {
if (!ItemChange.saveCalls_.length) {
@@ -81,32 +75,8 @@ export default class ItemChange extends BaseModel {
});
}
public static async deleteOldChanges(lowestChangeId: number, itemMinTtl: number) {
static async deleteOldChanges(lowestChangeId: number) {
if (!lowestChangeId) return;
const cutOffDate = Date.now() - itemMinTtl;
return this.db().exec(`
DELETE FROM item_changes
WHERE id <= ?
AND created_time <= ?
`, [lowestChangeId, cutOffDate]);
return this.db().exec('DELETE FROM item_changes WHERE id <= ?', [lowestChangeId]);
}
public static async changesSinceId(changeId: number, options: ChangeSinceIdOptions = null): Promise<ItemChangeEntity[]> {
options = {
limit: 100,
fields: ['id', 'item_type', 'item_id', 'type', 'created_time'],
...options,
};
return this.db().selectAll(`
SELECT ${this.db().escapeFieldsToString(options.fields)}
FROM item_changes
WHERE id > ?
ORDER BY id
LIMIT ?
`, [changeId, options.limit]);
}
}

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/lib",
"version": "2.4.1",
"version": "2.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/lib",
"version": "2.4.1",
"version": "2.4.0",
"license": "ISC",
"dependencies": {
"async-mutex": "^0.1.3",
@@ -64,7 +64,6 @@
"xml2js": "^0.4.19"
},
"devDependencies": {
"@types/css": "^0.0.33",
"@types/fs-extra": "^9.0.6",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",
@@ -1004,12 +1003,6 @@
"@babel/types": "^7.3.0"
}
},
"node_modules/@types/css": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/css/-/css-0.0.33.tgz",
"integrity": "sha512-qjeDgh86R0LIeEM588q65yatc8Yyo/VvSIYFqq8JOIHDolhGNX0rz7k/OuxqDpnpqlefoHj8X4Ai/6hT9IWtKQ==",
"dev": true
},
"node_modules/@types/fs-extra": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz",
@@ -9695,12 +9688,6 @@
"@babel/types": "^7.3.0"
}
},
"@types/css": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/css/-/css-0.0.33.tgz",
"integrity": "sha512-qjeDgh86R0LIeEM588q65yatc8Yyo/VvSIYFqq8JOIHDolhGNX0rz7k/OuxqDpnpqlefoHj8X4Ai/6hT9IWtKQ==",
"dev": true
},
"@types/fs-extra": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "2.4.1",
"version": "2.4.0",
"description": "Joplin Core library",
"author": "Laurent Cozic",
"homepage": "",
@@ -16,7 +16,6 @@
"test-ci": "npm run test"
},
"devDependencies": {
"@types/css": "^0.0.33",
"@types/fs-extra": "^9.0.6",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",
@@ -26,11 +25,11 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.34",
"@joplin/fork-sax": "^1.2.38",
"@joplin/renderer": "^2.4.1",
"@joplin/turndown": "^4.0.56",
"@joplin/turndown-plugin-gfm": "^1.0.38",
"@joplin/fork-htmlparser2": "^4.1.33",
"@joplin/fork-sax": "^1.2.37",
"@joplin/renderer": "~2.4",
"@joplin/turndown": "^4.0.55",
"@joplin/turndown-plugin-gfm": "^1.0.37",
"async-mutex": "^0.1.3",
"aws-sdk": "^2.588.0",
"base-64": "^0.1.0",

View File

@@ -1,10 +1,8 @@
import Setting from '../models/Setting';
import ItemChange from '../models/ItemChange';
const dayMs = 86400000;
export default class ItemChangeUtils {
static async deleteProcessedChanges(itemMinTtl: number = dayMs * 90) {
static async deleteProcessedChanges() {
const lastProcessedChangeIds = [
Setting.value('resourceService.lastProcessedChangeId'),
Setting.value('searchEngine.lastProcessedChangeId'),
@@ -12,6 +10,6 @@ export default class ItemChangeUtils {
];
const lowestChangeId = Math.min(...lastProcessedChangeIds);
await ItemChange.deleteOldChanges(lowestChangeId, itemMinTtl);
await ItemChange.deleteOldChanges(lowestChangeId);
}
}

View File

@@ -110,7 +110,7 @@ export async function findMasterKeyPassword(service: EncryptionService, masterKe
return masterPassword;
}
logger.info('findMasterKeyPassword: No master password is defined - trying to get master key specific password');
logger.warn('findMasterKeyPassword: No master password is defined - trying to get master key specific password');
const passwords = Setting.value('encryption.passwordCache');
return passwords[masterKey.id];

View File

@@ -5,28 +5,27 @@ import { ImportExportResult } from './types';
import Setting from '../../models/Setting';
export default class InteropService_Importer_Base {
private metadata_: any = null;
protected sourcePath_: string = '';
protected options_: any = {};
public setMetadata(md: any) {
setMetadata(md: any) {
this.metadata_ = md;
}
public metadata() {
metadata() {
return this.metadata_;
}
public async init(sourcePath: string, options: any) {
async init(sourcePath: string, options: any) {
this.sourcePath_ = sourcePath;
this.options_ = options;
}
// @ts-ignore
public async exec(result: ImportExportResult): Promise<ImportExportResult> {}
async exec(result: ImportExportResult): Promise<ImportExportResult> {}
protected async temporaryDirectory_(createIt: boolean) {
async temporaryDirectory_(createIt: boolean) {
const md5 = require('md5');
const tempDir = `${Setting.value('tempDir')}/${md5(Math.random() + Date.now())}`;
if (createIt) await require('fs-extra').mkdirp(tempDir);

View File

@@ -5,27 +5,15 @@ export default class InteropService_Importer_Custom extends InteropService_Impor
private module_: Module = null;
public constructor(handler: Module) {
constructor(handler: Module) {
super();
this.module_ = handler;
}
public async exec(result: ImportExportResult): Promise<ImportExportResult> {
// When passing the options to the plugin, we strip off any function
// because they won't serialized over ipc.
const processedOptions: any = {};
if (this.options_) {
for (const [k, v] of Object.entries(this.options_)) {
if (typeof v === 'function') continue;
processedOptions[k] = v;
}
}
async exec(result: ImportExportResult): Promise<ImportExportResult> {
return this.module_.onExec({
sourcePath: this.sourcePath_,
options: processedOptions,
options: this.options_,
warnings: result.warnings,
});
}

View File

@@ -24,13 +24,6 @@ export default function manifestFromObject(o: any): PluginManifest {
return o[name];
};
const getBoolean = (name: string, required: boolean = true, defaultValue: boolean = false): boolean => {
if (required && !o[name]) throw new Error(`Missing required field: ${name}`);
if (!o[name]) return defaultValue;
if (typeof o[name] !== 'boolean') throw new Error(`Field must be a boolean: ${name}`);
return o[name];
};
const permissions: PluginPermission[] = [];
const manifest: PluginManifest = {
@@ -46,8 +39,6 @@ export default function manifestFromObject(o: any): PluginManifest {
repository_url: getString('repository_url', false),
keywords: getStrings('keywords', false),
permissions: permissions,
_recommended: getBoolean('_recommended', false, false),
};
validatePluginId(manifest.id);

View File

@@ -20,6 +20,4 @@ export interface PluginManifest {
_publish_hash?: string;
_publish_commit?: string;
_npm_package_name?: string;
_obsolete?: boolean;
_recommended?: boolean;
}

View File

@@ -9,7 +9,6 @@ import route_master_keys from './routes/master_keys';
import route_search from './routes/search';
import route_ping from './routes/ping';
import route_auth from './routes/auth';
import route_events from './routes/events';
const { ltrimSlashes } = require('../../path-utils');
const md5 = require('md5');
@@ -44,9 +43,6 @@ interface RequestQuery {
// Auth token
auth_token?: string;
// Event cursor
cursor?: string;
}
export interface Request {
@@ -108,7 +104,6 @@ export default class Api {
search: route_search,
services: this.action_services.bind(this),
auth: route_auth,
events: route_events,
};
this.dispatch = this.dispatch.bind(this);

View File

@@ -1,94 +0,0 @@
import { ModelType } from '../../../BaseModel';
import ItemChange from '../../../models/ItemChange';
import Note from '../../../models/Note';
import { expectThrow, setupDatabaseAndSynchronizer, switchClient } from '../../../testing/test-utils';
import { ItemChangeEntity } from '../../database/types';
import Api, { RequestMethod } from '../Api';
let api: Api = null;
describe('routes/events', function() {
beforeEach(async (done) => {
api = new Api();
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should retrieve the latest events', async () => {
let cursor = '0';
{
const response = await api.route(RequestMethod.GET, 'events', { cursor });
expect(response.cursor).toBe('0');
}
const note1 = await Note.save({ title: 'toto' });
await Note.save({ id: note1.id, title: 'tutu' });
const note2 = await Note.save({ title: 'tata' });
await ItemChange.waitForAllSaved();
{
const response = await api.route(RequestMethod.GET, 'events', { cursor });
expect(response.cursor).toBe('3');
expect(response.items.length).toBe(2);
expect(response.has_more).toBe(false);
expect(response.items.map((it: ItemChangeEntity) => it.item_id).sort()).toEqual([note1.id, note2.id].sort());
cursor = response.cursor;
}
{
const response = await api.route(RequestMethod.GET, 'events', { cursor });
expect(response.cursor).toBe(cursor);
expect(response.items.length).toBe(0);
expect(response.has_more).toBe(false);
}
await Note.save({ id: note2.id, title: 'titi' });
await ItemChange.waitForAllSaved();
{
const response = await api.route(RequestMethod.GET, 'events', { cursor });
expect(response.cursor).toBe('4');
expect(response.items.length).toBe(1);
expect(response.items[0].item_id).toBe(note2.id);
}
});
it('should limit the number of response items', async () => {
const promises = [];
for (let i = 0; i < 101; i++) {
promises.push(Note.save({ title: 'toto' }));
}
await Promise.all(promises);
await ItemChange.waitForAllSaved();
const response1 = await api.route(RequestMethod.GET, 'events', { cursor: '0' });
expect(response1.items.length).toBe(100);
expect(response1.has_more).toBe(true);
const response2 = await api.route(RequestMethod.GET, 'events', { cursor: response1.cursor });
expect(response2.items.length).toBe(1);
expect(response2.has_more).toBe(false);
});
it('should retrieve a single item', async () => {
const beforeTime = Date.now();
const note = await Note.save({ title: 'toto' });
await ItemChange.waitForAllSaved();
const response = await api.route(RequestMethod.GET, 'events/1');
expect(response.item_type).toBe(ModelType.Note);
expect(response.type).toBe(1);
expect(response.item_id).toBe(note.id);
expect(response.created_time).toBeGreaterThanOrEqual(beforeTime);
await expectThrow(async () => api.route(RequestMethod.GET, 'events/1234'));
});
});

View File

@@ -1,39 +0,0 @@
import { ModelType } from '../../../BaseModel';
import { Request, RequestMethod } from '../Api';
import { ErrorBadRequest, ErrorNotFound } from '../utils/errors';
import ItemChange, { ChangeSinceIdOptions } from '../../../models/ItemChange';
import requestFields from '../utils/requestFields';
export default async function(request: Request, id: string = null, _link: string = null) {
if (request.method === RequestMethod.GET) {
const options: ChangeSinceIdOptions = {
limit: 100,
fields: requestFields(request, ModelType.ItemChange, ['id', 'item_type', 'item_id', 'type', 'created_time']),
};
if (!id) {
if (!('cursor' in request.query)) {
return {
items: [],
has_more: false,
cursor: (await ItemChange.lastChangeId()).toString(),
};
} else {
const cursor = Number(request.query.cursor);
if (isNaN(cursor)) throw new ErrorBadRequest(`Invalid cursor: ${request.query.cursor}`);
const changes = await ItemChange.changesSinceId(cursor, options);
return {
items: changes,
has_more: changes.length >= options.limit,
cursor: (changes.length ? changes[changes.length - 1].id : cursor).toString(),
};
}
} else {
const change = await ItemChange.load(id, { fields: options.fields });
if (!change) throw new ErrorNotFound();
return change;
}
}
}

View File

@@ -11,18 +11,13 @@ function defaultFieldsByModelType(modelType: number): string[] {
return output;
}
export default function(request: Request, modelType: number, defaultFields: string[] = null) {
const getDefaults = () => {
if (defaultFields) return defaultFields;
return defaultFieldsByModelType(modelType);
};
export default function(request: Request, modelType: number) {
const query = request.query;
if (!query || !query.fields) return getDefaults();
if (!query || !query.fields) return defaultFieldsByModelType(modelType);
if (Array.isArray(query.fields)) return query.fields.slice();
const fields = query.fields
.split(',')
.map((f: string) => f.trim())
.filter((f: string) => !!f);
return fields.length ? fields : getDefaults();
return fields.length ? fields : defaultFieldsByModelType(modelType);
}

View File

@@ -1,28 +0,0 @@
import cssToTheme from './cssToTheme';
describe('cssToTheme', function() {
it('should convert a CSS string to a theme', async () => {
const input = `
:root {
--joplin-appearence: light;
--joplin-color: #333333;
--joplin-background-color: #778899;
/* Should skip this comment and empty lines */
--joplin-background-color-transparent: rgba(255,255,255,0.9);
}`;
const expected = {
appearence: 'light',
color: '#333333',
backgroundColor: '#778899',
backgroundColorTransparent: 'rgba(255,255,255,0.9)',
};
const actual = cssToTheme(input, 'test.css');
expect(actual).toEqual(expected);
});
});

View File

@@ -1,46 +0,0 @@
import { Theme } from '../../themes/type';
// Need to include it that way due to a bug in the lib:
// https://github.com/reworkcss/css/pull/146#issuecomment-740412799
const cssParse = require('css/lib/parse');
function formatCssToThemeVariable(cssVariable: string): string {
const elements = cssVariable.substr(2).split('-');
if (elements[0] !== 'joplin') throw new Error(`CSS variable name must start with "--joplin": ${cssVariable}`);
elements.splice(0, 1);
return elements.map((e, i) => {
const c = i === 0 ? e[0] : e[0].toUpperCase();
return c + e.substr(1);
}).join('');
}
// function unquoteValue(v:string):string {
// if (v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"')) return v.substr(1, v.length - 2);
// return v;
// }
export default function cssToTheme(css: string, sourceFilePath: string): Theme {
const o = cssParse(css, {
silent: false,
source: sourceFilePath,
});
if (!o?.stylesheet?.rules?.length) throw new Error(`Invalid CSS color file: ${sourceFilePath}`);
// Need "as any" because outdated TS definition file
const rootRule = o.stylesheet.rules[0];
if (!rootRule.selectors.includes(':root')) throw new Error('`:root` rule not found');
const declarations: any[] = rootRule.declarations;
const output: any = {};
for (const declaration of declarations) {
if (declaration.type !== 'declaration') continue; // Skip comment lines
output[formatCssToThemeVariable(declaration.property)] = declaration.value;
}
return output;
}

View File

@@ -1,27 +0,0 @@
import { Theme } from '../../themes/type';
import { filename } from '../../path-utils';
import shim from '../../shim';
import cssToTheme from './cssToTheme';
export default async function(cssBaseDir: string): Promise<Record<string, Theme>> {
const themeDirs = (await shim.fsDriver().readDirStats(cssBaseDir)).filter((f: any) => f.isDirectory());
const output: Record<string, Theme> = {};
for (const themeDir of themeDirs) {
const themeName = filename(themeDir.path);
const cssFile = `${cssBaseDir}/${themeDir.path}/colors.css`;
const cssContent = await shim.fsDriver().readFile(cssFile, 'utf8');
let themeId = themeName;
const manifestFile = `${cssBaseDir}/${themeDir.path}/manifest.json`;
if (await shim.fsDriver().exists(manifestFile)) {
const manifest = JSON.parse(await shim.fsDriver().readFile(manifestFile, 'utf8'));
if (manifest.id) themeId = manifest.id;
}
output[themeId] = cssToTheme(cssContent, cssFile);
}
return output;
}

View File

@@ -1,105 +0,0 @@
import { Theme, ThemeAppearance } from '../../themes/type';
import themeToCss from './themeToCss';
const input: Theme = {
appearance: ThemeAppearance.Light,
// Color scheme "1" is the basic one, like used to display the note
// content. It's basically dark gray text on white background
backgroundColor: '#ffffff',
backgroundColorTransparent: 'rgba(255,255,255,0.9)',
oddBackgroundColor: '#eeeeee',
color: '#32373F', // For regular text
colorError: 'red',
colorWarn: 'rgb(228,86,0)',
colorWarnUrl: '#155BDA',
colorFaded: '#7C8B9E', // For less important text
colorBright: '#000000', // For important text
dividerColor: '#dddddd',
selectedColor: '#e5e5e5',
urlColor: '#155BDA',
// Color scheme "2" is used for the sidebar. It's white text over
// dark blue background.
backgroundColor2: '#313640',
color2: '#ffffff',
selectedColor2: '#131313',
colorError2: '#ff6c6c',
colorWarn2: '#ffcb81',
// Color scheme "3" is used for the config screens for example/
// It's dark text over gray background.
backgroundColor3: '#F4F5F6',
backgroundColorHover3: '#CBDAF1',
color3: '#738598',
// Color scheme "4" is used for secondary-style buttons. It makes a white
// button with blue text.
backgroundColor4: '#ffffff',
color4: '#2D6BDC',
raisedBackgroundColor: '#e5e5e5',
raisedColor: '#222222',
searchMarkerBackgroundColor: '#F7D26E',
searchMarkerColor: 'black',
warningBackgroundColor: '#FFD08D',
tableBackgroundColor: 'rgb(247, 247, 247)',
codeBackgroundColor: 'rgb(243, 243, 243)',
codeBorderColor: 'rgb(220, 220, 220)',
codeColor: 'rgb(0,0,0)',
blockQuoteOpacity: 0.7,
codeMirrorTheme: 'default',
codeThemeCss: 'atom-one-light.css',
};
const expected = `
:root {
--joplin-appearance: light;
--joplin-background-color: #ffffff;
--joplin-background-color-transparent: rgba(255,255,255,0.9);
--joplin-odd-background-color: #eeeeee;
--joplin-color: #32373F;
--joplin-color-error: red;
--joplin-color-warn: rgb(228,86,0);
--joplin-color-warn-url: #155BDA;
--joplin-color-faded: #7C8B9E;
--joplin-color-bright: #000000;
--joplin-divider-color: #dddddd;
--joplin-selected-color: #e5e5e5;
--joplin-url-color: #155BDA;
--joplin-background-color2: #313640;
--joplin-color2: #ffffff;
--joplin-selected-color2: #131313;
--joplin-color-error2: #ff6c6c;
--joplin-color-warn2: #ffcb81;
--joplin-background-color3: #F4F5F6;
--joplin-background-color-hover3: #CBDAF1;
--joplin-color3: #738598;
--joplin-background-color4: #ffffff;
--joplin-color4: #2D6BDC;
--joplin-raised-background-color: #e5e5e5;
--joplin-raised-color: #222222;
--joplin-search-marker-background-color: #F7D26E;
--joplin-search-marker-color: black;
--joplin-warning-background-color: #FFD08D;
--joplin-table-background-color: rgb(247, 247, 247);
--joplin-code-background-color: rgb(243, 243, 243);
--joplin-code-border-color: rgb(220, 220, 220);
--joplin-code-color: rgb(0,0,0);
--joplin-block-quote-opacity: 0.7;
--joplin-code-mirror-theme: default;
--joplin-code-theme-css: atom-one-light.css;
}`;
describe('themeToCss', function() {
it('should a theme to a CSS string', async () => {
const actual = themeToCss(input);
expect(actual.trim()).toBe(expected.trim());
});
});

View File

@@ -1,24 +0,0 @@
import { Theme } from '../../themes/type';
const { camelCaseToDash, formatCssSize } = require('../../string-utils');
// function quoteCssValue(name: string, value: string): string {
// const needsQuote = ['appearance', 'codeMirrorTheme', 'codeThemeCss'].includes(name);
// if (needsQuote) return `'${value}'`;
// return value;
// }
export default function(theme: Theme) {
const lines = [];
lines.push(':root {');
for (const name in theme) {
const value = (theme as any)[name];
const newName = `--joplin-${camelCaseToDash(name)}`;
const formattedValue = typeof value === 'number' && newName.indexOf('opacity') < 0 ? formatCssSize(value) : value;
lines.push(`\t${newName}: ${formattedValue};`);
}
lines.push('}');
return lines.join('\n');
}

View File

@@ -23,7 +23,7 @@ const themes: any = {
[Setting.THEME_OLED_DARK]: theme_oledDark,
};
export function themeById(themeId: string) {
function themeById(themeId: string) {
if (!themes[themeId]) throw new Error(`Invalid theme ID: ${themeId}`);
const output = Object.assign({}, themes[themeId]);
@@ -365,7 +365,7 @@ function addExtraStyles(style: any) {
const themeCache_: any = {};
export function themeStyle(themeId: number) {
function themeStyle(themeId: number) {
if (!themeId) throw new Error('Theme must be specified');
const zoomRatio = 1;
@@ -405,7 +405,7 @@ const cachedStyles_: any = {
// cacheKey must be a globally unique key, and must change whenever
// the dependencies of the style change. If the style depends only
// on the theme, a static string can be provided as a cache key.
export function buildStyle(cacheKey: any, themeId: number, callback: Function) {
function buildStyle(cacheKey: any, themeId: number, callback: Function) {
cacheKey = Array.isArray(cacheKey) ? cacheKey.join('_') : cacheKey;
// We clear the cache whenever switching themes
@@ -425,3 +425,5 @@ export function buildStyle(cacheKey: any, themeId: number, callback: Function) {
return cachedStyles_.styles[cacheKey].style;
}
export { themeStyle, buildStyle, themeById };

View File

@@ -10,8 +10,18 @@ import updateReadme from './lib/updateReadme';
import { NpmPackage } from './lib/types';
import gitCompareUrl from './lib/gitCompareUrl';
import commandUpdateRelease from './commands/updateRelease';
import { isJoplinPluginPackage, readJsonFile } from './lib/utils';
import { applyManifestOverrides, getObsoleteManifests, readManifestOverrides } from './lib/overrideUtils';
function stripOffPackageOrg(name: string): string {
const n = name.split('/');
if (n[0][0] === '@') n.splice(0, 1);
return n.join('/');
}
function isJoplinPluginPackage(pack: any): boolean {
if (!pack.keywords || !pack.keywords.includes('joplin-plugin')) return false;
if (stripOffPackageOrg(pack.name).indexOf('joplin-plugin') !== 0) return false;
return true;
}
function pluginInfoFromSearchResults(results: any[]): NpmPackage[] {
const output: NpmPackage[] = [];
@@ -34,13 +44,21 @@ async function checkPluginRepository(dirPath: string, dryRun: boolean) {
if (!(await fs.pathExists(`${dirPath}/.git`))) throw new Error(`Directory is not a Git repository: ${dirPath}`);
const previousDir = chdir(dirPath);
if (!dryRun) {
await gitRepoCleanTry();
await gitPullTry();
}
await gitRepoCleanTry();
if (!dryRun) await gitPullTry();
chdir(previousDir);
}
async function readJsonFile(manifestPath: string, defaultValue: any = null): Promise<any> {
if (!(await fs.pathExists(manifestPath))) {
if (defaultValue === null) throw new Error(`No such file: ${manifestPath}`);
return defaultValue;
}
const content = await fs.readFile(manifestPath, 'utf8');
return JSON.parse(content);
}
async function extractPluginFilesFromPackage(existingManifests: any, workDir: string, packageName: string, destDir: string): Promise<any> {
const previousDir = chdir(workDir);
@@ -129,14 +147,14 @@ function chdir(path: string): string {
return previous;
}
async function processNpmPackage(npmPackage: NpmPackage, repoDir: string, dryRun: boolean) {
async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
const tempDir = `${repoDir}/temp`;
const obsoleteManifestsPath = path.resolve(repoDir, 'obsoletes.json');
await fs.mkdirp(tempDir);
const originalPluginManifests = await readManifests(repoDir);
const manifestOverrides = await readManifestOverrides(repoDir);
const obsoleteManifests = getObsoleteManifests(manifestOverrides);
const obsoleteManifests = await readJsonFile(obsoleteManifestsPath, {});
const existingManifests = {
...originalPluginManifests,
...obsoleteManifests,
@@ -181,8 +199,6 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string, dryRun
...manifests,
};
manifests = applyManifestOverrides(manifests, manifestOverrides);
await writeManifests(repoDir, manifests);
await updateReadme(`${repoDir}/README.md`, manifests);
}
@@ -190,13 +206,11 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string, dryRun
chdir(repoDir);
await fs.remove(tempDir);
if (!dryRun) {
if (!(await gitRepoClean())) {
await execCommand2('git add -A', { showOutput: false });
await execCommand2(['git', 'commit', '-m', commitMessage(actionType, manifest, previousManifest, npmPackage, error)], { showOutput: false });
} else {
console.info('Nothing to commit');
}
if (!(await gitRepoClean())) {
await execCommand2('git add -A', { showOutput: false });
await execCommand2(['git', 'commit', '-m', commitMessage(actionType, manifest, previousManifest, npmPackage, error)], { showOutput: false });
} else {
console.info('Nothing to commit');
}
}
@@ -209,40 +223,36 @@ async function commandBuild(args: CommandBuildArgs) {
await checkPluginRepository(repoDir, dryRun);
// When starting, always update and commit README, in case something has
// been updated via a pull request. We do that separately so that the README
// update doesn't get mixed up with plugin updates, as in this example:
// been updated via a pull request (for example obsoletes.json being
// modified). We do that separately so that the README update doesn't get
// mixed up with plugin updates, as in this example:
// https://github.com/joplin/plugins/commit/8a65bbbf64bf267674f854a172466ffd4f07c672
const manifests = await readManifests(repoDir);
await updateReadme(`${repoDir}/README.md`, manifests);
const previousDir = chdir(repoDir);
if (!dryRun) {
if (!(await gitRepoClean())) {
console.info('Updating README...');
await execCommand2('git add -A', { showOutput: true });
await execCommand2('git commit -m "Update README"', { showOutput: true });
}
if (!(await gitRepoClean())) {
console.info('Updating README...');
await execCommand2('git add -A', { showOutput: true });
await execCommand2('git commit -m "Update README"', { showOutput: true });
}
chdir(previousDir);
const searchResults = (await execCommand2('npm search joplin-plugin --searchlimit 5000 --json', { showOutput: false })).trim();
const npmPackages = pluginInfoFromSearchResults(JSON.parse(searchResults));
for (const npmPackage of npmPackages) {
await processNpmPackage(npmPackage, repoDir, dryRun);
await processNpmPackage(npmPackage, repoDir);
}
if (!dryRun) {
await commandUpdateRelease(args);
if (!(await gitRepoClean())) {
await execCommand2('git add -A', { showOutput: true });
await execCommand2('git commit -m "Update stats"', { showOutput: true });
}
await execCommand2('git push');
}
if (!dryRun) await execCommand2('git push');
}
async function commandVersion() {

View File

@@ -1,95 +0,0 @@
import { applyManifestOverrides, getObsoleteManifests, ManifestOverrides } from './overrideUtils';
describe('overrideUtils', () => {
test('should get the obsolete manifests', () => {
const manifestOverrides: any = {
'ambrt.backlinksToNote': {
'manifest_version': 1,
'id': 'ambrt.backlinksToNote',
'app_min_version': '1.5',
'version': '1.0.2',
'name': 'Backlinks to note',
'description': 'Creates backlinks to opened note',
'author': 'a',
'homepage_url': 'https://discourse.joplinapp.org/t/insert-referencing-notes-backlinks-plugin/13632',
'_publish_hash': 'sha256:5676da6b9ad71fc5a9779d3bde13f17de5352344711e135f0db8c62c6dbb5696',
'_publish_commit': 'master:19e515bd67e51cc37bd270a32d2898ca009a0de2',
'_npm_package_name': 'joplin-plugin-backlinks',
'_obsolete': true,
},
'MyPlugin': {
'manifest_version': 1,
'id': 'MyPlugin',
'app_min_version': '1.6',
'version': '1.0.0',
'name': 'Testing New Plugin',
'description': 'bla',
'author': 'bla',
'homepage_url': 'bla',
'repository_url': 'bla',
'_publish_hash': 'sha256:065285d06ea3c084e7f8f8c23583de8d70c4d586274a242c4c750f6faad8c7cb',
'_publish_commit': '',
'_npm_package_name': 'joplin-plugin-testing-new-plugin',
'_obsolete': true,
},
'io.github.jackgruber.copytags': {
'_recommended': true,
},
};
const obsoletes = getObsoleteManifests(manifestOverrides);
expect(Object.keys(obsoletes).sort()).toEqual(['MyPlugin', 'ambrt.backlinksToNote']);
expect(obsoletes['ambrt.backlinksToNote'].description).toBe('Creates backlinks to opened note');
});
test('should apply the overrides', () => {
const manifests: any = {
'io.github.jackgruber.copytags': {
'manifest_version': 1,
'id': 'io.github.jackgruber.copytags',
'app_min_version': '1.6.2',
'version': '1.0.1',
'name': 'Tagging',
'description': 'Plugin to extend the Joplin tagging menu with a coppy all tags and a tagging dialog with more control. (Formerly Copy Tags).',
'author': 'JackGruber',
'homepage_url': 'https://github.com/JackGruber/joplin-plugin-tagging/blob/master/README.md',
'repository_url': 'https://github.com/JackGruber/joplin-plugin-tagging',
'keywords': [
'duplicate',
'copy',
'tags',
'tagging',
'tag',
],
'_publish_hash': 'sha256:88daaf234a9b47e5644a8de6f830a801d12edbe41ea5364d994773e89eeafeef',
'_publish_commit': 'master:64c0510e3236df7788a8d10ec28dcfbb4c2bdbb7',
'_npm_package_name': 'joplin-plugin-copytags',
},
'joplin.plugin.ambrt.backlinksToNote': {
'manifest_version': 1,
'id': 'joplin.plugin.ambrt.backlinksToNote',
'app_min_version': '1.7',
'version': '2.0.11',
'name': 'Automatic Backlinks to note',
'description': 'Creates backlinks to opened note, also in automatic way',
'author': 'ambrt,cyingfan',
'homepage_url': 'https://discourse.joplinapp.org/t/insert-referencing-notes-backlinks-plugin/13632',
'_publish_hash': 'sha256:df57930d1ab62d4297dad0bb1764888935fcbf6ca8c04e3a843e86a260735c51',
'_publish_commit': 'master:98102718a9c0fa9416d654451b18602798c4d3bb',
'_npm_package_name': 'joplin-plugin-backlinks',
},
};
const overrides: ManifestOverrides = {
'io.github.jackgruber.copytags': {
_recommended: true,
},
};
const updatedManifests = applyManifestOverrides(manifests, overrides);
expect(updatedManifests['io.github.jackgruber.copytags']._recommended).toBe(true);
expect(updatedManifests['joplin.plugin.ambrt.backlinksToNote']._recommended).toBe(undefined);
});
});

View File

@@ -1,51 +0,0 @@
import * as path from 'path';
import { readJsonFile } from './utils';
export interface ManifestOverride {
_obsolete?: boolean;
_recommended?: boolean;
}
export type ManifestOverrides = Record<string, ManifestOverride>;
export function applyManifestOverrides(manifests: any, overrides: ManifestOverrides) {
for (const [pluginId, override] of Object.entries(overrides)) {
const manifest: any = manifests[pluginId];
if (!manifest) {
// If the manifest does not exist in the destination, it means the
// plugin has been taken out, so the obsolete property should be set
// to `true`. If not, it's an error.
if (!override._obsolete) {
console.error(`Could not find manifest for plugin ${pluginId} when applying override`);
}
continue;
}
for (const [propName, propValue] of Object.entries(override)) {
manifest[propName] = propValue;
}
}
return manifests;
}
export function getObsoleteManifests(overrides: ManifestOverrides) {
const output: any = {};
for (const [pluginId, override] of Object.entries(overrides)) {
if (override._obsolete) {
output[pluginId] = override;
}
}
return output;
}
function pluginManifestOverridesPath(repoDir: string): string {
return path.resolve(repoDir, 'manifestOverrides.json');
}
export async function readManifestOverrides(repoDir: string): Promise<ManifestOverrides> {
return readJsonFile(pluginManifestOverridesPath(repoDir), {});
}

View File

@@ -1,23 +0,0 @@
import * as fs from 'fs-extra';
export async function readJsonFile(manifestPath: string, defaultValue: any = null): Promise<any> {
if (!(await fs.pathExists(manifestPath))) {
if (defaultValue === null) throw new Error(`No such file: ${manifestPath}`);
return defaultValue;
}
const content = await fs.readFile(manifestPath, 'utf8');
return JSON.parse(content);
}
function stripOffPackageOrg(name: string): string {
const n = name.split('/');
if (n[0][0] === '@') n.splice(0, 1);
return n.join('/');
}
export function isJoplinPluginPackage(pack: any): boolean {
if (!pack.keywords || !pack.keywords.includes('joplin-plugin')) return false;
if (stripOffPackageOrg(pack.name).indexOf('joplin-plugin') !== 0) return false;
return true;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/plugin-repo-cli",
"version": "2.4.1",
"version": "2.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/plugin-repo-cli",
"version": "2.4.1",
"version": "2.4.0",
"description": "",
"main": "index.js",
"bin": {
@@ -13,14 +13,13 @@
"tsc": "tsc --project tsconfig.json",
"watch": "tsc --watch --project tsconfig.json",
"test": "jest",
"test-ci": "npm run test",
"start": "node index.js"
"test-ci": "npm run test"
},
"author": "",
"license": "MIT",
"dependencies": {
"@joplin/lib": "^2.4.1",
"@joplin/tools": "^2.4.1",
"@joplin/lib": "~2.4",
"@joplin/tools": "~2.4",
"fs-extra": "^9.0.1",
"gh-release-assets": "^2.0.0",
"node-fetch": "^2.6.1",

View File

@@ -1,92 +0,0 @@
export default function(markdownIt: any) {
markdownIt.core.ruler.push('anchorHeader', (state: any): boolean => {
const tokens = state.tokens;
const Token = state.Token;
const doneNames = [];
const headingTextToAnchorName = (text: string, doneNames: string[]) => {
const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let lastWasDash = true;
let output = '';
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (allowed.indexOf(c) < 0) {
if (lastWasDash) continue;
lastWasDash = true;
output += '-';
} else {
lastWasDash = false;
output += c;
}
}
output = output.toLowerCase();
while (output.length && output[output.length - 1] === '-') {
output = output.substr(0, output.length - 1);
}
let temp = output;
let index = 1;
while (doneNames.indexOf(temp) >= 0) {
temp = `${output}-${index}`;
index++;
}
output = temp;
return output;
};
const createAnchorTokens = (anchorName: string) => {
const output = [];
{
const token = new Token('heading_anchor_open', 'a', 1);
token.attrs = [
['name', anchorName],
['href', `#${anchorName}`],
['class', 'heading-anchor'],
];
output.push(token);
}
{
const token = new Token('text', '', 0);
token.content = '🔗';
output.push(token);
}
{
const token = new Token('heading_anchor_close', 'a', -1);
output.push(token);
}
return output;
};
let insideHeading = false;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token.type === 'heading_open') {
insideHeading = true;
continue;
}
if (token.type === 'heading_close') {
insideHeading = false;
continue;
}
if (insideHeading && token.type === 'inline') {
const anchorName = headingTextToAnchorName(token.content, doneNames);
doneNames.push(anchorName);
const anchorTokens = createAnchorTokens(anchorName);
// token.children = anchorTokens.concat(token.children);
token.children = token.children.concat(anchorTokens);
}
}
return true;
});
}

View File

@@ -4,7 +4,6 @@ import HtmlToHtml from './HtmlToHtml';
import utils from './utils';
import setupLinkify from './MdToHtml/setupLinkify';
import validateLinks from './MdToHtml/validateLinks';
import headerAnchor from './headerAnchor';
const assetsToHeaders = require('./assetsToHeaders');
export {
@@ -14,7 +13,6 @@ export {
HtmlToHtml,
setupLinkify,
validateLinks,
headerAnchor,
assetsToHeaders,
utils,
};

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/renderer",
"version": "2.4.1",
"version": "2.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/renderer",
"version": "2.4.1",
"version": "2.4.0",
"license": "MIT",
"dependencies": {
"font-awesome-filetypes": "^2.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "2.4.1",
"version": "2.4.0",
"description": "The Joplin note renderer, used the mobile and desktop application",
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
"main": "index.js",
@@ -24,7 +24,7 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.34",
"@joplin/fork-htmlparser2": "^4.1.33",
"font-awesome-filetypes": "^2.1.0",
"fs-extra": "^8.1.0",
"highlight.js": "^11.2.0",

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/server",
"version": "2.4.3",
"version": "2.4.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/server",
"version": "2.4.3",
"version": "2.4.2",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.1",
"@koa/cors": "^3.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "2.4.3",
"version": "2.4.2",
"private": true,
"scripts": {
"start-dev": "nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",

View File

@@ -63,24 +63,4 @@ ul.pagination-list li {
.readable-block {
max-width: 740px;
}
a.heading-anchor {
display: inline-block;
opacity: 0;
width: 1.3em;
font-size: 0.7em;
margin-left: 0.4em;
line-height: 1em;
text-decoration: none;
transition: opacity 0.3s;
}
a.heading-anchor:hover,
h1:hover a.heading-anchor,
h2:hover a.heading-anchor,
h3:hover a.heading-anchor,
h4:hover a.heading-anchor,
h5:hover a.heading-anchor,
h6:hover a.heading-anchor {
opacity: 1;
}

View File

@@ -149,7 +149,7 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any
config_ = {
appVersion: packageJson.version,
appName,
isJoplinCloud: apiBaseUrl.includes('.joplincloud.com') || apiBaseUrl.includes('.joplincloud.local'),
isJoplinCloud: apiBaseUrl.includes('.joplincloud.com'),
env: envType,
rootDir: rootDir,
viewDir: viewDir,

View File

@@ -210,13 +210,13 @@ describe('UserModel', function() {
await models().user().save({
id: user1.id,
account_type: AccountType.Basic,
total_item_size: Math.round(accountByType(AccountType.Basic).max_total_item_size * 0.85),
total_item_size: accountByType(AccountType.Basic).max_total_item_size * 0.85,
});
await models().user().save({
id: user2.id,
account_type: AccountType.Pro,
total_item_size: Math.round(accountByType(AccountType.Pro).max_total_item_size * 0.2),
total_item_size: accountByType(AccountType.Pro).max_total_item_size * 0.2,
});
const emailBeforeCount = (await models().email().all()).length;
@@ -242,7 +242,7 @@ describe('UserModel', function() {
await models().user().save({
id: user2.id,
total_item_size: Math.round(accountByType(AccountType.Pro).max_total_item_size * 1.1),
total_item_size: accountByType(AccountType.Pro).max_total_item_size * 1.1,
});
// User upload should be enabled at this point

View File

@@ -383,8 +383,8 @@ export default class UserModel extends BaseModel<User> {
.db(this.tableName)
.select(['id', 'total_item_size', 'max_total_item_size', 'account_type', 'email', 'full_name'])
.where(function() {
void this.whereRaw('total_item_size > ? AND account_type = ?', [Math.round(alertLimit1 * basicAccount.max_total_item_size), AccountType.Basic])
.orWhereRaw('total_item_size > ? AND account_type = ?', [Math.round(alertLimit1 * proAccount.max_total_item_size), AccountType.Pro]);
void this.whereRaw('total_item_size > ? AND account_type = ?', [alertLimit1 * basicAccount.max_total_item_size, AccountType.Basic])
.orWhereRaw('total_item_size > ? AND account_type = ?', [alertLimit1 * proAccount.max_total_item_size, AccountType.Pro]);
})
// Users who are disabled or who cannot upload already received the
// notification.

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