You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-29 07:46:13 +02:00
Compare commits
100 Commits
v2.4.1
...
theme_to_c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99b5847244 | ||
|
|
4ad7e1b245 | ||
|
|
b6f889baca | ||
|
|
54fb4b0946 | ||
|
|
80762572cf | ||
|
|
8e5d209d3c | ||
|
|
f71dad6d09 | ||
|
|
f5891dfae8 | ||
|
|
736bbbd8ed | ||
|
|
973121addd | ||
|
|
95ad4c3177 | ||
|
|
71c470f59d | ||
|
|
c529b972e3 | ||
|
|
e6bff3f2e0 | ||
|
|
1bc674a1f9 | ||
|
|
f371bb8e59 | ||
|
|
7c85889c1f | ||
|
|
ab134807ea | ||
|
|
a5b3bb6058 | ||
|
|
8ab1cd984c | ||
|
|
e387d9a91b | ||
|
|
0793b1be59 | ||
|
|
19225abbcf | ||
|
|
85fa3288ab | ||
|
|
17f82c426a | ||
|
|
82331c9b93 | ||
|
|
886b6d1126 | ||
|
|
1a703c4ecd | ||
|
|
1126899769 | ||
|
|
a571d38862 | ||
|
|
4ab2fb73b7 | ||
|
|
78c7b79299 | ||
|
|
d97ba57dda | ||
|
|
9c44133bd0 | ||
|
|
dc008ecf64 | ||
|
|
93a4ad09bb | ||
|
|
9c1dc7898a | ||
|
|
6520a481ca | ||
|
|
5805a41249 | ||
|
|
b88b747ba6 | ||
|
|
ce89ee5bab | ||
|
|
596f679b1f | ||
|
|
78d5fd1385 | ||
|
|
0a98854e43 | ||
|
|
a8a0dd2dd6 | ||
|
|
ad931a738f | ||
|
|
3b6e6e45cf | ||
|
|
dec0a08954 | ||
|
|
b269c2fdb9 | ||
|
|
ad51090cdb | ||
|
|
bd4714037c | ||
|
|
75d118b19e | ||
|
|
9296b0ae36 | ||
|
|
1862030cff | ||
|
|
8a3eb8616e | ||
|
|
4c72de8bd8 | ||
|
|
d6da4299ad | ||
|
|
f19c4ab434 | ||
|
|
70efaddeaf | ||
|
|
814f602bd6 | ||
|
|
da6a5e3bb8 | ||
|
|
385b50d6ce | ||
|
|
43943299f3 | ||
|
|
f11ba29112 | ||
|
|
edf801a1ef | ||
|
|
f441177f7a | ||
|
|
1503a4e393 | ||
|
|
3ce23c75e6 | ||
|
|
5dd8862380 | ||
|
|
091945d9d6 | ||
|
|
30c7410cbe | ||
|
|
9b2094f688 | ||
|
|
4ba417a2f4 | ||
|
|
e3bd24f819 | ||
|
|
84d95c6dcb | ||
|
|
6e087bcb23 | ||
|
|
f922e9a239 | ||
|
|
82b157b491 | ||
|
|
2ae51acd29 | ||
|
|
f42fd0ecce | ||
|
|
62c5f433d7 | ||
|
|
e73a4b7286 | ||
|
|
6c18c6ddc7 | ||
|
|
20d1f74ee4 | ||
|
|
2386abea3e | ||
|
|
90621a8417 | ||
|
|
b02baa6891 | ||
|
|
ee46978389 | ||
|
|
2b6b4dd916 | ||
|
|
f0361bf80d | ||
|
|
ecf718005d | ||
|
|
305d0ffc49 | ||
|
|
f454c4e33b | ||
|
|
047883bd27 | ||
|
|
f118f5250f | ||
|
|
61161039c8 | ||
|
|
00504898f2 | ||
|
|
0a6390ed96 | ||
|
|
f909fe6670 | ||
|
|
b17d8bc533 |
103
.eslintignore
103
.eslintignore
@@ -64,6 +64,7 @@ packages/tools/PortableAppsLauncher
|
||||
packages/fork-*
|
||||
plugin_types/
|
||||
readme/
|
||||
**/commands/index.ts
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
packages/app-cli/app/LinkSelector.d.ts
|
||||
@@ -84,9 +85,6 @@ packages/app-cli/tests/HtmlToMd.js.map
|
||||
packages/app-cli/tests/MdToHtml.d.ts
|
||||
packages/app-cli/tests/MdToHtml.js
|
||||
packages/app-cli/tests/MdToHtml.js.map
|
||||
packages/app-cli/tests/MdToMd.d.ts
|
||||
packages/app-cli/tests/MdToMd.js
|
||||
packages/app-cli/tests/MdToMd.js.map
|
||||
packages/app-cli/tests/services/keychain/KeychainService.d.ts
|
||||
packages/app-cli/tests/services/keychain/KeychainService.js
|
||||
packages/app-cli/tests/services/keychain/KeychainService.js.map
|
||||
@@ -120,6 +118,12 @@ 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
|
||||
@@ -138,6 +142,9 @@ 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
|
||||
@@ -243,6 +250,9 @@ 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
|
||||
@@ -399,21 +409,24 @@ 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
|
||||
@@ -468,12 +481,18 @@ 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
|
||||
@@ -570,12 +589,18 @@ 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
|
||||
@@ -864,6 +889,9 @@ 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
|
||||
@@ -897,6 +925,9 @@ 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
|
||||
@@ -939,6 +970,9 @@ 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
|
||||
@@ -993,6 +1027,9 @@ 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
|
||||
@@ -1095,12 +1132,6 @@ packages/lib/services/CommandService.test.js.map
|
||||
packages/lib/services/DecryptionWorker.d.ts
|
||||
packages/lib/services/DecryptionWorker.js
|
||||
packages/lib/services/DecryptionWorker.js.map
|
||||
packages/lib/services/EncryptionService.d.ts
|
||||
packages/lib/services/EncryptionService.js
|
||||
packages/lib/services/EncryptionService.js.map
|
||||
packages/lib/services/EncryptionService.test.d.ts
|
||||
packages/lib/services/EncryptionService.test.js
|
||||
packages/lib/services/EncryptionService.test.js.map
|
||||
packages/lib/services/ExternalEditWatcher.d.ts
|
||||
packages/lib/services/ExternalEditWatcher.js
|
||||
packages/lib/services/ExternalEditWatcher.js.map
|
||||
@@ -1194,6 +1225,12 @@ packages/lib/services/database/types.js.map
|
||||
packages/lib/services/debug/populateDatabase.d.ts
|
||||
packages/lib/services/debug/populateDatabase.js
|
||||
packages/lib/services/debug/populateDatabase.js.map
|
||||
packages/lib/services/e2ee/EncryptionService.d.ts
|
||||
packages/lib/services/e2ee/EncryptionService.js
|
||||
packages/lib/services/e2ee/EncryptionService.js.map
|
||||
packages/lib/services/e2ee/EncryptionService.test.d.ts
|
||||
packages/lib/services/e2ee/EncryptionService.test.js
|
||||
packages/lib/services/e2ee/EncryptionService.test.js.map
|
||||
packages/lib/services/e2ee/types.d.ts
|
||||
packages/lib/services/e2ee/types.js
|
||||
packages/lib/services/e2ee/types.js.map
|
||||
@@ -1248,6 +1285,9 @@ packages/lib/services/interop/InteropService_Importer_Jex.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Md.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js.map
|
||||
@@ -1404,6 +1444,12 @@ 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
|
||||
@@ -1485,6 +1531,21 @@ 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
|
||||
@@ -1635,6 +1696,12 @@ 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
|
||||
@@ -1644,6 +1711,9 @@ 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
|
||||
@@ -1716,6 +1786,9 @@ 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
|
||||
@@ -1734,12 +1807,18 @@ 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
|
||||
packages/tools/generate-images.d.ts
|
||||
packages/tools/generate-images.js
|
||||
packages/tools/generate-images.js.map
|
||||
packages/tools/git-changelog.d.ts
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/git-changelog.js.map
|
||||
packages/tools/lerna-add.d.ts
|
||||
packages/tools/lerna-add.js
|
||||
packages/tools/lerna-add.js.map
|
||||
|
||||
8
.github/workflows/github-actions-main.yml
vendored
8
.github/workflows/github-actions-main.yml
vendored
@@ -35,6 +35,14 @@ 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
|
||||
|
||||
103
.gitignore
vendored
103
.gitignore
vendored
@@ -49,6 +49,7 @@ 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
|
||||
@@ -69,9 +70,6 @@ packages/app-cli/tests/HtmlToMd.js.map
|
||||
packages/app-cli/tests/MdToHtml.d.ts
|
||||
packages/app-cli/tests/MdToHtml.js
|
||||
packages/app-cli/tests/MdToHtml.js.map
|
||||
packages/app-cli/tests/MdToMd.d.ts
|
||||
packages/app-cli/tests/MdToMd.js
|
||||
packages/app-cli/tests/MdToMd.js.map
|
||||
packages/app-cli/tests/services/keychain/KeychainService.d.ts
|
||||
packages/app-cli/tests/services/keychain/KeychainService.js
|
||||
packages/app-cli/tests/services/keychain/KeychainService.js.map
|
||||
@@ -105,6 +103,12 @@ 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
|
||||
@@ -123,6 +127,9 @@ 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
|
||||
@@ -228,6 +235,9 @@ 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
|
||||
@@ -384,21 +394,24 @@ 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
|
||||
@@ -453,12 +466,18 @@ 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
|
||||
@@ -555,12 +574,18 @@ 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
|
||||
@@ -849,6 +874,9 @@ 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
|
||||
@@ -882,6 +910,9 @@ 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
|
||||
@@ -924,6 +955,9 @@ 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
|
||||
@@ -978,6 +1012,9 @@ 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
|
||||
@@ -1080,12 +1117,6 @@ packages/lib/services/CommandService.test.js.map
|
||||
packages/lib/services/DecryptionWorker.d.ts
|
||||
packages/lib/services/DecryptionWorker.js
|
||||
packages/lib/services/DecryptionWorker.js.map
|
||||
packages/lib/services/EncryptionService.d.ts
|
||||
packages/lib/services/EncryptionService.js
|
||||
packages/lib/services/EncryptionService.js.map
|
||||
packages/lib/services/EncryptionService.test.d.ts
|
||||
packages/lib/services/EncryptionService.test.js
|
||||
packages/lib/services/EncryptionService.test.js.map
|
||||
packages/lib/services/ExternalEditWatcher.d.ts
|
||||
packages/lib/services/ExternalEditWatcher.js
|
||||
packages/lib/services/ExternalEditWatcher.js.map
|
||||
@@ -1179,6 +1210,12 @@ packages/lib/services/database/types.js.map
|
||||
packages/lib/services/debug/populateDatabase.d.ts
|
||||
packages/lib/services/debug/populateDatabase.js
|
||||
packages/lib/services/debug/populateDatabase.js.map
|
||||
packages/lib/services/e2ee/EncryptionService.d.ts
|
||||
packages/lib/services/e2ee/EncryptionService.js
|
||||
packages/lib/services/e2ee/EncryptionService.js.map
|
||||
packages/lib/services/e2ee/EncryptionService.test.d.ts
|
||||
packages/lib/services/e2ee/EncryptionService.test.js
|
||||
packages/lib/services/e2ee/EncryptionService.test.js.map
|
||||
packages/lib/services/e2ee/types.d.ts
|
||||
packages/lib/services/e2ee/types.js
|
||||
packages/lib/services/e2ee/types.js.map
|
||||
@@ -1233,6 +1270,9 @@ packages/lib/services/interop/InteropService_Importer_Jex.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Md.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.js.map
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.d.ts
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js.map
|
||||
@@ -1389,6 +1429,12 @@ 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
|
||||
@@ -1470,6 +1516,21 @@ 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
|
||||
@@ -1620,6 +1681,12 @@ 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
|
||||
@@ -1629,6 +1696,9 @@ 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
|
||||
@@ -1701,6 +1771,9 @@ 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
|
||||
@@ -1719,12 +1792,18 @@ 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
|
||||
packages/tools/generate-images.d.ts
|
||||
packages/tools/generate-images.js
|
||||
packages/tools/generate-images.js.map
|
||||
packages/tools/git-changelog.d.ts
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/git-changelog.js.map
|
||||
packages/tools/lerna-add.d.ts
|
||||
packages/tools/lerna-add.js
|
||||
packages/tools/lerna-add.js.map
|
||||
|
||||
@@ -29,7 +29,7 @@ ol, ul {
|
||||
#main-container {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 225px;
|
||||
padding-bottom: 127px; /* Needs to be the height of the footer */
|
||||
}
|
||||
|
||||
.help-page-container img {
|
||||
@@ -583,7 +583,7 @@ div.navbar-mobile-content a.sponsor-button {
|
||||
|
||||
/* footer section */
|
||||
footer {
|
||||
padding-top: 50px;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -594,6 +594,7 @@ footer a,
|
||||
footer p {
|
||||
color: #90b1d9;
|
||||
text-decoration: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
@@ -617,6 +618,35 @@ 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
|
||||
@@ -679,7 +709,7 @@ footer .right-links a {
|
||||
#main-container {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 260px;
|
||||
padding-bottom: 130px;
|
||||
}
|
||||
|
||||
.front-page h1 {
|
||||
@@ -866,7 +896,7 @@ footer .right-links a {
|
||||
|
||||
.help-page-container {
|
||||
padding-top: 90px;
|
||||
padding-bottom: 50px;
|
||||
padding-bottom: 70px;
|
||||
}
|
||||
|
||||
.donate-links {
|
||||
@@ -880,7 +910,7 @@ footer .right-links a {
|
||||
border-radius: 20px;
|
||||
padding: 30px 20px;
|
||||
padding-bottom: 30px;
|
||||
margin-bottom: 50px;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
@@ -923,6 +953,11 @@ footer .right-links a {
|
||||
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;
|
||||
|
||||
@@ -368,40 +368,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 © 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>
|
||||
{{> footer}}
|
||||
</div>
|
||||
|
||||
<script
|
||||
|
||||
@@ -75,38 +75,8 @@ https://github.com/laurent22/joplin/blob/dev/{{{sourceMarkdownFile}}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 © 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>
|
||||
|
||||
{{> footer}}
|
||||
</div>
|
||||
|
||||
<script src="{{jsBaseUrl}}/script.js?t={{buildTime}}"></script>
|
||||
|
||||
26
Assets/WebsiteAssets/templates/partials/footer.mustache
Normal file
26
Assets/WebsiteAssets/templates/partials/footer.mustache
Normal file
@@ -0,0 +1,26 @@
|
||||
<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 © 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>
|
||||
@@ -6,7 +6,7 @@
|
||||
Joplin Cloud <span class="frame-bg frame-bg-yellow">plans</span>
|
||||
</h1>
|
||||
<p class="text-center sub-title">
|
||||
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.
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,6 +45,8 @@
|
||||
{{#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">
|
||||
|
||||
@@ -31,12 +31,14 @@ Joplin is available in multiple languages thanks to the help of its users. You c
|
||||
|
||||
If you want to start contributing to the project's code, please follow these guidelines before creating a pull request:
|
||||
|
||||
- Explain WHY you want to add this change. Explain it inside the pull request and you may link to an issue for additional information, but the PR should gives a clear overview of why you want to add this.
|
||||
- Bug fixes are always welcome. Start by reviewing the [list of bugs](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
||||
- A good way to easily start contributing is to pick and work on a [good first issue](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). We try to make these issues as clear as possible and provide basic info on how the code should be changed, and if something is unclear feel free to ask for more information on the issue.
|
||||
- Before adding a new feature, ask about it in the [Github Issue Tracker](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue) or the [Joplin Forum](https://discourse.joplinapp.org/), or check if existing discussions exist to make sure the new functionality is desired.
|
||||
- **Changes that will consist in more than 50 lines of code should be discussed the [Joplin Forum](https://discourse.joplinapp.org/)**, so that you don't spend too much time implementing something that might not be accepted.
|
||||
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, makes sure it still work in the other apps. Usually it does, but keep this in mind.
|
||||
- Pull requests that make many changes using an automated tool, like for spell fixing, styling, etc. will not be accepted. An exception would be if the changes have been discussed in the forum and someone has agreed to review **and test** the pull request.
|
||||
- Pull requests that make address multiple issues will most likely stall and eventually be closed. This is because we might be fine with one of the changes but not with others and untangling that kind of pull request is too much hassle both for maintainers and the person who submitted it. So most of the time someone gives up and the PR gets closed. So please keep the pull request focused on one issue.
|
||||
|
||||
Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/dev/BUILD.md) for more details.
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# https://versatile.nl/blog/deploying-lerna-web-apps-with-docker
|
||||
|
||||
FROM node:16
|
||||
FROM node:16-bullseye
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
python \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN echo "Node: $(node --version)"
|
||||
RUN echo "Npm: $(npm --version)"
|
||||
|
||||
@@ -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
|
||||
|
||||
12
README.md
12
README.md
@@ -511,7 +511,7 @@ Current translations:
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | jmontane, 2019 | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | Mustafa Al-Dailemi (dailemi@hotmail.com)Language-Team: | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | Mustafa Al-Dailemi (dailemi@hotmail.com)Language-Team: | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Atalanttore](mailto:atalanttore@googlemail.com) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 54%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gb.png" width="16px"/> | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
|
||||
@@ -527,7 +527,7 @@ Current translations:
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | Alexander Dawson | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 68%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 67%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Nicolas Suzuki](mailto:nicolas.suzuki@pm.me) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 90%
|
||||
@@ -536,13 +536,13 @@ Current translations:
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 42%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vi.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 81%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [南宫小骏](mailto:jackytsu@vip.qq.com) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Po-Chiang Chao](mailto:BobChao%29%20%28bobchao@gmail.com) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [南宫小骏](mailto:jackytsu@vip.qq.com) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Po-Chiang Chao](mailto:BobChao%29%20%28bobchao@gmail.com) | 94%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 95%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
@@ -5,6 +5,7 @@ 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 () => {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"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",
|
||||
|
||||
@@ -381,6 +381,30 @@ 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');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import EncryptionService from '@joplin/lib/services/EncryptionService';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
@@ -27,7 +27,7 @@ const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local.js');
|
||||
const EncryptionService = require('@joplin/lib/services/EncryptionService').default;
|
||||
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
|
||||
const envFromArgs = require('@joplin/lib/envFromArgs');
|
||||
|
||||
const env = envFromArgs(process.argv);
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
const mdImporterService = require('@joplin/lib/services/interop/InteropService_Importer_Md').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
const importer = new mdImporterService();
|
||||
|
||||
|
||||
describe('InteropService_Importer_Md: importLocalImages', function() {
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
it('should import linked files and modify tags appropriately', async function() {
|
||||
const tagNonExistentFile = '';
|
||||
const note = await importer.importFile(`${__dirname}/md_to_md/sample.md`, 'notebook');
|
||||
const items = await Note.linkedItems(note.body);
|
||||
expect(items.length).toBe(2);
|
||||
const inexistentLinkUnchanged = note.body.includes(tagNonExistentFile);
|
||||
expect(inexistentLinkUnchanged).toBe(true);
|
||||
});
|
||||
it('should only create 1 resource for duplicate links, all tags should be updated', async function() {
|
||||
const note = await importer.importFile(`${__dirname}/md_to_md/sample-duplicate-links.md`, 'notebook');
|
||||
const items = await Note.linkedItems(note.body);
|
||||
expect(items.length).toBe(1);
|
||||
const reg = new RegExp(items[0].id, 'g');
|
||||
const matched = note.body.match(reg);
|
||||
expect(matched.length).toBe(2);
|
||||
});
|
||||
it('should import linked files and modify tags appropriately when link is also in alt text', async function() {
|
||||
const note = await importer.importFile(`${__dirname}/md_to_md/sample-link-in-alt-text.md`, 'notebook');
|
||||
const items = await Note.linkedItems(note.body);
|
||||
expect(items.length).toBe(1);
|
||||
});
|
||||
it('should passthrough unchanged if no links present', async function() {
|
||||
const note = await importer.importFile(`${__dirname}/md_to_md/sample-no-links.md`, 'notebook');
|
||||
const items = await Note.linkedItems(note.body);
|
||||
expect(items.length).toBe(0);
|
||||
expect(note.body).toContain('Unidentified vessel travelling at sub warp speed, bearing 235.7. Fluctuations in energy readings from it, Captain. All transporters off.');
|
||||
});
|
||||
it('should import linked image with special characters in name', async function() {
|
||||
const note = await importer.importFile(`${__dirname}/md_to_md/sample-special-chars.md`, 'notebook');
|
||||
const items = await Note.linkedItems(note.body);
|
||||
expect(items.length).toBe(1);
|
||||
});
|
||||
it('should import resources for files', async function() {
|
||||
const note = await importer.importFile(`${__dirname}/md_to_md/sample-files.md`, 'notebook');
|
||||
const items = await Note.linkedItems(note.body);
|
||||
expect(items.length).toBe(4);
|
||||
});
|
||||
});
|
||||
@@ -1,2 +0,0 @@
|
||||

|
||||

|
||||
@@ -1,9 +0,0 @@
|
||||
# Markdown file test
|
||||
|
||||

|
||||
|
||||
[welcome.pdf](../support/welcome.pdf)
|
||||
|
||||
[sample.md](sample.md)
|
||||
|
||||
[sample2.md](./sample.md)
|
||||
@@ -1,3 +0,0 @@
|
||||
# Markdown
|
||||
 should put resource link inside () not []
|
||||
 this case (spaces before/after link but within parens) is not currently covered
|
||||
@@ -1 +0,0 @@
|
||||

|
||||
@@ -1,13 +0,0 @@
|
||||
# Markdown
|
||||
|
||||
lorem ipsum 
|
||||
- [ ] check!
|
||||
- [ ] boxes!
|
||||
|
||||
ipsum lorem
|
||||
|
||||
**strong text**
|
||||
 lorem ipsum
|
||||
|
||||
**some directory**
|
||||
 lorem ipsum
|
||||
BIN
packages/app-cli/tests/support/photo sample.jpg
Normal file
BIN
packages/app-cli/tests/support/photo sample.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,3 @@
|
||||
# Test Spaces
|
||||
|
||||
I hope this get's imported correctly!
|
||||
@@ -0,0 +1 @@
|
||||
[Section 1](./sample-no-links.md#markdown)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Markdown file test
|
||||
|
||||
[sample.md](sample-cycles-b.md)
|
||||
@@ -0,0 +1,4 @@
|
||||
# Markdown file test
|
||||
|
||||
|
||||
[sample.md](./sample-cycles-a.md)
|
||||
@@ -0,0 +1,2 @@
|
||||

|
||||

|
||||
@@ -0,0 +1 @@
|
||||

|
||||
@@ -0,0 +1,9 @@
|
||||
# Markdown file test
|
||||
|
||||

|
||||
|
||||
[welcome.pdf](../../welcome.pdf)
|
||||
|
||||
[sample.md](sample.md)
|
||||
|
||||
[sample2.md](./sample.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Markdown
|
||||
 should put resource link inside () not []
|
||||
 this case (spaces before/after link but within parens) is not currently covered
|
||||
@@ -0,0 +1,3 @@
|
||||

|
||||

|
||||
[Worst Case](<./sample spaces.md> "title")
|
||||
1
packages/app-cli/tests/support/test_notes/md/sample-md
Normal file
1
packages/app-cli/tests/support/test_notes/md/sample-md
Normal file
@@ -0,0 +1 @@
|
||||
I am here, but am I alive?
|
||||
@@ -0,0 +1,3 @@
|
||||
# Some Title
|
||||
|
||||
[link](./sample-md)
|
||||
@@ -0,0 +1,4 @@
|
||||

|
||||
[sample photo](../../photo%20sample.jpg)
|
||||
[sample.md](./sample%20spaces.md)
|
||||
[sample special syntax](<../../photo sample.jpg>)
|
||||
4
packages/app-cli/tests/support/test_notes/md/sample.html
Normal file
4
packages/app-cli/tests/support/test_notes/md/sample.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<img src="../../photo.jpg">
|
||||
<img src='../../photo-two.jpg'>
|
||||
<img src='does-not-exist' alt="../../photo.jpg">
|
||||
<a href="./sample-no-links.md">
|
||||
13
packages/app-cli/tests/support/test_notes/md/sample.md
Normal file
13
packages/app-cli/tests/support/test_notes/md/sample.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Markdown
|
||||
|
||||
lorem ipsum 
|
||||
- [ ] check!
|
||||
- [ ] boxes!
|
||||
|
||||
ipsum lorem
|
||||
|
||||
**strong text**
|
||||
 lorem ipsum
|
||||
|
||||
**some directory**
|
||||
 lorem ipsum
|
||||
@@ -237,7 +237,7 @@ export default class ElectronAppWrapper {
|
||||
const iid = setInterval(() => {
|
||||
if (this.electronApp().isReady()) {
|
||||
clearInterval(iid);
|
||||
resolve();
|
||||
resolve(null);
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
|
||||
47
packages/app-desktop/app.reducer.test.ts
Normal file
47
packages/app-desktop/app.reducer.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
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' }]);
|
||||
});
|
||||
|
||||
});
|
||||
316
packages/app-desktop/app.reducer.ts
Normal file
316
packages/app-desktop/app.reducer.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
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;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ 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';
|
||||
@@ -19,13 +18,10 @@ 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 produce from 'immer';
|
||||
import iterateItems from './gui/ResizableLayout/utils/iterateItems';
|
||||
import validateLayout from './gui/ResizableLayout/utils/validateLayout';
|
||||
import appReducer, { createAppDefaultState } from './app.reducer';
|
||||
const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
const fs = require('fs-extra');
|
||||
@@ -40,125 +36,38 @@ 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 = [
|
||||
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'),
|
||||
];
|
||||
const commands = mainScreenCommands
|
||||
.concat(noteEditorCommands)
|
||||
.concat(noteListCommands)
|
||||
.concat(noteListControlsCommands)
|
||||
.concat(sidebarCommands);
|
||||
|
||||
// Commands that are not tied to any particular component.
|
||||
// The runtime for these commands can be loaded when the app starts.
|
||||
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'),
|
||||
];
|
||||
const globalCommands = appCommands.concat(libCommands);
|
||||
|
||||
import editorCommandDeclarations from './gui/NoteEditor/commands/editorCommandDeclarations';
|
||||
import editorCommandDeclarations from './gui/NoteEditor/editorCommandDeclarations';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import checkForUpdates from './checkForUpdates';
|
||||
import { AppState } from './app.reducer';
|
||||
|
||||
const pluginClasses = [
|
||||
require('./plugins/GotoAnything').default,
|
||||
];
|
||||
|
||||
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,
|
||||
};
|
||||
const appDefaultState = createAppDefaultState(
|
||||
bridge().windowContentSize(),
|
||||
resourceEditWatcherDefaultState
|
||||
);
|
||||
|
||||
class Application extends BaseApplication {
|
||||
|
||||
@@ -175,249 +84,9 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
reducer(state: AppState = appDefaultState, 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':
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
let newState = appReducer(state, action);
|
||||
newState = resourceEditWatcherReducer(newState, action);
|
||||
newState = super.reducer(newState, action);
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
@@ -873,6 +542,16 @@ class Application extends BaseApplication {
|
||||
// });
|
||||
// }, 2000);
|
||||
|
||||
// setTimeout(() => {
|
||||
// this.dispatch({
|
||||
// type: 'NAV_GO',
|
||||
// routeName: 'Config',
|
||||
// props: {
|
||||
// defaultSection: 'plugins',
|
||||
// },
|
||||
// });
|
||||
// }, 2000);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
29
packages/app-desktop/commands/index.ts
Normal file
29
packages/app-desktop/commands/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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`
|
||||
@@ -1,5 +1,5 @@
|
||||
import CommandService, { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { AppState } from '../app';
|
||||
import { AppState } from '../app.reducer';
|
||||
import bridge from '../services/bridge';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
|
||||
@@ -7,8 +7,8 @@ import { themeStyle } from '@joplin/lib/theme';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import ClipperServer from '@joplin/lib/ClipperServer';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import EncryptionService from '@joplin/lib/services/EncryptionService';
|
||||
import { AppState } from '../app';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import { AppState } from '../app.reducer';
|
||||
|
||||
class ClipperConfigScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
|
||||
@@ -133,6 +133,20 @@ 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);
|
||||
@@ -144,6 +158,10 @@ 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.
|
||||
@@ -222,11 +240,18 @@ 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>
|
||||
|
||||
@@ -299,7 +299,7 @@ export default function(props: Props) {
|
||||
function renderRepoApiError() {
|
||||
if (!repoApiError) return null;
|
||||
|
||||
return <RepoApiErrorMessage maxWidth={maxWidth} type="error">{_('Could not connect to plugin repository')} - <StyledLink href="#" onClick={() => { setFetchManifestTime(Date.now()); }}>{_('Try again')}</StyledLink></RepoApiErrorMessage>;
|
||||
return <RepoApiErrorMessage maxWidth={maxWidth} type="error">{_('Could not connect to plugin repository.')}<br/><br/>- <StyledLink href="#" onClick={() => { setFetchManifestTime(Date.now()); }}>{_('Try again')}</StyledLink><br/><br/>- <StyledLink href="#" onClick={onBrowsePlugins}>{_('Browse all plugins')}</StyledLink></RepoApiErrorMessage>;
|
||||
}
|
||||
|
||||
function renderBottomArea() {
|
||||
|
||||
@@ -30,6 +30,14 @@ 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[]>([]);
|
||||
@@ -47,7 +55,7 @@ export default function(props: Props) {
|
||||
setSearchResultCount(null);
|
||||
} else {
|
||||
const r = await props.repoApi().search(props.searchQuery);
|
||||
setManifests(r);
|
||||
setManifests(sortManifestResults(r));
|
||||
setSearchResultCount(r.length);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import EncryptionService from '@joplin/lib/services/EncryptionService';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import time from '@joplin/lib/time';
|
||||
@@ -12,8 +12,16 @@ import bridge from '../services/bridge';
|
||||
import shared from '@joplin/lib/components/shared/encryption-config-shared';
|
||||
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
|
||||
import { getEncryptionEnabled, masterKeyEnabled, SyncInfo } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||
import { toggleAndSetupEncryption } from '@joplin/lib/services/e2ee/utils';
|
||||
import { getDefaultMasterKey, toggleAndSetupEncryption } from '@joplin/lib/services/e2ee/utils';
|
||||
import MasterKey from '@joplin/lib/models/MasterKey';
|
||||
import StyledInput from './style/StyledInput';
|
||||
import Button, { ButtonLevel } from './Button/Button';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const MasterPasswordInput = styled(StyledInput)`
|
||||
min-width: 300px;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
interface Props {}
|
||||
|
||||
@@ -45,6 +53,10 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
private renderMasterKey(mk: MasterKeyEntity, isDefault: boolean) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const onToggleEnabledClick = () => {
|
||||
return shared.onToggleEnabledClick(this, mk);
|
||||
};
|
||||
|
||||
const passwordStyle = {
|
||||
color: theme.color,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
@@ -60,8 +72,23 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
return shared.onPasswordChange(this, mk, event.target.value);
|
||||
};
|
||||
|
||||
const onToggleEnabledClick = () => {
|
||||
return shared.onToggleEnabledClick(this, mk);
|
||||
const renderPasswordInput = (masterKeyId: string) => {
|
||||
if (this.state.masterPasswordKeys[masterKeyId] || !this.state.passwordChecks['master']) {
|
||||
return (
|
||||
<td style={{ ...theme.textStyle, color: theme.colorFaded, fontStyle: 'italic' }}>
|
||||
({_('Master password')})
|
||||
</td>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<td style={theme.textStyle}>
|
||||
<input type="password" style={passwordStyle} value={password} onChange={event => onPasswordChange(event)} />{' '}
|
||||
<button style={theme.buttonStyle} onClick={() => onSaveClick()}>
|
||||
{_('Save')}
|
||||
</button>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const password = this.state.passwords[mk.id] ? this.state.passwords[mk.id] : '';
|
||||
@@ -74,12 +101,7 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
<td style={theme.textStyle}>{activeIcon}</td>
|
||||
<td style={theme.textStyle}>{mk.id}<br/>{_('Source: ')}{mk.source_application}</td>
|
||||
<td style={theme.textStyle}>{_('Created: ')}{time.formatMsToLocal(mk.created_time)}<br/>{_('Updated: ')}{time.formatMsToLocal(mk.updated_time)}</td>
|
||||
<td style={theme.textStyle}>
|
||||
<input type="password" style={passwordStyle} value={password} onChange={event => onPasswordChange(event)} />{' '}
|
||||
<button style={theme.buttonStyle} onClick={() => onSaveClick()}>
|
||||
{_('Save')}
|
||||
</button>
|
||||
</td>
|
||||
{renderPasswordInput(mk.id)}
|
||||
<td style={theme.textStyle}>{passwordOk}</td>
|
||||
<td style={theme.textStyle}>
|
||||
<button disabled={isActive || isDefault} style={theme.buttonStyle} onClick={() => onToggleEnabledClick()}>{masterKeyEnabled(mk) ? _('Disable') : _('Enable')}</button>
|
||||
@@ -165,7 +187,7 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const headerComp = isEnabledMasterKeys ? <h1 style={theme.h1Style}>{_('Master Keys')}</h1> : <a onClick={() => shared.toggleShowDisabledMasterKeys(this) } style={{ ...theme.urlStyle, display: 'inline-block', marginBottom: 10 }} href="#">{showTable ? _('Hide disabled master keys') : _('Show disabled master keys')}</a>;
|
||||
const infoComp = isEnabledMasterKeys ? <p style={theme.textStyle}>{_('Note: Only one master key is going to be used for encryption (the one marked as "active"). Any of the keys might be used for decryption, depending on how the notes or notebooks were originally encrypted.')}</p> : null;
|
||||
const infoComp = isEnabledMasterKeys ? <p style={theme.textStyle}>{'Note: Only one master key is going to be used for encryption (the one marked as "active"). Any of the keys might be used for decryption, depending on how the notes or notebooks were originally encrypted.'}</p> : null;
|
||||
const tableComp = !showTable ? null : (
|
||||
<table>
|
||||
<tbody>
|
||||
@@ -195,6 +217,39 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private renderMasterPassword() {
|
||||
if (!this.props.encryptionEnabled && !this.props.masterKeys.length) return null;
|
||||
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const onMasterPasswordSave = async () => {
|
||||
shared.onMasterPasswordSave(this);
|
||||
|
||||
if (!(await shared.masterPasswordIsValid(this, this.state.masterPasswordInput))) {
|
||||
alert('Password is invalid. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
if (this.state.passwordChecks['master']) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<span style={theme.textStyle}>{_('Master password:')}</span>
|
||||
<span style={{ ...theme.textStyle, fontWeight: 'bold' }}>✔ {_('Loaded')}</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<span style={theme.textStyle}>❌ {'The master password is not set or is invalid. Please type it below:'}</span>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 10 }}>
|
||||
<MasterPasswordInput placeholder={_('Enter your master password')} type="password" value={this.state.masterPasswordInput} onChange={(event: any) => shared.onMasterPasswordChange(this, event.target.value)} />{' '}
|
||||
<Button ml="10px" level={ButtonLevel.Secondary} onClick={onMasterPasswordSave} title={_('Save')} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const masterKeys: MasterKeyEntity[] = this.props.masterKeys;
|
||||
@@ -215,7 +270,7 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
|
||||
const onToggleButtonClick = async () => {
|
||||
const isEnabled = getEncryptionEnabled();
|
||||
const masterKey = MasterKey.latest();
|
||||
const masterKey = getDefaultMasterKey();
|
||||
|
||||
let answer = null;
|
||||
if (isEnabled) {
|
||||
@@ -304,6 +359,7 @@ class EncryptionConfigScreenComponent extends React.Component<Props> {
|
||||
<p style={theme.textStyle}>
|
||||
{_('Encryption is:')} <strong>{this.props.encryptionEnabled ? _('Enabled') : _('Disabled')}</strong>
|
||||
</p>
|
||||
{this.renderMasterPassword()}
|
||||
{decryptedItemsInfo}
|
||||
{toggleButton}
|
||||
{needUpgradeSection}
|
||||
@@ -329,6 +385,7 @@ const mapStateToProps = (state: State) => {
|
||||
activeMasterKeyId: syncInfo.activeMasterKeyId,
|
||||
shouldReencrypt: state.settings['encryption.shouldReencrypt'] >= Setting.SHOULD_REENCRYPT_YES,
|
||||
notLoadedMasterKeys: state.notLoadedMasterKeys,
|
||||
masterPassword: state.settings['encryption.masterPassword'],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import { saveLayout, loadLayout } from '../ResizableLayout/utils/persist';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import produce from 'immer';
|
||||
@@ -29,7 +29,7 @@ import { themeStyle } from '@joplin/lib/theme';
|
||||
import validateLayout from '../ResizableLayout/utils/validateLayout';
|
||||
import iterateItems from '../ResizableLayout/utils/iterateItems';
|
||||
import removeItem from '../ResizableLayout/utils/removeItem';
|
||||
import EncryptionService from '@joplin/lib/services/EncryptionService';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog';
|
||||
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
@@ -37,6 +37,7 @@ 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');
|
||||
@@ -110,39 +111,6 @@ 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;
|
||||
|
||||
67
packages/app-desktop/gui/MainScreen/commands/index.ts
Normal file
67
packages/app-desktop/gui/MainScreen/commands/index.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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`
|
||||
@@ -34,7 +34,7 @@ export const runtime = (comp: any): CommandRuntime => {
|
||||
value: '',
|
||||
autocomplete: startFolders,
|
||||
onClose: async (answer: any) => {
|
||||
if (answer != null) {
|
||||
if (answer) {
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
await Note.moveToFolder(noteIds[i], answer.value);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
|
||||
const Menu = bridge().Menu;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { AppState } from '../../../app';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleLayoutMoveMode',
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleNoteList',
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleSideBar',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { AppState } from '../app';
|
||||
import { AppState } from '../app.reducer';
|
||||
import InteropService from '@joplin/lib/services/interop/InteropService';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
|
||||
@@ -381,6 +381,9 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
`.CodeMirror-selected {
|
||||
background: #6b6b6b !important;
|
||||
}` : '';
|
||||
// Vim mode draws a fat cursor in the background, we don't want to add background colors
|
||||
// to the inline code in this case (it would hide the cursor)
|
||||
const codeBackgroundColor = Setting.value('editor.keyboardMode') !== 'vim' ? theme.codeBackgroundColor : 'inherit';
|
||||
const monospaceFonts = [];
|
||||
if (Setting.value('style.editor.monospaceFontFamily')) monospaceFonts.push(`"${Setting.value('style.editor.monospaceFontFamily')}"`);
|
||||
monospaceFonts.push('monospace');
|
||||
@@ -476,9 +479,9 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
}
|
||||
|
||||
/* Negative margins are needed to componsate for the border */
|
||||
div.CodeMirror span.cm-comment.cm-jn-inline-code {
|
||||
div.CodeMirror span.cm-comment.cm-jn-inline-code:not(.cm-search-marker):not(.cm-fat-cursor-mark):not(.cm-search-marker-selected):not(.CodeMirror-selectedtext) {
|
||||
border: 1px solid ${theme.codeBorderColor};
|
||||
background-color: ${theme.codeBackgroundColor};
|
||||
background-color: ${codeBackgroundColor};
|
||||
margin-left: -1px;
|
||||
margin-right: -1px;
|
||||
border-radius: .25em;
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../../../app.reducer';
|
||||
import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import stateToWhenClauseContext from '../../../../services/commands/stateToWhenClauseContext';
|
||||
const { buildStyle } = require('@joplin/lib/theme');
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import TagList from '../TagList';
|
||||
|
||||
15
packages/app-desktop/gui/NoteEditor/commands/index.ts
Normal file
15
packages/app-desktop/gui/NoteEditor/commands/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// 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`
|
||||
@@ -1,6 +1,11 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { SearchMarkers } from './useSearchMarkers';
|
||||
|
||||
const logger = Logger.create('useNoteSearchBar');
|
||||
|
||||
const queryMaxLength = 1000;
|
||||
|
||||
interface LocalSearch {
|
||||
query: string;
|
||||
selectedIndex: number;
|
||||
@@ -24,6 +29,14 @@ export default function useNoteSearchBar() {
|
||||
const [localSearch, setLocalSearch] = useState<LocalSearch>(defaultLocalSearch());
|
||||
|
||||
const onChange = useCallback((query: string) => {
|
||||
// A query that's too long would make CodeMirror throw an exception
|
||||
// which would crash the app.
|
||||
// https://github.com/laurent22/joplin/issues/5380
|
||||
if (query.length > queryMaxLength) {
|
||||
logger.warn(`Query is longer than ${queryMaxLength} characters - it is going to be trimmed`);
|
||||
query = query.substr(0, queryMaxLength);
|
||||
}
|
||||
|
||||
setLocalSearch((prev: LocalSearch) => {
|
||||
return {
|
||||
query: query,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { FormNote, ScrollOptionTypes } from './types';
|
||||
import editorCommandDeclarations from '../commands/editorCommandDeclarations';
|
||||
import editorCommandDeclarations from '../editorCommandDeclarations';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import time from '@joplin/lib/time';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppState } from '../../app';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import eventManager from '@joplin/lib/eventManager';
|
||||
import NoteListUtils from '../utils/NoteListUtils';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
9
packages/app-desktop/gui/NoteList/commands/index.ts
Normal file
9
packages/app-desktop/gui/NoteList/commands/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// AUTO-GENERATED using `gulp buildCommandIndex`
|
||||
import * as focusElementNoteList from './focusElementNoteList';
|
||||
|
||||
const index:any[] = [
|
||||
focusElementNoteList,
|
||||
];
|
||||
|
||||
export default index;
|
||||
// AUTO-GENERATED using `gulp buildCommandIndex`
|
||||
@@ -0,0 +1,9 @@
|
||||
// AUTO-GENERATED using `gulp buildCommandIndex`
|
||||
import * as focusSearch from './focusSearch';
|
||||
|
||||
const index:any[] = [
|
||||
focusSearch,
|
||||
];
|
||||
|
||||
export default index;
|
||||
// AUTO-GENERATED using `gulp buildCommandIndex`
|
||||
@@ -1,4 +1,5 @@
|
||||
import app, { AppState, AppStateDialog } from '../app';
|
||||
import app from '../app';
|
||||
import { AppState, AppStateDialog } from '../app.reducer';
|
||||
import MainScreen from './MainScreen/MainScreen';
|
||||
import ConfigScreen from './ConfigScreen/ConfigScreen';
|
||||
import StatusScreen from './StatusScreen/StatusScreen';
|
||||
@@ -19,6 +20,7 @@ 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');
|
||||
@@ -207,6 +209,7 @@ 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} />
|
||||
|
||||
@@ -16,6 +16,7 @@ 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');
|
||||
|
||||
@@ -100,20 +101,6 @@ 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,
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../app.reducer';
|
||||
import { getEncryptionEnabled } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||
const { clipboard } = require('electron');
|
||||
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import { ModelType } from '@joplin/lib/BaseModel';
|
||||
import BaseModel from '@joplin/lib/BaseModel';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
|
||||
@@ -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';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElementSideBar',
|
||||
|
||||
9
packages/app-desktop/gui/Sidebar/commands/index.ts
Normal file
9
packages/app-desktop/gui/Sidebar/commands/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// AUTO-GENERATED using `gulp buildCommandIndex`
|
||||
import * as focusElementSideBar from './focusElementSideBar';
|
||||
|
||||
const index:any[] = [
|
||||
focusElementSideBar,
|
||||
];
|
||||
|
||||
export default index;
|
||||
// AUTO-GENERATED using `gulp buildCommandIndex`
|
||||
41
packages/app-desktop/gui/StyleSheets/StyleSheetContainer.tsx
Normal file
41
packages/app-desktop/gui/StyleSheets/StyleSheetContainer.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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>;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { AppState } from '../app';
|
||||
import { AppState } from '../app.reducer';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
|
||||
@@ -19,6 +19,7 @@ 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);
|
||||
@@ -28,7 +29,8 @@ const buildParallel = [
|
||||
'compilePackageInfo',
|
||||
'copyPluginAssets',
|
||||
'copyTinyMceLangs',
|
||||
// 'updateIgnoredTypeScriptBuild',
|
||||
'updateIgnoredTypeScriptBuild',
|
||||
'buildCommandIndex',
|
||||
];
|
||||
|
||||
gulp.task('build', gulp.parallel(...buildParallel));
|
||||
|
||||
@@ -24,7 +24,7 @@ const Logger = require('@joplin/lib/Logger').default;
|
||||
const FsDriverNode = require('@joplin/lib/fs-driver-node').default;
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const EncryptionService = require('@joplin/lib/services/EncryptionService').default;
|
||||
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local.js');
|
||||
const React = require('react');
|
||||
|
||||
4
packages/app-desktop/package-lock.json
generated
4
packages/app-desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.4.1",
|
||||
"version": "2.4.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.4.1",
|
||||
"version": "2.4.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.4.1",
|
||||
"version": "2.4.4",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { AppState } from '../app';
|
||||
import { AppState } from '../app.reducer';
|
||||
import CommandService, { SearchResult as CommandSearchResult } from '@joplin/lib/services/CommandService';
|
||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||
import shim from '@joplin/lib/shim';
|
||||
@@ -337,6 +337,10 @@ 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);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// general, any desktop component should import this file, and not the lib
|
||||
// one.
|
||||
|
||||
import { AppState } from '../../app';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import libStateToWhenClauseContext, { WhenClauseContextOptions } from '@joplin/lib/services/commands/stateToWhenClauseContext';
|
||||
import layoutItemProp from '../../gui/ResizableLayout/utils/layoutItemProp';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppState } from '../../app';
|
||||
import { AppState } from '../../app.reducer';
|
||||
|
||||
export interface DesktopCommandContext {
|
||||
state: AppState;
|
||||
|
||||
@@ -64,28 +64,6 @@ 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);
|
||||
@@ -163,11 +141,6 @@ a {
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
/* .joplin-tinymce .tox-editor-header {
|
||||
padding-left: 88px;
|
||||
padding-right: 150px;
|
||||
} */
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
@@ -141,8 +141,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097648
|
||||
versionName "2.4.0"
|
||||
versionCode 2097649
|
||||
versionName "2.4.1"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ const { BaseScreenComponent } = require('../base-screen.js');
|
||||
const { themeStyle } = require('../global-style.js');
|
||||
const DialogBox = require('react-native-dialogbox').default;
|
||||
const { dialogs } = require('../../utils/dialogs.js');
|
||||
import EncryptionService from '@joplin/lib/services/EncryptionService';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import time from '@joplin/lib/time';
|
||||
import shared from '@joplin/lib/components/shared/encryption-config-shared';
|
||||
@@ -116,15 +116,29 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent<Props> {
|
||||
inputStyle.borderBottomWidth = 1;
|
||||
inputStyle.borderBottomColor = theme.dividerColor;
|
||||
|
||||
const renderPasswordInput = (masterKeyId: string) => {
|
||||
if (this.state.masterPasswordKeys[masterKeyId] || !this.state.passwordChecks['master']) {
|
||||
return (
|
||||
<Text style={{ ...this.styles().normalText, color: theme.colorFaded, fontStyle: 'italic' }}>({_('Master password')})</Text>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
|
||||
<TextInput selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} secureTextEntry={true} value={password} onChangeText={(text: string) => onPasswordChange(text)} style={inputStyle}></TextInput>
|
||||
<Text style={{ fontSize: theme.fontSize, marginRight: 10, color: theme.color }}>{passwordOk}</Text>
|
||||
<Button title={_('Save')} onPress={() => onSaveClick()}></Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View key={mk.id}>
|
||||
<Text style={this.styles().titleText}>{_('Master Key %s', mk.id.substr(0, 6))}</Text>
|
||||
<Text style={this.styles().normalText}>{_('Created: %s', time.formatMsToLocal(mk.created_time))}</Text>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Text style={{ flex: 0, fontSize: theme.fontSize, marginRight: 10, color: theme.color }}>{_('Password:')}</Text>
|
||||
<TextInput selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} secureTextEntry={true} value={password} onChangeText={(text: string) => onPasswordChange(text)} style={inputStyle}></TextInput>
|
||||
<Text style={{ fontSize: theme.fontSize, marginRight: 10, color: theme.color }}>{passwordOk}</Text>
|
||||
<Button title={_('Save')} onPress={() => onSaveClick()}></Button>
|
||||
{renderPasswordInput(mk.id)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@@ -203,6 +217,43 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
private renderMasterPassword() {
|
||||
if (!this.props.encryptionEnabled && !this.props.masterKeys.length) return null;
|
||||
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const onMasterPasswordSave = async () => {
|
||||
shared.onMasterPasswordSave(this);
|
||||
|
||||
if (!(await shared.masterPasswordIsValid(this, this.state.masterPasswordInput))) {
|
||||
alert('Password is invalid. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
const inputStyle: any = { flex: 1, marginRight: 10, color: theme.color };
|
||||
inputStyle.borderBottomWidth = 1;
|
||||
inputStyle.borderBottomColor = theme.dividerColor;
|
||||
|
||||
if (this.state.passwordChecks['master']) {
|
||||
return (
|
||||
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Text style={{ ...this.styles().normalText, flex: 0, marginRight: 5 }}>{_('Master password:')}</Text>
|
||||
<Text style={{ ...this.styles().normalText, fontWeight: 'bold' }}>{_('Loaded')}</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={{ display: 'flex', flexDirection: 'column', marginTop: 10 }}>
|
||||
<Text style={this.styles().normalText}>{'The master password is not set or is invalid. Please type it below:'}</Text>
|
||||
<View style={{ display: 'flex', flexDirection: 'row', marginTop: 10 }}>
|
||||
<TextInput selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} secureTextEntry={true} value={this.state.masterPasswordInput} onChangeText={(text: string) => shared.onMasterPasswordChange(this, text)} style={inputStyle}></TextInput>
|
||||
<Button onPress={onMasterPasswordSave} title={_('Save')} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const masterKeys = this.props.masterKeys;
|
||||
@@ -289,6 +340,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent<Props> {
|
||||
<Text style={this.styles().titleText}>{_('Status')}</Text>
|
||||
<Text style={this.styles().normalText}>{_('Encryption is: %s', this.props.encryptionEnabled ? _('Enabled') : _('Disabled'))}</Text>
|
||||
{decryptedItemsInfo}
|
||||
{this.renderMasterPassword()}
|
||||
{toggleButton}
|
||||
{passwordPromptComp}
|
||||
{mkComps}
|
||||
@@ -315,6 +367,7 @@ const EncryptionConfigScreen = connect((state: State) => {
|
||||
encryptionEnabled: syncInfo.e2ee,
|
||||
activeMasterKeyId: syncInfo.activeMasterKeyId,
|
||||
notLoadedMasterKeys: state.notLoadedMasterKeys,
|
||||
masterPassword: state.settings['encryption.masterPassword'],
|
||||
};
|
||||
})(EncryptionConfigScreenComponent);
|
||||
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>api.joplincloud.local</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
|
||||
@@ -14,7 +14,7 @@ import ResourceService from '@joplin/lib/services/ResourceService';
|
||||
import KvStore from '@joplin/lib/services/KvStore';
|
||||
import NoteScreen from './components/screens/Note';
|
||||
import UpgradeSyncTargetScreen from './components/screens/UpgradeSyncTargetScreen';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import Setting, { Env } from '@joplin/lib/models/Setting';
|
||||
import RNFetchBlob from 'rn-fetch-blob';
|
||||
import PoorManIntervals from '@joplin/lib/PoorManIntervals';
|
||||
import reducer from '@joplin/lib/reducer';
|
||||
@@ -97,13 +97,13 @@ SyncTargetRegistry.addClass(SyncTargetJoplinCloud);
|
||||
|
||||
import FsDriverRN from './utils/fs-driver-rn';
|
||||
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
||||
import EncryptionService from '@joplin/lib/services/EncryptionService';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import MigrationService from '@joplin/lib/services/MigrationService';
|
||||
import { clearSharedFilesCache } from './utils/ShareUtils';
|
||||
import setIgnoreTlsErrors from './utils/TlsUtils';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import setupNotifications from './utils/setupNotifications';
|
||||
import { loadMasterKeysFromSettings } from '@joplin/lib/services/e2ee/utils';
|
||||
import { loadMasterKeysFromSettings, migrateMasterPassword } from '@joplin/lib/services/e2ee/utils';
|
||||
import SyncTargetNone from '../lib/SyncTargetNone';
|
||||
|
||||
let storeDispatch = function(_action: any) {};
|
||||
@@ -474,15 +474,24 @@ async function initialize(dispatch: Function) {
|
||||
if (Setting.value('env') == 'prod') {
|
||||
await db.open({ name: 'joplin.sqlite' });
|
||||
} else {
|
||||
await db.open({ name: 'joplin-101.sqlite' });
|
||||
await db.open({ name: 'joplin-104.sqlite' });
|
||||
|
||||
// await db.clearForTesting();
|
||||
}
|
||||
|
||||
reg.logger().info('Database is ready.');
|
||||
|
||||
reg.logger().info('Loading settings...');
|
||||
|
||||
await loadKeychainServiceAndSettings(KeychainServiceDriverMobile);
|
||||
await migrateMasterPassword();
|
||||
|
||||
if (Setting.value('env') === Env.Dev) {
|
||||
// Setting.setValue('sync.10.path', 'https://api.joplincloud.com');
|
||||
// Setting.setValue('sync.10.userContentPath', 'https://joplinusercontent.com');
|
||||
Setting.setValue('sync.10.path', 'http://api.joplincloud.local:22300');
|
||||
Setting.setValue('sync.10.userContentPath', 'http://joplinusercontent.local:22300');
|
||||
}
|
||||
|
||||
if (!Setting.value('clientId')) Setting.setValue('clientId', uuid.create());
|
||||
|
||||
@@ -530,7 +539,6 @@ async function initialize(dispatch: Function) {
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
EncryptionService.fsDriver_ = fsDriver;
|
||||
EncryptionService.instance().setLogger(mainLogger);
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
BaseItem.encryptionService_ = EncryptionService.instance();
|
||||
BaseItem.shareService_ = ShareService.instance();
|
||||
@@ -634,7 +642,7 @@ async function initialize(dispatch: Function) {
|
||||
|
||||
class AppComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
@@ -677,7 +685,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
|
||||
async componentDidMount() {
|
||||
public async componentDidMount() {
|
||||
if (this.props.appState == 'starting') {
|
||||
this.props.dispatch({
|
||||
type: 'APP_STATE_SET',
|
||||
@@ -725,15 +733,18 @@ class AppComponent extends React.Component {
|
||||
setupQuickActions(this.props.dispatch, this.props.selectedFolderId);
|
||||
|
||||
await setupNotifications(this.props.dispatch);
|
||||
|
||||
// Setting.setValue('encryption.masterPassword', 'WRONG');
|
||||
// setTimeout(() => NavService.go('EncryptionConfig'), 2000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount() {
|
||||
AppState.removeEventListener('change', this.onAppStateChange_);
|
||||
Linking.removeEventListener('url', this.handleOpenURL_);
|
||||
if (this.unsubscribeNetInfoHandler_) this.unsubscribeNetInfoHandler_();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any) {
|
||||
public componentDidUpdate(prevProps: any) {
|
||||
if (this.props.showSideMenu !== prevProps.showSideMenu) {
|
||||
Animated.timing(this.state.sideMenuContentOpacity, {
|
||||
toValue: this.props.showSideMenu ? 0.5 : 0,
|
||||
@@ -742,7 +753,7 @@ class AppComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
async backButtonHandler() {
|
||||
private async backButtonHandler() {
|
||||
if (this.props.noteSelectionEnabled) {
|
||||
this.props.dispatch({ type: 'NOTE_SELECTION_END' });
|
||||
return true;
|
||||
@@ -763,7 +774,7 @@ class AppComponent extends React.Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
async handleShareData() {
|
||||
private async handleShareData() {
|
||||
const sharedData = await ShareExtension.data();
|
||||
if (sharedData) {
|
||||
reg.logger().info('Received shared data');
|
||||
@@ -775,14 +786,14 @@ class AppComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(newProps: any) {
|
||||
public UNSAFE_componentWillReceiveProps(newProps: any) {
|
||||
if (newProps.syncStarted != this.lastSyncStarted_) {
|
||||
if (!newProps.syncStarted) FoldersScreenUtils.refreshFolders();
|
||||
this.lastSyncStarted_ = newProps.syncStarted;
|
||||
}
|
||||
}
|
||||
|
||||
sideMenu_change(isOpen: boolean) {
|
||||
private sideMenu_change(isOpen: boolean) {
|
||||
// Make sure showSideMenu property of state is updated
|
||||
// when the menu is open/closed.
|
||||
this.props.dispatch({
|
||||
@@ -790,7 +801,7 @@ class AppComponent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.props.appState != 'ready') return null;
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
|
||||
2
packages/fork-htmlparser2/package-lock.json
generated
2
packages/fork-htmlparser2/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-htmlparser2",
|
||||
"version": "4.1.33",
|
||||
"version": "4.1.34",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@joplin/fork-htmlparser2",
|
||||
"description": "Fast & forgiving HTML/XML/RSS parser",
|
||||
"version": "4.1.33",
|
||||
"version": "4.1.34",
|
||||
"author": "Felix Boehm <me@feedic.com>",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
2
packages/fork-sax/package-lock.json
generated
2
packages/fork-sax/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-sax",
|
||||
"version": "1.2.37",
|
||||
"version": "1.2.38",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -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.37",
|
||||
"version": "1.2.38",
|
||||
"main": "lib/sax.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -36,14 +36,14 @@ const SyncTargetNextcloud = require('./SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('./SyncTargetWebDAV.js');
|
||||
const SyncTargetDropbox = require('./SyncTargetDropbox.js');
|
||||
const SyncTargetAmazonS3 = require('./SyncTargetAmazonS3.js');
|
||||
import EncryptionService from './services/EncryptionService';
|
||||
import EncryptionService from './services/e2ee/EncryptionService';
|
||||
import ResourceFetcher from './services/ResourceFetcher';
|
||||
import SearchEngineUtils from './services/searchengine/SearchEngineUtils';
|
||||
import SearchEngine from './services/searchengine/SearchEngine';
|
||||
import RevisionService from './services/RevisionService';
|
||||
import ResourceService from './services/ResourceService';
|
||||
import DecryptionWorker from './services/DecryptionWorker';
|
||||
const { loadKeychainServiceAndSettings } = require('./services/SettingUtils');
|
||||
import { loadKeychainServiceAndSettings } from './services/SettingUtils';
|
||||
import MigrationService from './services/MigrationService';
|
||||
import ShareService from './services/share/ShareService';
|
||||
import handleSyncStartupOperation from './services/synchronizer/utils/handleSyncStartupOperation';
|
||||
@@ -51,7 +51,7 @@ import SyncTargetJoplinCloud from './SyncTargetJoplinCloud';
|
||||
const { toSystemSlashes } = require('./path-utils');
|
||||
const { setAutoFreeze } = require('immer');
|
||||
import { getEncryptionEnabled } from './services/synchronizer/syncInfoUtils';
|
||||
import { loadMasterKeysFromSettings } from './services/e2ee/utils';
|
||||
import { loadMasterKeysFromSettings, migrateMasterPassword } from './services/e2ee/utils';
|
||||
import SyncTargetNone from './SyncTargetNone';
|
||||
|
||||
const appLogger: LoggerWrapper = Logger.create('App');
|
||||
@@ -465,6 +465,7 @@ export default class BaseApplication {
|
||||
sideEffects['timeFormat'] = sideEffects['dateFormat'];
|
||||
sideEffects['locale'] = sideEffects['dateFormat'];
|
||||
sideEffects['encryption.passwordCache'] = sideEffects['syncInfoCache'];
|
||||
sideEffects['encryption.masterPassword'] = sideEffects['syncInfoCache'];
|
||||
|
||||
if (action) {
|
||||
const effect = sideEffects[action.key];
|
||||
@@ -768,6 +769,7 @@ export default class BaseApplication {
|
||||
BaseModel.setDb(this.database_);
|
||||
|
||||
await loadKeychainServiceAndSettings(options.keychainEnabled ? KeychainServiceDriver : KeychainServiceDriverDummy);
|
||||
await migrateMasterPassword();
|
||||
await handleSyncStartupOperation();
|
||||
|
||||
appLogger.info(`Client ID: ${Setting.value('clientId')}`);
|
||||
@@ -825,7 +827,6 @@ export default class BaseApplication {
|
||||
|
||||
KvStore.instance().setDb(reg.db());
|
||||
|
||||
EncryptionService.instance().setLogger(globalLogger);
|
||||
BaseItem.encryptionService_ = EncryptionService.instance();
|
||||
BaseItem.shareService_ = ShareService.instance();
|
||||
DecryptionWorker.instance().setLogger(globalLogger);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Logger from './Logger';
|
||||
import Synchronizer from './Synchronizer';
|
||||
import EncryptionService from './services/EncryptionService';
|
||||
import EncryptionService from './services/e2ee/EncryptionService';
|
||||
import shim from './shim';
|
||||
import ResourceService from './services/ResourceService';
|
||||
import ShareService from './services/share/ShareService';
|
||||
|
||||
@@ -259,6 +259,14 @@ 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'];
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const ObjectUtils = {};
|
||||
|
||||
ObjectUtils.sortByValue = function(object) {
|
||||
export function sortByValue(object: any) {
|
||||
const temp = [];
|
||||
for (const k in object) {
|
||||
if (!object.hasOwnProperty(k)) continue;
|
||||
@@ -19,16 +17,16 @@ ObjectUtils.sortByValue = function(object) {
|
||||
return v1 < v2 ? -1 : +1;
|
||||
});
|
||||
|
||||
const output = {};
|
||||
const output: any = {};
|
||||
for (let i = 0; i < temp.length; i++) {
|
||||
const item = temp[i];
|
||||
output[item.key] = item.value;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
ObjectUtils.fieldsEqual = function(o1, o2) {
|
||||
export function fieldsEqual(o1: any, o2: any) {
|
||||
if ((!o1 || !o2) && o1 !== o2) return false;
|
||||
|
||||
for (const k in o1) {
|
||||
@@ -42,10 +40,10 @@ ObjectUtils.fieldsEqual = function(o1, o2) {
|
||||
if (c1.length !== c2.length) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
ObjectUtils.convertValuesToFunctions = function(o) {
|
||||
const output = {};
|
||||
export function convertValuesToFunctions(o: any) {
|
||||
const output: any = {};
|
||||
for (const n in o) {
|
||||
if (!o.hasOwnProperty(n)) continue;
|
||||
output[n] = () => {
|
||||
@@ -53,11 +51,26 @@ ObjectUtils.convertValuesToFunctions = function(o) {
|
||||
};
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
ObjectUtils.isEmpty = function(o) {
|
||||
export function isEmpty(o: any) {
|
||||
if (!o) return true;
|
||||
return Object.keys(o).length === 0 && o.constructor === Object;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = ObjectUtils;
|
||||
// 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;
|
||||
// }
|
||||
@@ -15,7 +15,7 @@ import MasterKey from './models/MasterKey';
|
||||
import BaseModel, { ModelType } from './BaseModel';
|
||||
import time from './time';
|
||||
import ResourceService from './services/ResourceService';
|
||||
import EncryptionService from './services/EncryptionService';
|
||||
import EncryptionService from './services/e2ee/EncryptionService';
|
||||
import JoplinError from './JoplinError';
|
||||
import ShareService from './services/share/ShareService';
|
||||
import TaskQueue from './TaskQueue';
|
||||
|
||||
13
packages/lib/commands/index.ts
Normal file
13
packages/lib/commands/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// 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`
|
||||
@@ -1,4 +1,4 @@
|
||||
import EncryptionService from '../../services/EncryptionService';
|
||||
import EncryptionService from '../../services/e2ee/EncryptionService';
|
||||
import { _ } from '../../locale';
|
||||
import BaseItem from '../../models/BaseItem';
|
||||
import Setting from '../../models/Setting';
|
||||
@@ -8,6 +8,7 @@ import shim from '../../shim';
|
||||
import { MasterKeyEntity } from '../../services/e2ee/types';
|
||||
import time from '../../time';
|
||||
import { masterKeyEnabled, setMasterKeyEnabled } from '../../services/synchronizer/syncInfoUtils';
|
||||
import { findMasterKeyPassword } from '../../services/e2ee/utils';
|
||||
|
||||
class Shared {
|
||||
|
||||
@@ -16,12 +17,16 @@ class Shared {
|
||||
public initialize(comp: any, props: any) {
|
||||
comp.state = {
|
||||
passwordChecks: {},
|
||||
// Master keys that can be decrypted with the master password
|
||||
// (normally all of them, but for legacy support we need this).
|
||||
masterPasswordKeys: {},
|
||||
stats: {
|
||||
encrypted: null,
|
||||
total: null,
|
||||
},
|
||||
passwords: Object.assign({}, props.passwords),
|
||||
showDisabledMasterKeys: false,
|
||||
masterPasswordInput: '',
|
||||
};
|
||||
comp.isMounted_ = false;
|
||||
|
||||
@@ -108,15 +113,37 @@ class Shared {
|
||||
}
|
||||
}
|
||||
|
||||
public async masterPasswordIsValid(comp: any, masterPassword: string = null) {
|
||||
const activeMasterKey = comp.props.masterKeys.find((mk: MasterKeyEntity) => mk.id === comp.props.activeMasterKeyId);
|
||||
masterPassword = masterPassword === null ? comp.props.masterPassword : masterPassword;
|
||||
if (activeMasterKey && masterPassword) {
|
||||
return EncryptionService.instance().checkMasterKeyPassword(activeMasterKey, masterPassword);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async checkPasswords(comp: any) {
|
||||
const passwordChecks = Object.assign({}, comp.state.passwordChecks);
|
||||
const masterPasswordKeys = Object.assign({}, comp.state.masterPasswordKeys);
|
||||
for (let i = 0; i < comp.props.masterKeys.length; i++) {
|
||||
const mk = comp.props.masterKeys[i];
|
||||
const password = comp.state.passwords[mk.id];
|
||||
const password = await findMasterKeyPassword(EncryptionService.instance(), mk);
|
||||
const ok = password ? await EncryptionService.instance().checkMasterKeyPassword(mk, password) : false;
|
||||
passwordChecks[mk.id] = ok;
|
||||
masterPasswordKeys[mk.id] = password === comp.props.masterPassword;
|
||||
}
|
||||
comp.setState({ passwordChecks: passwordChecks });
|
||||
|
||||
passwordChecks['master'] = await this.masterPasswordIsValid(comp);
|
||||
|
||||
comp.setState({ passwordChecks, masterPasswordKeys });
|
||||
}
|
||||
|
||||
public masterPasswordStatus(comp: any) {
|
||||
// Don't translate for now because that's temporary - later it should
|
||||
// always be set and the label should be replaced by a "Change master
|
||||
// password" button.
|
||||
return comp.props.masterPassword ? 'Master password is set' : 'Master password is not set';
|
||||
}
|
||||
|
||||
public decryptedStatText(comp: any) {
|
||||
@@ -138,6 +165,14 @@ class Shared {
|
||||
comp.checkPasswords();
|
||||
}
|
||||
|
||||
public onMasterPasswordChange(comp: any, value: string) {
|
||||
comp.setState({ masterPasswordInput: value });
|
||||
}
|
||||
|
||||
public onMasterPasswordSave(comp: any) {
|
||||
Setting.setValue('encryption.masterPassword', comp.state.masterPasswordInput);
|
||||
}
|
||||
|
||||
public onPasswordChange(comp: any, mk: MasterKeyEntity, password: string) {
|
||||
const passwords = Object.assign({}, comp.state.passwords);
|
||||
passwords[mk.id] = password;
|
||||
|
||||
@@ -77,7 +77,7 @@ export default class Database {
|
||||
throw new Error(`Invalid field format: ${field}`);
|
||||
}
|
||||
|
||||
escapeFields(fields: string[] | string): string[] | string {
|
||||
public escapeFields(fields: string[] | string): string[] | string {
|
||||
if (fields == '*') return '*';
|
||||
|
||||
const output = [];
|
||||
@@ -87,6 +87,16 @@ 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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user