You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-26 23:38:08 +02:00
Compare commits
63 Commits
release-se
...
server_mai
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b686a9b164 | ||
|
|
853830d8da | ||
|
|
aa9397e9e7 | ||
|
|
411ce01552 | ||
|
|
0f85acf16d | ||
|
|
e94503abbe | ||
|
|
427e32d324 | ||
|
|
f8253cc2f0 | ||
|
|
2806aa1b19 | ||
|
|
8f57e07279 | ||
|
|
6ff560f22f | ||
|
|
2226b79c46 | ||
|
|
9e9bf63d70 | ||
|
|
5d9419be5d | ||
|
|
9f37aa96c6 | ||
|
|
eceb14ff9e | ||
|
|
6a41d43399 | ||
|
|
85211e8d5c | ||
|
|
bd08041f53 | ||
|
|
fa3895452f | ||
|
|
77b284f01f | ||
|
|
daaaa133ab | ||
|
|
e6c4eb7cdf | ||
|
|
dc2cdb7d3a | ||
|
|
f3e03d48bb | ||
|
|
6577f4f35d | ||
|
|
d29624c816 | ||
|
|
6afde54bda | ||
|
|
ec7f0f479a | ||
|
|
7f05420fda | ||
|
|
a3f8cd4850 | ||
|
|
01ccf5170a | ||
|
|
6ddb69e1ea | ||
|
|
b01f82bb33 | ||
|
|
b6c9edba21 | ||
|
|
f7d164be6e | ||
|
|
6f2f24171d | ||
|
|
12cc64008b | ||
|
|
b9955f58d3 | ||
|
|
489995daef | ||
|
|
e156ee1b58 | ||
|
|
a24b0091ad | ||
|
|
2655b6deee | ||
|
|
45c40f7395 | ||
|
|
ecb0eee355 | ||
|
|
4916f4cc92 | ||
|
|
15fe119256 | ||
|
|
0b46880a00 | ||
|
|
deaa731983 | ||
|
|
d061bb1a4f | ||
|
|
03db0c5486 | ||
|
|
bb275e671d | ||
|
|
2d0580ff71 | ||
|
|
2331d3487b | ||
|
|
f1380fd51d | ||
|
|
d462dab8eb | ||
|
|
cf37b74d9a | ||
|
|
aec3ea9c0c | ||
|
|
1f5aa70acd | ||
|
|
416637ce83 | ||
|
|
99c4b0bc01 | ||
|
|
74d8fec98a | ||
|
|
321a58c356 |
219
.eslintignore
219
.eslintignore
@@ -74,69 +74,21 @@ packages/app-cli/app/command-settingschema.js.map
|
||||
packages/app-cli/app/services/plugins/PluginRunner.d.ts
|
||||
packages/app-cli/app/services/plugins/PluginRunner.js
|
||||
packages/app-cli/app/services/plugins/PluginRunner.js.map
|
||||
packages/app-cli/tests/EnexToMd.d.ts
|
||||
packages/app-cli/tests/EnexToMd.js
|
||||
packages/app-cli/tests/EnexToMd.js.map
|
||||
packages/app-cli/tests/HtmlToMd.d.ts
|
||||
packages/app-cli/tests/HtmlToMd.js
|
||||
packages/app-cli/tests/HtmlToMd.js.map
|
||||
packages/app-cli/tests/InMemoryCache.d.ts
|
||||
packages/app-cli/tests/InMemoryCache.js
|
||||
packages/app-cli/tests/InMemoryCache.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/Synchronizer.basics.d.ts
|
||||
packages/app-cli/tests/Synchronizer.basics.js
|
||||
packages/app-cli/tests/Synchronizer.basics.js.map
|
||||
packages/app-cli/tests/Synchronizer.conflicts.d.ts
|
||||
packages/app-cli/tests/Synchronizer.conflicts.js
|
||||
packages/app-cli/tests/Synchronizer.conflicts.js.map
|
||||
packages/app-cli/tests/Synchronizer.e2ee.d.ts
|
||||
packages/app-cli/tests/Synchronizer.e2ee.js
|
||||
packages/app-cli/tests/Synchronizer.e2ee.js.map
|
||||
packages/app-cli/tests/Synchronizer.resources.d.ts
|
||||
packages/app-cli/tests/Synchronizer.resources.js
|
||||
packages/app-cli/tests/Synchronizer.resources.js.map
|
||||
packages/app-cli/tests/Synchronizer.revisions.d.ts
|
||||
packages/app-cli/tests/Synchronizer.revisions.js
|
||||
packages/app-cli/tests/Synchronizer.revisions.js.map
|
||||
packages/app-cli/tests/Synchronizer.sharing.d.ts
|
||||
packages/app-cli/tests/Synchronizer.sharing.js
|
||||
packages/app-cli/tests/Synchronizer.sharing.js.map
|
||||
packages/app-cli/tests/Synchronizer.tags.d.ts
|
||||
packages/app-cli/tests/Synchronizer.tags.js
|
||||
packages/app-cli/tests/Synchronizer.tags.js.map
|
||||
packages/app-cli/tests/Synchronizer.tools.d.ts
|
||||
packages/app-cli/tests/Synchronizer.tools.js
|
||||
packages/app-cli/tests/Synchronizer.tools.js.map
|
||||
packages/app-cli/tests/dateTimeFormats.d.ts
|
||||
packages/app-cli/tests/dateTimeFormats.js
|
||||
packages/app-cli/tests/dateTimeFormats.js.map
|
||||
packages/app-cli/tests/file-api-driver.d.ts
|
||||
packages/app-cli/tests/file-api-driver.js
|
||||
packages/app-cli/tests/file-api-driver.js.map
|
||||
packages/app-cli/tests/fsDriver.d.ts
|
||||
packages/app-cli/tests/fsDriver.js
|
||||
packages/app-cli/tests/fsDriver.js.map
|
||||
packages/app-cli/tests/htmlUtils.d.ts
|
||||
packages/app-cli/tests/htmlUtils.js
|
||||
packages/app-cli/tests/htmlUtils.js.map
|
||||
packages/app-cli/tests/models_Folder.d.ts
|
||||
packages/app-cli/tests/models_Folder.js
|
||||
packages/app-cli/tests/models_Folder.js.map
|
||||
packages/app-cli/tests/models_Folder.sharing.d.ts
|
||||
packages/app-cli/tests/models_Folder.sharing.js
|
||||
packages/app-cli/tests/models_Folder.sharing.js.map
|
||||
packages/app-cli/tests/models_Note.d.ts
|
||||
packages/app-cli/tests/models_Note.js
|
||||
packages/app-cli/tests/models_Note.js.map
|
||||
packages/app-cli/tests/models_Setting.d.ts
|
||||
packages/app-cli/tests/models_Setting.js
|
||||
packages/app-cli/tests/models_Setting.js.map
|
||||
packages/app-cli/tests/registry.d.ts
|
||||
packages/app-cli/tests/registry.js
|
||||
packages/app-cli/tests/registry.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
|
||||
packages/app-cli/tests/services/plugins/PluginService.d.ts
|
||||
packages/app-cli/tests/services/plugins/PluginService.js
|
||||
packages/app-cli/tests/services/plugins/PluginService.js.map
|
||||
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
|
||||
packages/app-cli/tests/services/plugins/RepositoryApi.js
|
||||
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
|
||||
@@ -152,42 +104,6 @@ packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
|
||||
packages/app-cli/tests/services_CommandService.d.ts
|
||||
packages/app-cli/tests/services_CommandService.js
|
||||
packages/app-cli/tests/services_CommandService.js.map
|
||||
packages/app-cli/tests/services_InteropService.d.ts
|
||||
packages/app-cli/tests/services_InteropService.js
|
||||
packages/app-cli/tests/services_InteropService.js.map
|
||||
packages/app-cli/tests/services_InteropService_Exporter_Html.d.ts
|
||||
packages/app-cli/tests/services_InteropService_Exporter_Html.js
|
||||
packages/app-cli/tests/services_InteropService_Exporter_Html.js.map
|
||||
packages/app-cli/tests/services_PluginService.d.ts
|
||||
packages/app-cli/tests/services_PluginService.js
|
||||
packages/app-cli/tests/services_PluginService.js.map
|
||||
packages/app-cli/tests/services_ResourceService.d.ts
|
||||
packages/app-cli/tests/services_ResourceService.js
|
||||
packages/app-cli/tests/services_ResourceService.js.map
|
||||
packages/app-cli/tests/services_SearchEngineUtils.d.ts
|
||||
packages/app-cli/tests/services_SearchEngineUtils.js
|
||||
packages/app-cli/tests/services_SearchEngineUtils.js.map
|
||||
packages/app-cli/tests/services_keychainService.d.ts
|
||||
packages/app-cli/tests/services_keychainService.js
|
||||
packages/app-cli/tests/services_keychainService.js.map
|
||||
packages/app-cli/tests/services_rest_Api.d.ts
|
||||
packages/app-cli/tests/services_rest_Api.js
|
||||
packages/app-cli/tests/services_rest_Api.js.map
|
||||
packages/app-cli/tests/synchronizer_LockHandler.d.ts
|
||||
packages/app-cli/tests/synchronizer_LockHandler.js
|
||||
packages/app-cli/tests/synchronizer_LockHandler.js.map
|
||||
packages/app-cli/tests/synchronizer_MigrationHandler.d.ts
|
||||
packages/app-cli/tests/synchronizer_MigrationHandler.js
|
||||
packages/app-cli/tests/synchronizer_MigrationHandler.js.map
|
||||
packages/app-cli/tests/test-utils-synchronizer.d.ts
|
||||
packages/app-cli/tests/test-utils-synchronizer.js
|
||||
packages/app-cli/tests/test-utils-synchronizer.js.map
|
||||
packages/app-cli/tests/test-utils.d.ts
|
||||
packages/app-cli/tests/test-utils.js
|
||||
packages/app-cli/tests/test-utils.js.map
|
||||
packages/app-desktop/ElectronAppWrapper.d.ts
|
||||
packages/app-desktop/ElectronAppWrapper.js
|
||||
packages/app-desktop/ElectronAppWrapper.js.map
|
||||
@@ -578,6 +494,9 @@ packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
|
||||
@@ -677,6 +596,9 @@ packages/app-desktop/gui/style/StyledFormLabel.js.map
|
||||
packages/app-desktop/gui/style/StyledInput.d.ts
|
||||
packages/app-desktop/gui/style/StyledInput.js
|
||||
packages/app-desktop/gui/style/StyledInput.js.map
|
||||
packages/app-desktop/gui/style/StyledLink.d.ts
|
||||
packages/app-desktop/gui/style/StyledLink.js
|
||||
packages/app-desktop/gui/style/StyledLink.js.map
|
||||
packages/app-desktop/gui/style/StyledMessage.d.ts
|
||||
packages/app-desktop/gui/style/StyledMessage.js
|
||||
packages/app-desktop/gui/style/StyledMessage.js.map
|
||||
@@ -782,9 +704,9 @@ packages/app-mobile/services/AlarmServiceDriver.android.js.map
|
||||
packages/app-mobile/services/AlarmServiceDriver.ios.d.ts
|
||||
packages/app-mobile/services/AlarmServiceDriver.ios.js
|
||||
packages/app-mobile/services/AlarmServiceDriver.ios.js.map
|
||||
packages/app-mobile/setUpQuickActions.d.ts
|
||||
packages/app-mobile/setUpQuickActions.js
|
||||
packages/app-mobile/setUpQuickActions.js.map
|
||||
packages/app-mobile/setupQuickActions.d.ts
|
||||
packages/app-mobile/setupQuickActions.js
|
||||
packages/app-mobile/setupQuickActions.js.map
|
||||
packages/app-mobile/utils/ShareExtension.d.ts
|
||||
packages/app-mobile/utils/ShareExtension.js
|
||||
packages/app-mobile/utils/ShareExtension.js.map
|
||||
@@ -800,6 +722,9 @@ packages/app-mobile/utils/checkPermissions.js.map
|
||||
packages/app-mobile/utils/fs-driver-rn.d.ts
|
||||
packages/app-mobile/utils/fs-driver-rn.js
|
||||
packages/app-mobile/utils/fs-driver-rn.js.map
|
||||
packages/app-mobile/utils/setupNotifications.d.ts
|
||||
packages/app-mobile/utils/setupNotifications.js
|
||||
packages/app-mobile/utils/setupNotifications.js.map
|
||||
packages/app-mobile/utils/shareHandler.d.ts
|
||||
packages/app-mobile/utils/shareHandler.js
|
||||
packages/app-mobile/utils/shareHandler.js.map
|
||||
@@ -875,6 +800,9 @@ packages/lib/HtmlToMd.js.map
|
||||
packages/lib/InMemoryCache.d.ts
|
||||
packages/lib/InMemoryCache.js
|
||||
packages/lib/InMemoryCache.js.map
|
||||
packages/lib/InMemoryCache.test.d.ts
|
||||
packages/lib/InMemoryCache.test.js
|
||||
packages/lib/InMemoryCache.test.js.map
|
||||
packages/lib/JoplinDatabase.d.ts
|
||||
packages/lib/JoplinDatabase.js
|
||||
packages/lib/JoplinDatabase.js.map
|
||||
@@ -926,6 +854,9 @@ packages/lib/eventManager.js.map
|
||||
packages/lib/file-api-driver-joplinServer.d.ts
|
||||
packages/lib/file-api-driver-joplinServer.js
|
||||
packages/lib/file-api-driver-joplinServer.js.map
|
||||
packages/lib/file-api-driver.test.d.ts
|
||||
packages/lib/file-api-driver.test.js
|
||||
packages/lib/file-api-driver.test.js.map
|
||||
packages/lib/file-api.d.ts
|
||||
packages/lib/file-api.js
|
||||
packages/lib/file-api.js.map
|
||||
@@ -935,12 +866,24 @@ packages/lib/fs-driver-base.js.map
|
||||
packages/lib/fs-driver-node.d.ts
|
||||
packages/lib/fs-driver-node.js
|
||||
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/htmlUtils.d.ts
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils.js.map
|
||||
packages/lib/htmlUtils.test.d.ts
|
||||
packages/lib/htmlUtils.test.js
|
||||
packages/lib/htmlUtils.test.js.map
|
||||
packages/lib/htmlUtils2.test.d.ts
|
||||
packages/lib/htmlUtils2.test.js
|
||||
packages/lib/htmlUtils2.test.js.map
|
||||
packages/lib/import-enex-md-gen.d.ts
|
||||
packages/lib/import-enex-md-gen.js
|
||||
packages/lib/import-enex-md-gen.js.map
|
||||
packages/lib/import-enex-md-gen.test.d.ts
|
||||
packages/lib/import-enex-md-gen.test.js
|
||||
packages/lib/import-enex-md-gen.test.js.map
|
||||
packages/lib/locale.d.ts
|
||||
packages/lib/locale.js
|
||||
packages/lib/locale.js.map
|
||||
@@ -950,6 +893,9 @@ packages/lib/markdownUtils.js.map
|
||||
packages/lib/markdownUtils.test.d.ts
|
||||
packages/lib/markdownUtils.test.js
|
||||
packages/lib/markdownUtils.test.js.map
|
||||
packages/lib/markdownUtils2.test.d.ts
|
||||
packages/lib/markdownUtils2.test.js
|
||||
packages/lib/markdownUtils2.test.js.map
|
||||
packages/lib/markupLanguageUtils.d.ts
|
||||
packages/lib/markupLanguageUtils.js
|
||||
packages/lib/markupLanguageUtils.js.map
|
||||
@@ -962,6 +908,12 @@ packages/lib/models/BaseItem.js.map
|
||||
packages/lib/models/Folder.d.ts
|
||||
packages/lib/models/Folder.js
|
||||
packages/lib/models/Folder.js.map
|
||||
packages/lib/models/Folder.sharing.test.d.ts
|
||||
packages/lib/models/Folder.sharing.test.js
|
||||
packages/lib/models/Folder.sharing.test.js.map
|
||||
packages/lib/models/Folder.test.d.ts
|
||||
packages/lib/models/Folder.test.js
|
||||
packages/lib/models/Folder.test.js.map
|
||||
packages/lib/models/ItemChange.d.ts
|
||||
packages/lib/models/ItemChange.js
|
||||
packages/lib/models/ItemChange.js.map
|
||||
@@ -974,6 +926,9 @@ packages/lib/models/Migration.js.map
|
||||
packages/lib/models/Note.d.ts
|
||||
packages/lib/models/Note.js
|
||||
packages/lib/models/Note.js.map
|
||||
packages/lib/models/Note.test.d.ts
|
||||
packages/lib/models/Note.test.js
|
||||
packages/lib/models/Note.test.js.map
|
||||
packages/lib/models/NoteResource.d.ts
|
||||
packages/lib/models/NoteResource.js
|
||||
packages/lib/models/NoteResource.js.map
|
||||
@@ -995,12 +950,18 @@ packages/lib/models/Search.js.map
|
||||
packages/lib/models/Setting.d.ts
|
||||
packages/lib/models/Setting.js
|
||||
packages/lib/models/Setting.js.map
|
||||
packages/lib/models/Setting.test.d.ts
|
||||
packages/lib/models/Setting.test.js
|
||||
packages/lib/models/Setting.test.js.map
|
||||
packages/lib/models/SmartFilter.d.ts
|
||||
packages/lib/models/SmartFilter.js
|
||||
packages/lib/models/SmartFilter.js.map
|
||||
packages/lib/models/Tag.d.ts
|
||||
packages/lib/models/Tag.js
|
||||
packages/lib/models/Tag.js.map
|
||||
packages/lib/models/dateTimeFormats.test.d.ts
|
||||
packages/lib/models/dateTimeFormats.test.js
|
||||
packages/lib/models/dateTimeFormats.test.js.map
|
||||
packages/lib/models/settings/FileHandler.d.ts
|
||||
packages/lib/models/settings/FileHandler.js
|
||||
packages/lib/models/settings/FileHandler.js.map
|
||||
@@ -1028,6 +989,9 @@ packages/lib/reducer.js.map
|
||||
packages/lib/registry.d.ts
|
||||
packages/lib/registry.js
|
||||
packages/lib/registry.js.map
|
||||
packages/lib/registry.test.d.ts
|
||||
packages/lib/registry.test.js
|
||||
packages/lib/registry.test.js.map
|
||||
packages/lib/services/AlarmService.d.ts
|
||||
packages/lib/services/AlarmService.js
|
||||
packages/lib/services/AlarmService.js.map
|
||||
@@ -1040,6 +1004,9 @@ packages/lib/services/BaseService.js.map
|
||||
packages/lib/services/CommandService.d.ts
|
||||
packages/lib/services/CommandService.js
|
||||
packages/lib/services/CommandService.js.map
|
||||
packages/lib/services/CommandService.test.d.ts
|
||||
packages/lib/services/CommandService.test.js
|
||||
packages/lib/services/CommandService.test.js.map
|
||||
packages/lib/services/DecryptionWorker.d.ts
|
||||
packages/lib/services/DecryptionWorker.js
|
||||
packages/lib/services/DecryptionWorker.js.map
|
||||
@@ -1088,6 +1055,9 @@ packages/lib/services/ResourceFetcher.js.map
|
||||
packages/lib/services/ResourceService.d.ts
|
||||
packages/lib/services/ResourceService.js
|
||||
packages/lib/services/ResourceService.js.map
|
||||
packages/lib/services/ResourceService.test.d.ts
|
||||
packages/lib/services/ResourceService.test.js
|
||||
packages/lib/services/ResourceService.test.js.map
|
||||
packages/lib/services/RevisionService.d.ts
|
||||
packages/lib/services/RevisionService.js
|
||||
packages/lib/services/RevisionService.js.map
|
||||
@@ -1100,6 +1070,9 @@ packages/lib/services/UndoRedoService.js.map
|
||||
packages/lib/services/WhenClause.d.ts
|
||||
packages/lib/services/WhenClause.js
|
||||
packages/lib/services/WhenClause.js.map
|
||||
packages/lib/services/WhenClause.test.d.ts
|
||||
packages/lib/services/WhenClause.test.js
|
||||
packages/lib/services/WhenClause.test.js.map
|
||||
packages/lib/services/commands/MenuUtils.d.ts
|
||||
packages/lib/services/commands/MenuUtils.js
|
||||
packages/lib/services/commands/MenuUtils.js.map
|
||||
@@ -1133,6 +1106,9 @@ packages/lib/services/debug/populateDatabase.js.map
|
||||
packages/lib/services/interop/InteropService.d.ts
|
||||
packages/lib/services/interop/InteropService.js
|
||||
packages/lib/services/interop/InteropService.js.map
|
||||
packages/lib/services/interop/InteropService.test.d.ts
|
||||
packages/lib/services/interop/InteropService.test.js
|
||||
packages/lib/services/interop/InteropService.test.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.js.map
|
||||
@@ -1142,6 +1118,9 @@ packages/lib/services/interop/InteropService_Exporter_Custom.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.js.map
|
||||
@@ -1304,6 +1283,9 @@ packages/lib/services/plugins/utils/validatePluginId.test.js.map
|
||||
packages/lib/services/rest/Api.d.ts
|
||||
packages/lib/services/rest/Api.js
|
||||
packages/lib/services/rest/Api.js.map
|
||||
packages/lib/services/rest/Api.test.d.ts
|
||||
packages/lib/services/rest/Api.test.js
|
||||
packages/lib/services/rest/Api.test.js.map
|
||||
packages/lib/services/rest/ApiResponse.d.ts
|
||||
packages/lib/services/rest/ApiResponse.js
|
||||
packages/lib/services/rest/ApiResponse.js.map
|
||||
@@ -1364,6 +1346,9 @@ packages/lib/services/searchengine/SearchEngine.js.map
|
||||
packages/lib/services/searchengine/SearchEngineUtils.d.ts
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js.map
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.d.ts
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.js.map
|
||||
packages/lib/services/searchengine/filterParser.d.ts
|
||||
packages/lib/services/searchengine/filterParser.js
|
||||
packages/lib/services/searchengine/filterParser.js.map
|
||||
@@ -1388,6 +1373,30 @@ packages/lib/services/synchronizer/LockHandler.js.map
|
||||
packages/lib/services/synchronizer/MigrationHandler.d.ts
|
||||
packages/lib/services/synchronizer/MigrationHandler.js
|
||||
packages/lib/services/synchronizer/MigrationHandler.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.conflicts.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.e2ee.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.resources.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.resources.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.resources.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.revisions.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.revisions.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.revisions.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.sharing.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.sharing.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.sharing.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.js.map
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.d.ts
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js.map
|
||||
@@ -1397,6 +1406,12 @@ packages/lib/services/synchronizer/migrations/1.js.map
|
||||
packages/lib/services/synchronizer/migrations/2.d.ts
|
||||
packages/lib/services/synchronizer/migrations/2.js
|
||||
packages/lib/services/synchronizer/migrations/2.js.map
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.d.ts
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js.map
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.d.ts
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js.map
|
||||
packages/lib/services/synchronizer/tools.d.ts
|
||||
packages/lib/services/synchronizer/tools.js
|
||||
packages/lib/services/synchronizer/tools.js.map
|
||||
@@ -1409,6 +1424,12 @@ packages/lib/services/synchronizer/utils/types.js.map
|
||||
packages/lib/shim.d.ts
|
||||
packages/lib/shim.js
|
||||
packages/lib/shim.js.map
|
||||
packages/lib/testing/test-utils-synchronizer.d.ts
|
||||
packages/lib/testing/test-utils-synchronizer.js
|
||||
packages/lib/testing/test-utils-synchronizer.js.map
|
||||
packages/lib/testing/test-utils.d.ts
|
||||
packages/lib/testing/test-utils.js
|
||||
packages/lib/testing/test-utils.js.map
|
||||
packages/lib/theme.d.ts
|
||||
packages/lib/theme.js
|
||||
packages/lib/theme.js.map
|
||||
@@ -1442,6 +1463,9 @@ packages/lib/themes/type.js.map
|
||||
packages/lib/time.d.ts
|
||||
packages/lib/time.js
|
||||
packages/lib/time.js.map
|
||||
packages/lib/utils/credentialFiles.d.ts
|
||||
packages/lib/utils/credentialFiles.js
|
||||
packages/lib/utils/credentialFiles.js.map
|
||||
packages/lib/uuid.d.ts
|
||||
packages/lib/uuid.js
|
||||
packages/lib/uuid.js.map
|
||||
@@ -1568,6 +1592,9 @@ packages/tools/generate-database-types.js.map
|
||||
packages/tools/lerna-add.d.ts
|
||||
packages/tools/lerna-add.js
|
||||
packages/tools/lerna-add.js.map
|
||||
packages/tools/release-android.d.ts
|
||||
packages/tools/release-android.js
|
||||
packages/tools/release-android.js.map
|
||||
packages/tools/release-cli.d.ts
|
||||
packages/tools/release-cli.js
|
||||
packages/tools/release-cli.js.map
|
||||
|
||||
@@ -76,7 +76,7 @@ module.exports = {
|
||||
|
||||
// Warn only for now because fixing everything would take too much
|
||||
// refactoring, but new code should try to stick to it.
|
||||
'complexity': ['warn', { max: 10 }],
|
||||
// 'complexity': ['warn', { max: 10 }],
|
||||
|
||||
// Checks rules of Hooks
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
|
||||
219
.gitignore
vendored
219
.gitignore
vendored
@@ -60,69 +60,21 @@ packages/app-cli/app/command-settingschema.js.map
|
||||
packages/app-cli/app/services/plugins/PluginRunner.d.ts
|
||||
packages/app-cli/app/services/plugins/PluginRunner.js
|
||||
packages/app-cli/app/services/plugins/PluginRunner.js.map
|
||||
packages/app-cli/tests/EnexToMd.d.ts
|
||||
packages/app-cli/tests/EnexToMd.js
|
||||
packages/app-cli/tests/EnexToMd.js.map
|
||||
packages/app-cli/tests/HtmlToMd.d.ts
|
||||
packages/app-cli/tests/HtmlToMd.js
|
||||
packages/app-cli/tests/HtmlToMd.js.map
|
||||
packages/app-cli/tests/InMemoryCache.d.ts
|
||||
packages/app-cli/tests/InMemoryCache.js
|
||||
packages/app-cli/tests/InMemoryCache.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/Synchronizer.basics.d.ts
|
||||
packages/app-cli/tests/Synchronizer.basics.js
|
||||
packages/app-cli/tests/Synchronizer.basics.js.map
|
||||
packages/app-cli/tests/Synchronizer.conflicts.d.ts
|
||||
packages/app-cli/tests/Synchronizer.conflicts.js
|
||||
packages/app-cli/tests/Synchronizer.conflicts.js.map
|
||||
packages/app-cli/tests/Synchronizer.e2ee.d.ts
|
||||
packages/app-cli/tests/Synchronizer.e2ee.js
|
||||
packages/app-cli/tests/Synchronizer.e2ee.js.map
|
||||
packages/app-cli/tests/Synchronizer.resources.d.ts
|
||||
packages/app-cli/tests/Synchronizer.resources.js
|
||||
packages/app-cli/tests/Synchronizer.resources.js.map
|
||||
packages/app-cli/tests/Synchronizer.revisions.d.ts
|
||||
packages/app-cli/tests/Synchronizer.revisions.js
|
||||
packages/app-cli/tests/Synchronizer.revisions.js.map
|
||||
packages/app-cli/tests/Synchronizer.sharing.d.ts
|
||||
packages/app-cli/tests/Synchronizer.sharing.js
|
||||
packages/app-cli/tests/Synchronizer.sharing.js.map
|
||||
packages/app-cli/tests/Synchronizer.tags.d.ts
|
||||
packages/app-cli/tests/Synchronizer.tags.js
|
||||
packages/app-cli/tests/Synchronizer.tags.js.map
|
||||
packages/app-cli/tests/Synchronizer.tools.d.ts
|
||||
packages/app-cli/tests/Synchronizer.tools.js
|
||||
packages/app-cli/tests/Synchronizer.tools.js.map
|
||||
packages/app-cli/tests/dateTimeFormats.d.ts
|
||||
packages/app-cli/tests/dateTimeFormats.js
|
||||
packages/app-cli/tests/dateTimeFormats.js.map
|
||||
packages/app-cli/tests/file-api-driver.d.ts
|
||||
packages/app-cli/tests/file-api-driver.js
|
||||
packages/app-cli/tests/file-api-driver.js.map
|
||||
packages/app-cli/tests/fsDriver.d.ts
|
||||
packages/app-cli/tests/fsDriver.js
|
||||
packages/app-cli/tests/fsDriver.js.map
|
||||
packages/app-cli/tests/htmlUtils.d.ts
|
||||
packages/app-cli/tests/htmlUtils.js
|
||||
packages/app-cli/tests/htmlUtils.js.map
|
||||
packages/app-cli/tests/models_Folder.d.ts
|
||||
packages/app-cli/tests/models_Folder.js
|
||||
packages/app-cli/tests/models_Folder.js.map
|
||||
packages/app-cli/tests/models_Folder.sharing.d.ts
|
||||
packages/app-cli/tests/models_Folder.sharing.js
|
||||
packages/app-cli/tests/models_Folder.sharing.js.map
|
||||
packages/app-cli/tests/models_Note.d.ts
|
||||
packages/app-cli/tests/models_Note.js
|
||||
packages/app-cli/tests/models_Note.js.map
|
||||
packages/app-cli/tests/models_Setting.d.ts
|
||||
packages/app-cli/tests/models_Setting.js
|
||||
packages/app-cli/tests/models_Setting.js.map
|
||||
packages/app-cli/tests/registry.d.ts
|
||||
packages/app-cli/tests/registry.js
|
||||
packages/app-cli/tests/registry.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
|
||||
packages/app-cli/tests/services/plugins/PluginService.d.ts
|
||||
packages/app-cli/tests/services/plugins/PluginService.js
|
||||
packages/app-cli/tests/services/plugins/PluginService.js.map
|
||||
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
|
||||
packages/app-cli/tests/services/plugins/RepositoryApi.js
|
||||
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
|
||||
@@ -138,42 +90,6 @@ packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
||||
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
|
||||
packages/app-cli/tests/services_CommandService.d.ts
|
||||
packages/app-cli/tests/services_CommandService.js
|
||||
packages/app-cli/tests/services_CommandService.js.map
|
||||
packages/app-cli/tests/services_InteropService.d.ts
|
||||
packages/app-cli/tests/services_InteropService.js
|
||||
packages/app-cli/tests/services_InteropService.js.map
|
||||
packages/app-cli/tests/services_InteropService_Exporter_Html.d.ts
|
||||
packages/app-cli/tests/services_InteropService_Exporter_Html.js
|
||||
packages/app-cli/tests/services_InteropService_Exporter_Html.js.map
|
||||
packages/app-cli/tests/services_PluginService.d.ts
|
||||
packages/app-cli/tests/services_PluginService.js
|
||||
packages/app-cli/tests/services_PluginService.js.map
|
||||
packages/app-cli/tests/services_ResourceService.d.ts
|
||||
packages/app-cli/tests/services_ResourceService.js
|
||||
packages/app-cli/tests/services_ResourceService.js.map
|
||||
packages/app-cli/tests/services_SearchEngineUtils.d.ts
|
||||
packages/app-cli/tests/services_SearchEngineUtils.js
|
||||
packages/app-cli/tests/services_SearchEngineUtils.js.map
|
||||
packages/app-cli/tests/services_keychainService.d.ts
|
||||
packages/app-cli/tests/services_keychainService.js
|
||||
packages/app-cli/tests/services_keychainService.js.map
|
||||
packages/app-cli/tests/services_rest_Api.d.ts
|
||||
packages/app-cli/tests/services_rest_Api.js
|
||||
packages/app-cli/tests/services_rest_Api.js.map
|
||||
packages/app-cli/tests/synchronizer_LockHandler.d.ts
|
||||
packages/app-cli/tests/synchronizer_LockHandler.js
|
||||
packages/app-cli/tests/synchronizer_LockHandler.js.map
|
||||
packages/app-cli/tests/synchronizer_MigrationHandler.d.ts
|
||||
packages/app-cli/tests/synchronizer_MigrationHandler.js
|
||||
packages/app-cli/tests/synchronizer_MigrationHandler.js.map
|
||||
packages/app-cli/tests/test-utils-synchronizer.d.ts
|
||||
packages/app-cli/tests/test-utils-synchronizer.js
|
||||
packages/app-cli/tests/test-utils-synchronizer.js.map
|
||||
packages/app-cli/tests/test-utils.d.ts
|
||||
packages/app-cli/tests/test-utils.js
|
||||
packages/app-cli/tests/test-utils.js.map
|
||||
packages/app-desktop/ElectronAppWrapper.d.ts
|
||||
packages/app-desktop/ElectronAppWrapper.js
|
||||
packages/app-desktop/ElectronAppWrapper.js.map
|
||||
@@ -564,6 +480,9 @@ packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js.map
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
|
||||
@@ -663,6 +582,9 @@ packages/app-desktop/gui/style/StyledFormLabel.js.map
|
||||
packages/app-desktop/gui/style/StyledInput.d.ts
|
||||
packages/app-desktop/gui/style/StyledInput.js
|
||||
packages/app-desktop/gui/style/StyledInput.js.map
|
||||
packages/app-desktop/gui/style/StyledLink.d.ts
|
||||
packages/app-desktop/gui/style/StyledLink.js
|
||||
packages/app-desktop/gui/style/StyledLink.js.map
|
||||
packages/app-desktop/gui/style/StyledMessage.d.ts
|
||||
packages/app-desktop/gui/style/StyledMessage.js
|
||||
packages/app-desktop/gui/style/StyledMessage.js.map
|
||||
@@ -768,9 +690,9 @@ packages/app-mobile/services/AlarmServiceDriver.android.js.map
|
||||
packages/app-mobile/services/AlarmServiceDriver.ios.d.ts
|
||||
packages/app-mobile/services/AlarmServiceDriver.ios.js
|
||||
packages/app-mobile/services/AlarmServiceDriver.ios.js.map
|
||||
packages/app-mobile/setUpQuickActions.d.ts
|
||||
packages/app-mobile/setUpQuickActions.js
|
||||
packages/app-mobile/setUpQuickActions.js.map
|
||||
packages/app-mobile/setupQuickActions.d.ts
|
||||
packages/app-mobile/setupQuickActions.js
|
||||
packages/app-mobile/setupQuickActions.js.map
|
||||
packages/app-mobile/utils/ShareExtension.d.ts
|
||||
packages/app-mobile/utils/ShareExtension.js
|
||||
packages/app-mobile/utils/ShareExtension.js.map
|
||||
@@ -786,6 +708,9 @@ packages/app-mobile/utils/checkPermissions.js.map
|
||||
packages/app-mobile/utils/fs-driver-rn.d.ts
|
||||
packages/app-mobile/utils/fs-driver-rn.js
|
||||
packages/app-mobile/utils/fs-driver-rn.js.map
|
||||
packages/app-mobile/utils/setupNotifications.d.ts
|
||||
packages/app-mobile/utils/setupNotifications.js
|
||||
packages/app-mobile/utils/setupNotifications.js.map
|
||||
packages/app-mobile/utils/shareHandler.d.ts
|
||||
packages/app-mobile/utils/shareHandler.js
|
||||
packages/app-mobile/utils/shareHandler.js.map
|
||||
@@ -861,6 +786,9 @@ packages/lib/HtmlToMd.js.map
|
||||
packages/lib/InMemoryCache.d.ts
|
||||
packages/lib/InMemoryCache.js
|
||||
packages/lib/InMemoryCache.js.map
|
||||
packages/lib/InMemoryCache.test.d.ts
|
||||
packages/lib/InMemoryCache.test.js
|
||||
packages/lib/InMemoryCache.test.js.map
|
||||
packages/lib/JoplinDatabase.d.ts
|
||||
packages/lib/JoplinDatabase.js
|
||||
packages/lib/JoplinDatabase.js.map
|
||||
@@ -912,6 +840,9 @@ packages/lib/eventManager.js.map
|
||||
packages/lib/file-api-driver-joplinServer.d.ts
|
||||
packages/lib/file-api-driver-joplinServer.js
|
||||
packages/lib/file-api-driver-joplinServer.js.map
|
||||
packages/lib/file-api-driver.test.d.ts
|
||||
packages/lib/file-api-driver.test.js
|
||||
packages/lib/file-api-driver.test.js.map
|
||||
packages/lib/file-api.d.ts
|
||||
packages/lib/file-api.js
|
||||
packages/lib/file-api.js.map
|
||||
@@ -921,12 +852,24 @@ packages/lib/fs-driver-base.js.map
|
||||
packages/lib/fs-driver-node.d.ts
|
||||
packages/lib/fs-driver-node.js
|
||||
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/htmlUtils.d.ts
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils.js.map
|
||||
packages/lib/htmlUtils.test.d.ts
|
||||
packages/lib/htmlUtils.test.js
|
||||
packages/lib/htmlUtils.test.js.map
|
||||
packages/lib/htmlUtils2.test.d.ts
|
||||
packages/lib/htmlUtils2.test.js
|
||||
packages/lib/htmlUtils2.test.js.map
|
||||
packages/lib/import-enex-md-gen.d.ts
|
||||
packages/lib/import-enex-md-gen.js
|
||||
packages/lib/import-enex-md-gen.js.map
|
||||
packages/lib/import-enex-md-gen.test.d.ts
|
||||
packages/lib/import-enex-md-gen.test.js
|
||||
packages/lib/import-enex-md-gen.test.js.map
|
||||
packages/lib/locale.d.ts
|
||||
packages/lib/locale.js
|
||||
packages/lib/locale.js.map
|
||||
@@ -936,6 +879,9 @@ packages/lib/markdownUtils.js.map
|
||||
packages/lib/markdownUtils.test.d.ts
|
||||
packages/lib/markdownUtils.test.js
|
||||
packages/lib/markdownUtils.test.js.map
|
||||
packages/lib/markdownUtils2.test.d.ts
|
||||
packages/lib/markdownUtils2.test.js
|
||||
packages/lib/markdownUtils2.test.js.map
|
||||
packages/lib/markupLanguageUtils.d.ts
|
||||
packages/lib/markupLanguageUtils.js
|
||||
packages/lib/markupLanguageUtils.js.map
|
||||
@@ -948,6 +894,12 @@ packages/lib/models/BaseItem.js.map
|
||||
packages/lib/models/Folder.d.ts
|
||||
packages/lib/models/Folder.js
|
||||
packages/lib/models/Folder.js.map
|
||||
packages/lib/models/Folder.sharing.test.d.ts
|
||||
packages/lib/models/Folder.sharing.test.js
|
||||
packages/lib/models/Folder.sharing.test.js.map
|
||||
packages/lib/models/Folder.test.d.ts
|
||||
packages/lib/models/Folder.test.js
|
||||
packages/lib/models/Folder.test.js.map
|
||||
packages/lib/models/ItemChange.d.ts
|
||||
packages/lib/models/ItemChange.js
|
||||
packages/lib/models/ItemChange.js.map
|
||||
@@ -960,6 +912,9 @@ packages/lib/models/Migration.js.map
|
||||
packages/lib/models/Note.d.ts
|
||||
packages/lib/models/Note.js
|
||||
packages/lib/models/Note.js.map
|
||||
packages/lib/models/Note.test.d.ts
|
||||
packages/lib/models/Note.test.js
|
||||
packages/lib/models/Note.test.js.map
|
||||
packages/lib/models/NoteResource.d.ts
|
||||
packages/lib/models/NoteResource.js
|
||||
packages/lib/models/NoteResource.js.map
|
||||
@@ -981,12 +936,18 @@ packages/lib/models/Search.js.map
|
||||
packages/lib/models/Setting.d.ts
|
||||
packages/lib/models/Setting.js
|
||||
packages/lib/models/Setting.js.map
|
||||
packages/lib/models/Setting.test.d.ts
|
||||
packages/lib/models/Setting.test.js
|
||||
packages/lib/models/Setting.test.js.map
|
||||
packages/lib/models/SmartFilter.d.ts
|
||||
packages/lib/models/SmartFilter.js
|
||||
packages/lib/models/SmartFilter.js.map
|
||||
packages/lib/models/Tag.d.ts
|
||||
packages/lib/models/Tag.js
|
||||
packages/lib/models/Tag.js.map
|
||||
packages/lib/models/dateTimeFormats.test.d.ts
|
||||
packages/lib/models/dateTimeFormats.test.js
|
||||
packages/lib/models/dateTimeFormats.test.js.map
|
||||
packages/lib/models/settings/FileHandler.d.ts
|
||||
packages/lib/models/settings/FileHandler.js
|
||||
packages/lib/models/settings/FileHandler.js.map
|
||||
@@ -1014,6 +975,9 @@ packages/lib/reducer.js.map
|
||||
packages/lib/registry.d.ts
|
||||
packages/lib/registry.js
|
||||
packages/lib/registry.js.map
|
||||
packages/lib/registry.test.d.ts
|
||||
packages/lib/registry.test.js
|
||||
packages/lib/registry.test.js.map
|
||||
packages/lib/services/AlarmService.d.ts
|
||||
packages/lib/services/AlarmService.js
|
||||
packages/lib/services/AlarmService.js.map
|
||||
@@ -1026,6 +990,9 @@ packages/lib/services/BaseService.js.map
|
||||
packages/lib/services/CommandService.d.ts
|
||||
packages/lib/services/CommandService.js
|
||||
packages/lib/services/CommandService.js.map
|
||||
packages/lib/services/CommandService.test.d.ts
|
||||
packages/lib/services/CommandService.test.js
|
||||
packages/lib/services/CommandService.test.js.map
|
||||
packages/lib/services/DecryptionWorker.d.ts
|
||||
packages/lib/services/DecryptionWorker.js
|
||||
packages/lib/services/DecryptionWorker.js.map
|
||||
@@ -1074,6 +1041,9 @@ packages/lib/services/ResourceFetcher.js.map
|
||||
packages/lib/services/ResourceService.d.ts
|
||||
packages/lib/services/ResourceService.js
|
||||
packages/lib/services/ResourceService.js.map
|
||||
packages/lib/services/ResourceService.test.d.ts
|
||||
packages/lib/services/ResourceService.test.js
|
||||
packages/lib/services/ResourceService.test.js.map
|
||||
packages/lib/services/RevisionService.d.ts
|
||||
packages/lib/services/RevisionService.js
|
||||
packages/lib/services/RevisionService.js.map
|
||||
@@ -1086,6 +1056,9 @@ packages/lib/services/UndoRedoService.js.map
|
||||
packages/lib/services/WhenClause.d.ts
|
||||
packages/lib/services/WhenClause.js
|
||||
packages/lib/services/WhenClause.js.map
|
||||
packages/lib/services/WhenClause.test.d.ts
|
||||
packages/lib/services/WhenClause.test.js
|
||||
packages/lib/services/WhenClause.test.js.map
|
||||
packages/lib/services/commands/MenuUtils.d.ts
|
||||
packages/lib/services/commands/MenuUtils.js
|
||||
packages/lib/services/commands/MenuUtils.js.map
|
||||
@@ -1119,6 +1092,9 @@ packages/lib/services/debug/populateDatabase.js.map
|
||||
packages/lib/services/interop/InteropService.d.ts
|
||||
packages/lib/services/interop/InteropService.js
|
||||
packages/lib/services/interop/InteropService.js.map
|
||||
packages/lib/services/interop/InteropService.test.d.ts
|
||||
packages/lib/services/interop/InteropService.test.js
|
||||
packages/lib/services/interop/InteropService.test.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.js.map
|
||||
@@ -1128,6 +1104,9 @@ packages/lib/services/interop/InteropService_Exporter_Custom.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.js.map
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.d.ts
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.js.map
|
||||
@@ -1290,6 +1269,9 @@ packages/lib/services/plugins/utils/validatePluginId.test.js.map
|
||||
packages/lib/services/rest/Api.d.ts
|
||||
packages/lib/services/rest/Api.js
|
||||
packages/lib/services/rest/Api.js.map
|
||||
packages/lib/services/rest/Api.test.d.ts
|
||||
packages/lib/services/rest/Api.test.js
|
||||
packages/lib/services/rest/Api.test.js.map
|
||||
packages/lib/services/rest/ApiResponse.d.ts
|
||||
packages/lib/services/rest/ApiResponse.js
|
||||
packages/lib/services/rest/ApiResponse.js.map
|
||||
@@ -1350,6 +1332,9 @@ packages/lib/services/searchengine/SearchEngine.js.map
|
||||
packages/lib/services/searchengine/SearchEngineUtils.d.ts
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js.map
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.d.ts
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.js.map
|
||||
packages/lib/services/searchengine/filterParser.d.ts
|
||||
packages/lib/services/searchengine/filterParser.js
|
||||
packages/lib/services/searchengine/filterParser.js.map
|
||||
@@ -1374,6 +1359,30 @@ packages/lib/services/synchronizer/LockHandler.js.map
|
||||
packages/lib/services/synchronizer/MigrationHandler.d.ts
|
||||
packages/lib/services/synchronizer/MigrationHandler.js
|
||||
packages/lib/services/synchronizer/MigrationHandler.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.conflicts.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.e2ee.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.resources.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.resources.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.resources.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.revisions.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.revisions.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.revisions.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.sharing.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.sharing.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.sharing.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.js.map
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.d.ts
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.js.map
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.d.ts
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js.map
|
||||
@@ -1383,6 +1392,12 @@ packages/lib/services/synchronizer/migrations/1.js.map
|
||||
packages/lib/services/synchronizer/migrations/2.d.ts
|
||||
packages/lib/services/synchronizer/migrations/2.js
|
||||
packages/lib/services/synchronizer/migrations/2.js.map
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.d.ts
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js.map
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.d.ts
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js.map
|
||||
packages/lib/services/synchronizer/tools.d.ts
|
||||
packages/lib/services/synchronizer/tools.js
|
||||
packages/lib/services/synchronizer/tools.js.map
|
||||
@@ -1395,6 +1410,12 @@ packages/lib/services/synchronizer/utils/types.js.map
|
||||
packages/lib/shim.d.ts
|
||||
packages/lib/shim.js
|
||||
packages/lib/shim.js.map
|
||||
packages/lib/testing/test-utils-synchronizer.d.ts
|
||||
packages/lib/testing/test-utils-synchronizer.js
|
||||
packages/lib/testing/test-utils-synchronizer.js.map
|
||||
packages/lib/testing/test-utils.d.ts
|
||||
packages/lib/testing/test-utils.js
|
||||
packages/lib/testing/test-utils.js.map
|
||||
packages/lib/theme.d.ts
|
||||
packages/lib/theme.js
|
||||
packages/lib/theme.js.map
|
||||
@@ -1428,6 +1449,9 @@ packages/lib/themes/type.js.map
|
||||
packages/lib/time.d.ts
|
||||
packages/lib/time.js
|
||||
packages/lib/time.js.map
|
||||
packages/lib/utils/credentialFiles.d.ts
|
||||
packages/lib/utils/credentialFiles.js
|
||||
packages/lib/utils/credentialFiles.js.map
|
||||
packages/lib/uuid.d.ts
|
||||
packages/lib/uuid.js
|
||||
packages/lib/uuid.js.map
|
||||
@@ -1554,6 +1578,9 @@ packages/tools/generate-database-types.js.map
|
||||
packages/tools/lerna-add.d.ts
|
||||
packages/tools/lerna-add.js
|
||||
packages/tools/lerna-add.js.map
|
||||
packages/tools/release-android.d.ts
|
||||
packages/tools/release-android.js
|
||||
packages/tools/release-android.js.map
|
||||
packages/tools/release-cli.d.ts
|
||||
packages/tools/release-cli.js
|
||||
packages/tools/release-cli.js.map
|
||||
|
||||
80
README.md
80
README.md
@@ -292,7 +292,7 @@ To add a **Bucket Policy** from the AWS S3 Web Console, navigate to the **Permis
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteObjectVersion",
|
||||
"s3:PutObject"
|
||||
]
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::joplin-bucket",
|
||||
"arn:aws:s3:::joplin-bucket/*"
|
||||
@@ -342,7 +342,7 @@ In the desktop and mobile apps, an alarm can be associated with any to-do. It wi
|
||||
|
||||
- **Windows**: >= 8. Make sure the Action Center is enabled on Windows. Task bar balloon for Windows < 8. Growl as fallback. Growl takes precedence over Windows balloons.
|
||||
- **macOS**: >= 10.8 or Growl if earlier.
|
||||
- **Linux**: `notify-osd` or `libnotify-bin` installed (Ubuntu should have this by default). Growl otherwise
|
||||
- **Linux**: `notify-send` tool, delivered through packages `notify-osd`, `libnotify-bin` or `libnotify-tools`. GNOME should have this by default, but install `libnotify-tools` if using KDE Plasma.
|
||||
|
||||
See [documentation and flow chart for reporter choice](https://github.com/mikaelbr/node-notifier/blob/master/DECISION_FLOW.md)
|
||||
|
||||
@@ -511,47 +511,47 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
 | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 98%
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 31%
|
||||
 | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 76%
|
||||
 | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 59%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | jmontane, 2019 | 84%
|
||||
 | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 99%
|
||||
 | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Lukas Helebrandt](mailto:lukas@aiya.cz) | 88%
|
||||
 | 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: | 98%
|
||||
 | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Atalanttore](mailto:atalanttore@googlemail.com) | 98%
|
||||
 | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 58%
|
||||
 | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 96%
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 30%
|
||||
 | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 75%
|
||||
 | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 58%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | jmontane, 2019 | 83%
|
||||
 | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 96%
|
||||
 | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Lukas Helebrandt](mailto:lukas@aiya.cz) | 86%
|
||||
 | 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%
|
||||
 | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Atalanttore](mailto:atalanttore@googlemail.com) | 95%
|
||||
 | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 57%
|
||||
 | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
|
||||
 | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
|
||||
 | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Mario Campo](mailto:mario.campo@gmail.com) | 97%
|
||||
 | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Mario Campo](mailto:mario.campo@gmail.com) | 94%
|
||||
 | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 33%
|
||||
 | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 97%
|
||||
 | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 94%
|
||||
 | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 39%
|
||||
 | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [eresytter](mailto:42007357+eresytter@users.noreply.github.com) | 95%
|
||||
 | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Alessandro Bernardello](mailto:mailfilledwithspam@gmail.com) | 97%
|
||||
 | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Szőke Sándor](mailto:mail@szokesandor.hu) | 90%
|
||||
 | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 94%
|
||||
 | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 97%
|
||||
 | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 78%
|
||||
 | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 73%
|
||||
 | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 97%
|
||||
 | 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) | 97%
|
||||
 | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 97%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 68%
|
||||
 | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 98%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 63%
|
||||
 | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 46%
|
||||
 | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 75%
|
||||
 | 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) | 97%
|
||||
 | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 97%
|
||||
 | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 84%
|
||||
 | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 97%
|
||||
 | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 73%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [Yang Zhang](mailto:zyangmath@gmail.com) | 97%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Yaoze Ye](mailto:yaozeye@yahoo.co.jp) | 95%
|
||||
 | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 98%
|
||||
 | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 99%
|
||||
 | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 94%
|
||||
 | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 99%
|
||||
 | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 38%
|
||||
 | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [eresytter](mailto:42007357+eresytter@users.noreply.github.com) | 93%
|
||||
 | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Alessandro Bernardello](mailto:mailfilledwithspam@gmail.com) | 94%
|
||||
 | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Szőke Sándor](mailto:mail@szokesandor.hu) | 88%
|
||||
 | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 92%
|
||||
 | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 95%
|
||||
 | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 76%
|
||||
 | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 71%
|
||||
 | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 94%
|
||||
 | 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) | 94%
|
||||
 | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 94%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 66%
|
||||
 | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 96%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 61%
|
||||
 | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 45%
|
||||
 | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 73%
|
||||
 | 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) | 94%
|
||||
 | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 94%
|
||||
 | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 97%
|
||||
 | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 94%
|
||||
 | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 71%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [Yang Zhang](mailto:zyangmath@gmail.com) | 94%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Yaoze Ye](mailto:yaozeye@yahoo.co.jp) | 92%
|
||||
 | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 97%
|
||||
 | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 96%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Contributors
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"audit": "lerna-audit",
|
||||
"bootstrap": "lerna bootstrap --no-ci",
|
||||
"bootstrapIgnoreScripts": "lerna bootstrap --ignore-scripts --no-ci",
|
||||
"bootstrapServerOnly": "lerna bootstrap --no-ci --include-dependents --include-dependencies --scope @joplin/server",
|
||||
"bootstrap": "lerna bootstrap --force-local --no-ci",
|
||||
"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",
|
||||
"buildDoc": "./packages/tools/build-all.sh",
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
const { afterEachCleanUp } = require('./tests/test-utils.js');
|
||||
const { afterEachCleanUp } = require('@joplin/lib/testing/test-utils.js');
|
||||
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const sharp = require('sharp');
|
||||
|
||||
let keytar;
|
||||
try {
|
||||
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
|
||||
} catch (error) {
|
||||
console.error('Cannot load keytar - keychain support will be disabled', error);
|
||||
keytar = null;
|
||||
}
|
||||
|
||||
shimInit(sharp, keytar);
|
||||
|
||||
global.afterEach(async () => {
|
||||
await afterEachCleanUp();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"author": "Laurent Cozic",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "jest --config=jest.config.js --bail --forceExit",
|
||||
"test": "jest --verbose=false --config=jest.config.js --bail --forceExit",
|
||||
"test-one": "jest --verbose=false --config=jest.config.js --bail --forceExit",
|
||||
"test-ci": "jest --config=jest.config.js --forceExit",
|
||||
"build": "gulp build",
|
||||
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.8.1",
|
||||
"version": "2.0.0",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
@@ -43,7 +43,6 @@
|
||||
"@joplin/renderer": "1.8",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"chalk": "^4.1.0",
|
||||
"clean-html": "^1.5.0",
|
||||
"compare-version": "^0.1.2",
|
||||
"fs-extra": "^5.0.0",
|
||||
"html-entities": "^1.2.1",
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const { enexXmlToHtml } = require('@joplin/lib/import-enex-html-gen.js');
|
||||
const cleanHtml = require('clean-html');
|
||||
|
||||
const fileWithPath = (filename) =>
|
||||
`${__dirname}/enex_to_html/${filename}`;
|
||||
|
||||
const audioResource = {
|
||||
filename: 'audio test',
|
||||
id: '9168ee833d03c5ea7c730ac6673978c1',
|
||||
mime: 'audio/x-m4a',
|
||||
size: 82011,
|
||||
title: 'audio test',
|
||||
};
|
||||
|
||||
// All the test HTML files are beautified ones, so we need to run
|
||||
// this before the comparison. Before, beautifying was done by `enexXmlToHtml`
|
||||
// but that was removed due to problems with the clean-html package.
|
||||
const beautifyHtml = (html) => {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
cleanHtml.clean(html, { wrap: 0 }, (...cleanedHtml) => resolve(cleanedHtml.join('')));
|
||||
} catch (error) {
|
||||
console.warn(`Could not clean HTML - the "unclean" version will be used: ${error.message}: ${html.trim().substr(0, 512).replace(/[\n\r]/g, ' ')}...`);
|
||||
resolve([html].join(''));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the importer for a single note, checking that the result of
|
||||
* processing the given `.enex` input file matches the contents of the given
|
||||
* `.html` file.
|
||||
*
|
||||
* Note that this does not test the importing of an entire exported `.enex`
|
||||
* archive, but rather a single node of such a file. Thus, the test data files
|
||||
* (e.g. `./enex_to_html/code1.enex`) correspond to the contents of a single
|
||||
* `<note>...</note>` node in an `.enex` file already extracted from
|
||||
* `<content><![CDATA[...]]</content>`.
|
||||
*/
|
||||
const compareOutputToExpected = (options) => {
|
||||
const inputFile = fileWithPath(`${options.testName}.enex`);
|
||||
const outputFile = fileWithPath(`${options.testName}.html`);
|
||||
const testTitle = `should convert from Enex to Html: ${options.testName}`;
|
||||
|
||||
it(testTitle, (async () => {
|
||||
const enexInput = await shim.fsDriver().readFile(inputFile);
|
||||
const expectedOutput = await shim.fsDriver().readFile(outputFile);
|
||||
const actualOutput = await beautifyHtml(await enexXmlToHtml(enexInput, options.resources));
|
||||
|
||||
expect(actualOutput).toEqual(expectedOutput);
|
||||
}));
|
||||
};
|
||||
|
||||
describe('EnexToHtml', function() {
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
compareOutputToExpected({
|
||||
testName: 'checklist-list',
|
||||
resources: [],
|
||||
});
|
||||
|
||||
compareOutputToExpected({
|
||||
testName: 'svg',
|
||||
resources: [],
|
||||
});
|
||||
|
||||
compareOutputToExpected({
|
||||
testName: 'en-media--image',
|
||||
resources: [{
|
||||
filename: '',
|
||||
id: '89ce7da62c6b2832929a6964237e98e9', // Mock id
|
||||
mime: 'image/jpeg',
|
||||
size: 50347,
|
||||
title: '',
|
||||
}],
|
||||
});
|
||||
|
||||
compareOutputToExpected({
|
||||
testName: 'en-media--audio',
|
||||
resources: [audioResource],
|
||||
});
|
||||
|
||||
compareOutputToExpected({
|
||||
testName: 'attachment',
|
||||
resources: [{
|
||||
filename: 'attachment-1',
|
||||
id: '21ca2b948f222a38802940ec7e2e5de3',
|
||||
mime: 'application/pdf', // Any non-image/non-audio mime type will do
|
||||
size: 1000,
|
||||
}],
|
||||
});
|
||||
|
||||
// it('fails when not given a matching resource', (async () => {
|
||||
// // To test the promise-unexpectedly-resolved case, add `audioResource` to the array.
|
||||
// const resources = [];
|
||||
// const inputFile = fileWithPath('en-media--image.enex');
|
||||
// const enexInput = await shim.fsDriver().readFile(inputFile);
|
||||
// const promisedOutput = enexXmlToHtml(enexInput, resources);
|
||||
|
||||
// promisedOutput.then(() => {
|
||||
// // Promise should not be resolved
|
||||
// expect(false).toEqual(true);
|
||||
// }, (reason) => {
|
||||
// expect(reason)
|
||||
// .toBe('Hash with no associated resource: 89ce7da62c6b2832929a6964237e98e9');
|
||||
// });
|
||||
// }));
|
||||
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
const os = require('os');
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const { filename } = require('@joplin/lib/path-utils');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('@joplin/lib/testing/test-utils.js');
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import MdToHtml from '@joplin/renderer/MdToHtml';
|
||||
const os = require('os');
|
||||
const { filename } = require('@joplin/lib/path-utils');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
|
||||
import shim from '@joplin/lib/shim';
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
|
||||
@@ -137,6 +137,14 @@ describe('MdToHtml', function() {
|
||||
}
|
||||
}));
|
||||
|
||||
it('should render an empty string', (async () => {
|
||||
const mdToHtml = newTestMdToHtml();
|
||||
const result = await mdToHtml.render('', null, { splitted: true });
|
||||
// The TinyMCE component checks for this exact string to apply a hack,
|
||||
// so make sure it doesn't change from version to version.
|
||||
expect(result.html).toBe('<div id="rendered-md"></div>');
|
||||
}));
|
||||
|
||||
it('should split HTML and CSS', (async () => {
|
||||
const mdToHtml = newTestMdToHtml();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const mdImporterService = require('@joplin/lib/services/interop/InteropService_Importer_Md').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
|
||||
|
||||
const importer = new mdImporterService();
|
||||
|
||||
@@ -43,4 +43,9 @@ describe('InteropService_Importer_Md: importLocalImages', function() {
|
||||
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,290 +0,0 @@
|
||||
'use strict';
|
||||
const __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function(resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator['throw'](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const Setting_1 = require('@joplin/lib/models/Setting');
|
||||
const test_utils_synchronizer_1 = require('./test-utils-synchronizer');
|
||||
const { syncTargetName, afterAllCleanUp, synchronizerStart, setupDatabaseAndSynchronizer, synchronizer, sleep, switchClient, syncTargetId, fileApi } = require('./test-utils.js');
|
||||
const Folder_1 = require('@joplin/lib/models/Folder');
|
||||
const Note_1 = require('@joplin/lib/models/Note');
|
||||
const BaseItem_1 = require('@joplin/lib/models/BaseItem');
|
||||
const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
|
||||
describe('Synchronizer.basics', function() {
|
||||
beforeEach((done) => __awaiter(this, void 0, void 0, function* () {
|
||||
yield setupDatabaseAndSynchronizer(1);
|
||||
yield setupDatabaseAndSynchronizer(2);
|
||||
yield switchClient(1);
|
||||
done();
|
||||
}));
|
||||
afterAll(() => __awaiter(this, void 0, void 0, function* () {
|
||||
yield afterAllCleanUp();
|
||||
}));
|
||||
it('should create remote items', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder = yield Folder_1.default.save({ title: 'folder1' });
|
||||
yield Note_1.default.save({ title: 'un', parent_id: folder.id });
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield synchronizerStart();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
|
||||
})));
|
||||
it('should update remote items', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const note = yield Note_1.default.save({ title: 'un', parent_id: folder.id });
|
||||
yield synchronizerStart();
|
||||
yield Note_1.default.save({ title: 'un UPDATE', id: note.id });
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield synchronizerStart();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
|
||||
})));
|
||||
it('should create local items', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder = yield Folder_1.default.save({ title: 'folder1' });
|
||||
yield Note_1.default.save({ title: 'un', parent_id: folder.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
|
||||
})));
|
||||
it('should update local items', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield sleep(0.1);
|
||||
let note2 = yield Note_1.default.load(note1.id);
|
||||
note2.title = 'Updated on client 2';
|
||||
yield Note_1.default.save(note2);
|
||||
note2 = yield Note_1.default.load(note2.id);
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield synchronizerStart();
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
|
||||
})));
|
||||
it('should delete remote notes', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield sleep(0.1);
|
||||
yield Note_1.default.delete(note1.id);
|
||||
yield synchronizerStart();
|
||||
const remotes = yield test_utils_synchronizer_1.remoteNotesAndFolders();
|
||||
expect(remotes.length).toBe(1);
|
||||
expect(remotes[0].id).toBe(folder1.id);
|
||||
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
|
||||
expect(deletedItems.length).toBe(0);
|
||||
})));
|
||||
it('should not created deleted_items entries for items deleted via sync', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield Folder_1.default.delete(folder1.id);
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield synchronizerStart();
|
||||
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
|
||||
expect(deletedItems.length).toBe(0);
|
||||
})));
|
||||
it('should delete local notes', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
|
||||
const note2 = yield Note_1.default.save({ title: 'deux', parent_id: folder1.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield Note_1.default.delete(note1.id);
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield synchronizerStart();
|
||||
const items = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
expect(items.length).toBe(2);
|
||||
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
|
||||
expect(deletedItems.length).toBe(0);
|
||||
yield Note_1.default.delete(note2.id);
|
||||
yield synchronizerStart();
|
||||
})));
|
||||
it('should delete remote folder', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
yield Folder_1.default.save({ title: 'folder1' });
|
||||
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield sleep(0.1);
|
||||
yield Folder_1.default.delete(folder2.id);
|
||||
yield synchronizerStart();
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
|
||||
})));
|
||||
it('should delete local folder', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
yield Folder_1.default.save({ title: 'folder1' });
|
||||
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield Folder_1.default.delete(folder2.id);
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield synchronizerStart();
|
||||
const items = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(items, expect);
|
||||
})));
|
||||
it('should cross delete all folders', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
// If client1 and 2 have two folders, client 1 deletes item 1 and client
|
||||
// 2 deletes item 2, they should both end up with no items after sync.
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield sleep(0.1);
|
||||
yield Folder_1.default.delete(folder1.id);
|
||||
yield switchClient(1);
|
||||
yield Folder_1.default.delete(folder2.id);
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
const items2 = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield switchClient(1);
|
||||
yield synchronizerStart();
|
||||
const items1 = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
expect(items1.length).toBe(0);
|
||||
expect(items1.length).toBe(items2.length);
|
||||
})));
|
||||
it('items should be downloaded again when user cancels in the middle of delta operation', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
yield Note_1.default.save({ title: 'un', is_todo: 1, parent_id: folder1.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
synchronizer().testingHooks_ = ['cancelDeltaLoop2'];
|
||||
yield synchronizerStart();
|
||||
let notes = yield Note_1.default.all();
|
||||
expect(notes.length).toBe(0);
|
||||
synchronizer().testingHooks_ = [];
|
||||
yield synchronizerStart();
|
||||
notes = yield Note_1.default.all();
|
||||
expect(notes.length).toBe(1);
|
||||
})));
|
||||
it('should skip items that cannot be synced', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const note1 = yield Note_1.default.save({ title: 'un', is_todo: 1, parent_id: folder1.id });
|
||||
const noteId = note1.id;
|
||||
yield synchronizerStart();
|
||||
let disabledItems = yield BaseItem_1.default.syncDisabledItems(syncTargetId());
|
||||
expect(disabledItems.length).toBe(0);
|
||||
yield Note_1.default.save({ id: noteId, title: 'un mod' });
|
||||
synchronizer().testingHooks_ = ['notesRejectedByTarget'];
|
||||
yield synchronizerStart();
|
||||
synchronizer().testingHooks_ = [];
|
||||
yield synchronizerStart(); // Another sync to check that this item is now excluded from sync
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
const notes = yield Note_1.default.all();
|
||||
expect(notes.length).toBe(1);
|
||||
expect(notes[0].title).toBe('un');
|
||||
yield switchClient(1);
|
||||
disabledItems = yield BaseItem_1.default.syncDisabledItems(syncTargetId());
|
||||
expect(disabledItems.length).toBe(1);
|
||||
})));
|
||||
it('should allow duplicate folder titles', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
yield Folder_1.default.save({ title: 'folder' });
|
||||
yield switchClient(2);
|
||||
let remoteF2 = yield Folder_1.default.save({ title: 'folder' });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield sleep(0.1);
|
||||
yield synchronizerStart();
|
||||
const localF2 = yield Folder_1.default.load(remoteF2.id);
|
||||
expect(localF2.title == remoteF2.title).toBe(true);
|
||||
// Then that folder that has been renamed locally should be set in such a way
|
||||
// that synchronizing it applies the title change remotely, and that new title
|
||||
// should be retrieved by client 2.
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield sleep(0.1);
|
||||
yield synchronizerStart();
|
||||
remoteF2 = yield Folder_1.default.load(remoteF2.id);
|
||||
expect(remoteF2.title == localF2.title).toBe(true);
|
||||
})));
|
||||
it('should create remote items with UTF-8 content', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder = yield Folder_1.default.save({ title: 'Fahrräder' });
|
||||
yield Note_1.default.save({ title: 'Fahrräder', body: 'Fahrräder', parent_id: folder.id });
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
yield synchronizerStart();
|
||||
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
|
||||
})));
|
||||
it('should update remote items but not pull remote changes', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
const folder = yield Folder_1.default.save({ title: 'folder1' });
|
||||
const note = yield Note_1.default.save({ title: 'un', parent_id: folder.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
yield Note_1.default.save({ title: 'deux', parent_id: folder.id });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield Note_1.default.save({ title: 'un UPDATE', id: note.id });
|
||||
yield synchronizerStart(null, { syncSteps: ['update_remote'] });
|
||||
const all = yield test_utils_synchronizer_1.allNotesFolders();
|
||||
expect(all.length).toBe(2);
|
||||
yield switchClient(2);
|
||||
yield synchronizerStart();
|
||||
const note2 = yield Note_1.default.load(note.id);
|
||||
expect(note2.title).toBe('un UPDATE');
|
||||
})));
|
||||
it('should create a new Welcome notebook on each client', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
// Create the Welcome items on two separate clients
|
||||
yield WelcomeUtils.createWelcomeItems();
|
||||
yield synchronizerStart();
|
||||
yield switchClient(2);
|
||||
yield WelcomeUtils.createWelcomeItems();
|
||||
const beforeFolderCount = (yield Folder_1.default.all()).length;
|
||||
const beforeNoteCount = (yield Note_1.default.all()).length;
|
||||
expect(beforeFolderCount === 1).toBe(true);
|
||||
expect(beforeNoteCount > 1).toBe(true);
|
||||
yield synchronizerStart();
|
||||
const afterFolderCount = (yield Folder_1.default.all()).length;
|
||||
const afterNoteCount = (yield Note_1.default.all()).length;
|
||||
expect(afterFolderCount).toBe(beforeFolderCount * 2);
|
||||
expect(afterNoteCount).toBe(beforeNoteCount * 2);
|
||||
// Changes to the Welcome items should be synced to all clients
|
||||
const f1 = (yield Folder_1.default.all())[0];
|
||||
yield Folder_1.default.save({ id: f1.id, title: 'Welcome MOD' });
|
||||
yield synchronizerStart();
|
||||
yield switchClient(1);
|
||||
yield synchronizerStart();
|
||||
const f1_1 = yield Folder_1.default.load(f1.id);
|
||||
expect(f1_1.title).toBe('Welcome MOD');
|
||||
})));
|
||||
it('should not wipe out user data when syncing with an empty target', (() => __awaiter(this, void 0, void 0, function* () {
|
||||
// Only these targets support the wipeOutFailSafe flag (in other words, the targets that use basicDelta)
|
||||
if (!['nextcloud', 'memory', 'filesystem', 'amazon_s3'].includes(syncTargetName())) { return; }
|
||||
for (let i = 0; i < 10; i++) { yield Note_1.default.save({ title: 'note' }); }
|
||||
Setting_1.default.setValue('sync.wipeOutFailSafe', true);
|
||||
yield synchronizerStart();
|
||||
yield fileApi().clearRoot(); // oops
|
||||
yield synchronizerStart();
|
||||
expect((yield Note_1.default.all()).length).toBe(10); // but since the fail-safe if on, the notes have not been deleted
|
||||
Setting_1.default.setValue('sync.wipeOutFailSafe', false); // Now switch it off
|
||||
yield synchronizerStart();
|
||||
expect((yield Note_1.default.all()).length).toBe(0); // Since the fail-safe was off, the data has been cleared
|
||||
// Handle case where the sync target has been wiped out, then the user creates one note and sync.
|
||||
for (let i = 0; i < 10; i++) { yield Note_1.default.save({ title: 'note' }); }
|
||||
Setting_1.default.setValue('sync.wipeOutFailSafe', true);
|
||||
yield synchronizerStart();
|
||||
yield fileApi().clearRoot();
|
||||
yield Note_1.default.save({ title: 'ma note encore' });
|
||||
yield synchronizerStart();
|
||||
expect((yield Note_1.default.all()).length).toBe(11);
|
||||
})));
|
||||
});
|
||||
// # sourceMappingURL=Synchronizer.share.js.map
|
||||
@@ -1,31 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const { sortedIds, createNTestNotes, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
|
||||
describe('database', function() {
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not modify cached field names', (async () => {
|
||||
const db = BaseModel.db();
|
||||
|
||||
const fieldNames = db.tableFieldNames('notes');
|
||||
const fieldCount = fieldNames.length;
|
||||
fieldNames.push('type_');
|
||||
|
||||
expect(fieldCount).toBeGreaterThan(0);
|
||||
expect(db.tableFieldNames('notes').length).toBe(fieldCount);
|
||||
}));
|
||||
|
||||
});
|
||||
@@ -1,14 +1,16 @@
|
||||
For example, consider a web page like this:
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="page-scripts/page-script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<body>
|
||||
<script src="page-scripts/page-script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The script "page-script.js" does this:
|
||||
@@ -1,7 +1,9 @@
|
||||
Subshell:
|
||||
|
||||
(
|
||||
set -e
|
||||
false
|
||||
echo Unreachable
|
||||
) && echo Great success
|
||||
```
|
||||
(
|
||||
set -e
|
||||
false
|
||||
echo Unreachable
|
||||
) && echo Great success
|
||||
```
|
||||
@@ -1 +1 @@
|
||||
jq -r '.[]|[.index, .name, .section, .award, .industry]|join("\t")' raw.json |pbcopy
|
||||
`jq -r '.[]|[.index, .name, .section, .award, .industry]|join("\t")' raw.json |pbcopy`
|
||||
1
packages/app-cli/tests/enex_to_md/code4.html
Normal file
1
packages/app-cli/tests/enex_to_md/code4.html
Normal file
@@ -0,0 +1 @@
|
||||
<div><div>code block:</div><div><br/></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px; color: rgb(51, 51, 51); border-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.15);-en-codeblock:true;"><div>public static void main(String[] args) {</div><div><span> System.out.println('Hello World');</span><br/></div><div>}</div></div><div><br/></div><div>end of code block</div></div>
|
||||
9
packages/app-cli/tests/enex_to_md/code4.md
Normal file
9
packages/app-cli/tests/enex_to_md/code4.md
Normal file
@@ -0,0 +1,9 @@
|
||||
code block:
|
||||
|
||||
```
|
||||
public static void main(String[] args) {
|
||||
System.out.println('Hello World');
|
||||
}
|
||||
```
|
||||
|
||||
end of code block
|
||||
@@ -1,4 +1,4 @@
|
||||
const { id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('./test-utils.js');
|
||||
const { id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('@joplin/lib/testing/test-utils.js');
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const uuid = require('@joplin/lib/uuid').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
const { setupDatabaseAndSynchronizer, switchClient, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const Note = require('@joplin/lib/models/Note').default;
|
||||
|
||||
9
packages/app-cli/tests/md_to_md/sample-files.md
Normal file
9
packages/app-cli/tests/md_to_md/sample-files.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Markdown file test
|
||||
|
||||

|
||||
|
||||
[welcome.pdf](../support/welcome.pdf)
|
||||
|
||||
[sample.md](sample.md)
|
||||
|
||||
[sample2.md](./sample.md)
|
||||
@@ -1,8 +1,7 @@
|
||||
import KeychainService from '@joplin/lib/services/keychain/KeychainService';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
const { db, setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
|
||||
import { db, setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
|
||||
function describeIfCompatible(name: string, fn: any, elseFn: any) {
|
||||
if (['win32', 'darwin'].includes(shim.platformName())) {
|
||||
@@ -1,4 +1,4 @@
|
||||
import PluginRunner from '../app/services/plugins/PluginRunner';
|
||||
import PluginRunner from '../../../app/services/plugins/PluginRunner';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import { ContentScriptType } from '@joplin/lib/services/plugins/api/types';
|
||||
import MdToHtml from '@joplin/renderer/MdToHtml';
|
||||
@@ -7,10 +7,10 @@ import Setting from '@joplin/lib/models/Setting';
|
||||
import * as fs from 'fs-extra';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import { newPluginScript } from './test-utils';
|
||||
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir } from './test-utils.js';
|
||||
import { newPluginScript } from '@joplin/lib/testing/test-utils';
|
||||
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir, supportDir } from '@joplin/lib/testing/test-utils';
|
||||
|
||||
const testPluginDir = `${__dirname}/../tests/support/plugins`;
|
||||
const testPluginDir = `${supportDir}/plugins`;
|
||||
|
||||
function newPluginService(appVersion: string = '1.4') {
|
||||
const runner = new PluginRunner();
|
||||
@@ -102,7 +102,7 @@ describe('services_PluginService', function() {
|
||||
"homepage_url": "https://joplinapp.org"
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.data.post(['folders'], null, { title: "my plugin folder" });
|
||||
@@ -141,14 +141,14 @@ describe('services_PluginService', function() {
|
||||
"not_a_valid_manifest_at_all": 1
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {},
|
||||
});
|
||||
`, `
|
||||
/* joplin-manifest:
|
||||
*/
|
||||
|
||||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {},
|
||||
});
|
||||
@@ -189,7 +189,7 @@ describe('services_PluginService', function() {
|
||||
"homepage_url": "https://joplinapp.org"
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.contentScripts.register('markdownItPlugin', 'justtesting', './markdownItTestPlugin.js');
|
||||
@@ -236,7 +236,7 @@ describe('services_PluginService', function() {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() { },
|
||||
});
|
||||
@@ -283,7 +283,7 @@ describe('services_PluginService', function() {
|
||||
}));
|
||||
|
||||
it('should create the data directory', (async () => {
|
||||
const pluginScript = newPluginScript(`
|
||||
const pluginScript = newPluginScript(`
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
const dataDir = await joplin.plugins.dataDir();
|
||||
@@ -1,6 +1,6 @@
|
||||
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '../../test-utils';
|
||||
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '@joplin/lib/testing/test-utils';
|
||||
|
||||
async function newRepoApi(): Promise<RepositoryApi> {
|
||||
const repo = new RepositoryApi(`${supportDir}/pluginRepo`, await createTempDir());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
const { waitForFolderCount, newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
|
||||
const { waitForFolderCount, newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('@joplin/lib/testing/test-utils');
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
|
||||
describe('JoplinSettings', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
|
||||
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('@joplin/lib/testing/test-utils');
|
||||
|
||||
describe('JoplinViewMenuItem', () => {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '../../../test-utils';
|
||||
import { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '@joplin/lib/testing/test-utils';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import ItemChange from '@joplin/lib/models/ItemChange';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sandboxProxy, { Target } from '@joplin/lib/services/plugins/sandboxProxy';
|
||||
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('../../test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
|
||||
|
||||
describe('services_plugins_sandboxProxy', function() {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {main} = require('./syncTargetUtils');
|
||||
const {main} = require('@joplin/lib/testing/syncTargetUtils');
|
||||
|
||||
const syncTargetType = process.argv.length <= 2 ? 'normal' : process.argv[2];
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "1.8.0",
|
||||
"version": "2.0.0",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'",
|
||||
|
||||
@@ -166,6 +166,7 @@ export default class InteropServiceHelper {
|
||||
exportOptions.format = module.format;
|
||||
// exportOptions.modulePath = module.path;
|
||||
if (options.plugins) exportOptions.plugins = options.plugins;
|
||||
exportOptions.customCss = options.customCss;
|
||||
exportOptions.target = module.target;
|
||||
exportOptions.includeConflicts = !!options.includeConflicts;
|
||||
if (options.sourceFolderIds) exportOptions.sourceFolderIds = options.sourceFolderIds;
|
||||
|
||||
@@ -13,8 +13,13 @@ import { PluginItem } from './PluginBox';
|
||||
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import useOnInstallHandler, { OnPluginSettingChangeEvent } from './useOnInstallHandler';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import StyledMessage from '../../../style/StyledMessage';
|
||||
import StyledLink from '../../../style/StyledLink';
|
||||
const { space } = require('styled-system');
|
||||
|
||||
const logger = Logger.create('PluginState');
|
||||
|
||||
const maxWidth: number = 320;
|
||||
|
||||
const Root = styled.div`
|
||||
@@ -32,6 +37,11 @@ const ToolsButton = styled(Button)`
|
||||
margin-right: 6px;
|
||||
`;
|
||||
|
||||
const RepoApiErrorMessage = styled(StyledMessage)`
|
||||
max-width: ${props => props.maxWidth}px;
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
value: any;
|
||||
themeId: number;
|
||||
@@ -84,6 +94,8 @@ export default function(props: Props) {
|
||||
const [manifestsLoaded, setManifestsLoaded] = useState<boolean>(false);
|
||||
const [updatingPluginsIds, setUpdatingPluginIds] = useState<Record<string, boolean>>({});
|
||||
const [canBeUpdatedPluginIds, setCanBeUpdatedPluginIds] = useState<Record<string, boolean>>({});
|
||||
const [repoApiError, setRepoApiError] = useState<Error>(null);
|
||||
const [fetchManifestTime, setFetchManifestTime] = useState<number>(Date.now());
|
||||
|
||||
const pluginService = PluginService.instance();
|
||||
|
||||
@@ -96,9 +108,25 @@ export default function(props: Props) {
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
async function fetchManifests() {
|
||||
await repoApi().loadManifests();
|
||||
setManifestsLoaded(false);
|
||||
setRepoApiError(null);
|
||||
|
||||
let loadError: Error = null;
|
||||
try {
|
||||
await repoApi().loadManifests();
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
loadError = error;
|
||||
}
|
||||
|
||||
if (cancelled) return;
|
||||
setManifestsLoaded(true);
|
||||
|
||||
if (loadError) {
|
||||
setManifestsLoaded(false);
|
||||
setRepoApiError(loadError);
|
||||
} else {
|
||||
setManifestsLoaded(true);
|
||||
}
|
||||
}
|
||||
|
||||
void fetchManifests();
|
||||
@@ -106,7 +134,7 @@ export default function(props: Props) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
}, [fetchManifestTime]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!manifestsLoaded) return () => {};
|
||||
@@ -252,7 +280,7 @@ export default function(props: Props) {
|
||||
|
||||
function renderSearchArea() {
|
||||
return (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<div style={{ marginBottom: 0 }}>
|
||||
<SearchPlugins
|
||||
disabled={!manifestsLoaded}
|
||||
maxWidth={maxWidth}
|
||||
@@ -268,11 +296,18 @@ 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>;
|
||||
}
|
||||
|
||||
function renderBottomArea() {
|
||||
if (searchQuery) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{renderRepoApiError()}
|
||||
<div style={{ display: 'flex', flexDirection: 'row', maxWidth }}>
|
||||
<ToolsButton tooltip={_('Plugin tools')} iconName="fas fa-cog" level={ButtonLevel.Secondary} onClick={onToolsClick}/>
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function(props: Props) {
|
||||
onChange={onChange}
|
||||
onSearchButtonClick={onSearchButtonClick}
|
||||
searchStarted={searchStarted}
|
||||
placeholder={_('Search for plugins...')}
|
||||
placeholder={props.disabled ? _('Please wait...') : _('Search for plugins...')}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -256,7 +256,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
||||
<div>
|
||||
<div style={containerStyle}>
|
||||
{
|
||||
<div style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
|
||||
<div className="alert alert-warning" style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
|
||||
<p style={theme.textStyle}>
|
||||
<span>{_('For more information about End-To-End Encryption (E2EE) and advice on how to enable it please check the documentation:')}</span>{' '}
|
||||
<a
|
||||
|
||||
@@ -34,6 +34,7 @@ import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog';
|
||||
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { PromptDialog } = require('../PromptDialog.min.js');
|
||||
@@ -234,6 +235,14 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
try {
|
||||
output = loadLayout(Object.keys(userLayout).length ? userLayout : null, defaultLayout, rootLayoutSize);
|
||||
|
||||
// For unclear reasons, layout items sometimes end up witout a key.
|
||||
// In that case, we can't do anything with them, so remove them
|
||||
// here. It could be due to the deprecated plugin API, which allowed
|
||||
// creating panel without a key, although in this case it should
|
||||
// have been set automatically.
|
||||
// https://github.com/laurent22/joplin/issues/4926
|
||||
output = removeKeylessItems(output);
|
||||
|
||||
if (!findItemByKey(output, 'sideBar') || !findItemByKey(output, 'noteList') || !findItemByKey(output, 'editor')) {
|
||||
throw new Error('"sideBar", "noteList" and "editor" must be present in the layout');
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@ export const runtime = (comp: any): CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
enabledCondition: 'folderIsShareRootAndOwnedByUser || !folderIsShared',
|
||||
enabledCondition: 'joplinServerConnected && (folderIsShareRootAndOwnedByUser || !folderIsShared)',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,5 +18,6 @@ export const runtime = (comp: any): CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
enabledCondition: 'joplinServerConnected && someNotesSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -90,6 +90,7 @@ interface Props {
|
||||
['spellChecker.enabled']: boolean;
|
||||
['spellChecker.language']: string;
|
||||
plugins: PluginStates;
|
||||
customCss: string;
|
||||
}
|
||||
|
||||
const commandNames: string[] = menuCommandNames();
|
||||
@@ -313,7 +314,10 @@ function useMenu(props: Props) {
|
||||
await InteropServiceHelper.export(
|
||||
(action: any) => props.dispatch(action),
|
||||
module,
|
||||
{ plugins: props.plugins }
|
||||
{
|
||||
plugins: props.plugins,
|
||||
customCss: props.customCss,
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -695,11 +699,18 @@ function useMenu(props: Props) {
|
||||
},
|
||||
],
|
||||
},
|
||||
folder: {
|
||||
label: _('Note&book'),
|
||||
submenu: [
|
||||
menuItemDic.showShareFolderDialog,
|
||||
],
|
||||
},
|
||||
note: {
|
||||
label: _('&Note'),
|
||||
submenu: [
|
||||
menuItemDic.toggleExternalEditing,
|
||||
menuItemDic.setTags,
|
||||
menuItemDic.showShareNoteDialog,
|
||||
separator(),
|
||||
menuItemDic.showNoteContentProperties,
|
||||
],
|
||||
@@ -818,6 +829,7 @@ function useMenu(props: Props) {
|
||||
rootMenus.edit,
|
||||
rootMenus.view,
|
||||
rootMenus.go,
|
||||
rootMenus.folder,
|
||||
rootMenus.note,
|
||||
rootMenus.tools,
|
||||
rootMenus.help,
|
||||
@@ -852,7 +864,7 @@ function useMenu(props: Props) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
};
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins]);
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins, props.customCss]);
|
||||
|
||||
useMenuStates(menu, props);
|
||||
|
||||
@@ -909,6 +921,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
['spellChecker.language']: state.settings['spellChecker.language'],
|
||||
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
|
||||
plugins: state.pluginService.plugins,
|
||||
customCss: state.customCss,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ interface MultiNoteActionsProps {
|
||||
watchedNoteFiles: string[];
|
||||
plugins: PluginStates;
|
||||
inConflictFolder: boolean;
|
||||
customCss: string;
|
||||
}
|
||||
|
||||
function styles_(props: MultiNoteActionsProps) {
|
||||
@@ -53,6 +54,7 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
|
||||
watchedNoteFiles: props.watchedNoteFiles,
|
||||
plugins: props.plugins,
|
||||
inConflictFolder: props.inConflictFolder,
|
||||
customCss: props.customCss,
|
||||
});
|
||||
|
||||
const itemComps = [];
|
||||
|
||||
@@ -24,7 +24,7 @@ interface KeyToLabelMap {
|
||||
let markupToHtml_: any = null;
|
||||
function markupToHtml() {
|
||||
if (markupToHtml_) return markupToHtml_;
|
||||
markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
|
||||
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
|
||||
return markupToHtml_;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
||||
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml } from '../../utils/resourceHandling';
|
||||
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos } from '../../utils/types';
|
||||
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml, attachedResources } from '../../utils/resourceHandling';
|
||||
import useScroll from './utils/useScroll';
|
||||
import styles_ from './styles';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
@@ -20,6 +20,7 @@ const taboverride = require('taboverride');
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import setupToolbarButtons from './utils/setupToolbarButtons';
|
||||
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
@@ -66,6 +67,26 @@ function newBlockSource(language: string = '', content: string = ''): any {
|
||||
};
|
||||
}
|
||||
|
||||
// In TinyMCE 5.2, when setting the body to '<div id="rendered-md"></div>',
|
||||
// it would end up as '<div id="rendered-md"><br/></div>' once rendered
|
||||
// (an additional <br/> was inserted).
|
||||
//
|
||||
// This behaviour was "fixed" later on, possibly in 5.6, which has this change:
|
||||
//
|
||||
// - Fixed getContent with text format returning a new line when the editor is empty #TINY-6281
|
||||
//
|
||||
// The problem is that the list plugin was, unknown to me, relying on this <br/>
|
||||
// being present. Without it, trying to add a bullet point or checkbox on an
|
||||
// empty document, does nothing. The exact reason for this is unclear
|
||||
// so as a workaround we manually add this <br> for empty documents,
|
||||
// which fixes the issue.
|
||||
//
|
||||
// Perhaps upgrading the list plugin (which is a fork of TinyMCE own list plugin)
|
||||
// would help?
|
||||
function awfulBrHack(html: string): string {
|
||||
return html === '<div id="rendered-md"></div>' ? '<div id="rendered-md"><br/></div>' : html;
|
||||
}
|
||||
|
||||
function findEditableContainer(node: any): any {
|
||||
while (node) {
|
||||
if (node.classList && node.classList.contains('joplin-editable')) return node;
|
||||
@@ -127,6 +148,12 @@ const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
|
||||
'search': { name: 'SearchReplace' },
|
||||
};
|
||||
|
||||
interface LastOnChangeEventInfo {
|
||||
content: string;
|
||||
resourceInfos: ResourceInfos;
|
||||
contentKey: string;
|
||||
}
|
||||
|
||||
let loadedCssFiles_: string[] = [];
|
||||
let loadedJsFiles_: string[] = [];
|
||||
let dispatchDidUpdateIID_: any = null;
|
||||
@@ -147,7 +174,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
const markupToHtml = useRef(null);
|
||||
markupToHtml.current = props.markupToHtml;
|
||||
|
||||
const lastOnChangeEventInfo = useRef<any>({
|
||||
const lastOnChangeEventInfo = useRef<LastOnChangeEventInfo>({
|
||||
content: null,
|
||||
resourceInfos: null,
|
||||
contentKey: null,
|
||||
@@ -338,7 +365,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
element.id = script.id;
|
||||
|
||||
element.onload = () => {
|
||||
resolve();
|
||||
resolve(null);
|
||||
};
|
||||
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
@@ -807,6 +834,25 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
function resourceInfosEqual(ri1: ResourceInfos, ri2: ResourceInfos): boolean {
|
||||
if (ri1 && !ri2 || !ri1 && ri2) return false;
|
||||
if (!ri1 && !ri2) return true;
|
||||
|
||||
const keys1 = Object.keys(ri1);
|
||||
const keys2 = Object.keys(ri2);
|
||||
|
||||
if (keys1.length !== keys2.length) return false;
|
||||
|
||||
// The attachedResources() call that generates the ResourceInfos object
|
||||
// uses cache for the resource objects, so we can use strict equality
|
||||
// for comparison.
|
||||
for (const k of keys1) {
|
||||
if (ri1[k] !== ri2[k]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return () => {};
|
||||
|
||||
@@ -818,11 +864,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
let cancelled = false;
|
||||
|
||||
const loadContent = async () => {
|
||||
if (lastOnChangeEventInfo.current.content !== props.content || lastOnChangeEventInfo.current.resourceInfos !== props.resourceInfos) {
|
||||
const resourcesEqual = resourceInfosEqual(lastOnChangeEventInfo.current.resourceInfos, props.resourceInfos);
|
||||
|
||||
if (lastOnChangeEventInfo.current.content !== props.content || !resourcesEqual) {
|
||||
const result = await props.markupToHtml(props.contentMarkupLanguage, props.content, markupRenderOptions({ resourceInfos: props.resourceInfos }));
|
||||
if (cancelled) return;
|
||||
|
||||
editor.setContent(result.html);
|
||||
editor.setContent(awfulBrHack(result.html));
|
||||
|
||||
if (lastOnChangeEventInfo.current.contentKey !== props.contentKey) {
|
||||
// Need to clear UndoManager to avoid this problem:
|
||||
@@ -922,6 +970,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
const contentMd = await prop_htmlToMarkdownRef.current(info.contentMarkupLanguage, info.editor.getContent(), info.contentOriginalCss);
|
||||
|
||||
lastOnChangeEventInfo.current.content = contentMd;
|
||||
lastOnChangeEventInfo.current.resourceInfos = await attachedResources(contentMd);
|
||||
|
||||
props_onChangeRef.current({
|
||||
changeId: info.changeId,
|
||||
@@ -1017,31 +1066,37 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
}
|
||||
|
||||
async function onPaste(event: any) {
|
||||
// We do not use the default pasting behaviour because the input has
|
||||
// to be processed in various ways.
|
||||
event.preventDefault();
|
||||
|
||||
const resourceMds = await handlePasteEvent(event);
|
||||
if (resourceMds.length) {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
} else {
|
||||
const pastedText = event.clipboardData.getData('text');
|
||||
const pastedText = event.clipboardData.getData('text/plain');
|
||||
|
||||
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
|
||||
event.preventDefault();
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, pastedText, markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
} else { // Paste regular text
|
||||
// HACK: TinyMCE doesn't add an undo step when pasting, for unclear reasons
|
||||
// so we manually add it here. We also can't do it immediately it seems, or
|
||||
// else nothing is added to the stack, so do it on the next frame.
|
||||
|
||||
const pastedHtml = clipboard.readHTML();
|
||||
if (pastedHtml) {
|
||||
event.preventDefault();
|
||||
const pastedHtml = event.clipboardData.getData('text/html');
|
||||
if (pastedHtml) { // Handles HTML
|
||||
const modifiedHtml = await processPastedHtml(pastedHtml);
|
||||
editor.insertContent(modifiedHtml);
|
||||
} else { // Handles plain text
|
||||
pasteAsPlainText(pastedText);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => editor.undoManager.add());
|
||||
onChangeHandler();
|
||||
// This code before was necessary to get undo working after
|
||||
// pasting but it seems it's no longer necessary, so
|
||||
// removing it for now. We also couldn't do it immediately
|
||||
// it seems, or else nothing is added to the stack, so do it
|
||||
// on the next frame.
|
||||
//
|
||||
// window.requestAnimationFrame(() =>
|
||||
// editor.undoManager.add()); onChangeHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1060,6 +1115,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
onChangeHandler();
|
||||
}
|
||||
|
||||
function pasteAsPlainText(text: string = null) {
|
||||
const pastedText = text === null ? clipboard.readText() : text;
|
||||
if (pastedText) {
|
||||
editor.insertContent(plainTextToHtml(pastedText));
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyDown(event: any) {
|
||||
// It seems "paste as text" is handled automatically by
|
||||
// on Windows so the code below so we need to run the below
|
||||
@@ -1072,8 +1134,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
// it here and we don't need to do anything special in onPaste
|
||||
if (!shim.isWindows()) {
|
||||
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
|
||||
const pastedText = clipboard.readText();
|
||||
if (pastedText) editor.insertContent(pastedText);
|
||||
pasteAsPlainText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,10 +156,11 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
customCss: props.customCss,
|
||||
});
|
||||
|
||||
return markupToHtml.allAssets(markupLanguage, theme);
|
||||
}, [props.themeId]);
|
||||
}, [props.themeId, props.customCss]);
|
||||
|
||||
const handleProvisionalFlag = useCallback(() => {
|
||||
if (props.isProvisional) {
|
||||
@@ -458,6 +459,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
watchedNoteFiles={props.watchedNoteFiles}
|
||||
plugins={props.plugins}
|
||||
inConflictFolder={props.selectedFolderId === Folder.conflictFolderId()}
|
||||
customCss={props.customCss}
|
||||
/>;
|
||||
}
|
||||
|
||||
@@ -549,7 +551,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{renderSearchBar()}
|
||||
</div>
|
||||
<div style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
|
||||
<div className="tag-bar" style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
|
||||
{renderTagButton()}
|
||||
{renderTagBar()}
|
||||
</div>
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function NoteTitleBar(props: Props) {
|
||||
}, []);
|
||||
|
||||
function renderTitleBarDate() {
|
||||
return <span style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
|
||||
return <span className="updated-time-label" style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
|
||||
}
|
||||
|
||||
function renderNoteToolbar() {
|
||||
@@ -104,6 +104,7 @@ export default function NoteTitleBar(props: Props) {
|
||||
return (
|
||||
<StyledRoot>
|
||||
<input
|
||||
className="title-input"
|
||||
type="text"
|
||||
ref={props.titleInputRef}
|
||||
placeholder={props.isProvisional ? _('Creating new %s...', props.noteIsTodo ? _('to-do') : _('note')) : ''}
|
||||
|
||||
@@ -135,6 +135,13 @@ export async function processPastedHtml(html: string) {
|
||||
const allImageUrls: string[] = [];
|
||||
const mappedResources: Record<string, string> = {};
|
||||
|
||||
// When copying text from eg. GitHub, the HTML might contain non-breaking
|
||||
// spaces instead of regular spaces. If these non-breaking spaces are
|
||||
// inserted into the TinyMCE editor (using insertContent), they will be
|
||||
// dropped. So here we convert them to regular spaces.
|
||||
// https://stackoverflow.com/a/31790544/561309
|
||||
html = html.replace(/[\u202F\u00A0]/g, ' ');
|
||||
|
||||
htmlUtils.replaceImageUrls(html, (src: string) => {
|
||||
allImageUrls.push(src);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
|
||||
|
||||
export interface ToolbarButtonInfos {
|
||||
[key: string]: ToolbarButtonInfo;
|
||||
@@ -49,9 +51,9 @@ export interface NoteBodyEditorProps {
|
||||
onWillChange(event: any): void;
|
||||
onMessage(event: any): void;
|
||||
onScroll(event: any): void;
|
||||
markupToHtml: Function;
|
||||
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: any)=> Promise<RenderResult>;
|
||||
htmlToMarkdown: Function;
|
||||
allAssets: Function;
|
||||
allAssets: (markupLanguage: MarkupLanguage)=> Promise<RenderResultPluginAsset[]>;
|
||||
disabled: boolean;
|
||||
dispatch: Function;
|
||||
noteToolbar: any;
|
||||
|
||||
@@ -24,6 +24,7 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
const markupToHtml = useMemo(() => {
|
||||
return markupLanguageUtils.newMarkupToHtml(deps.plugins, {
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
customCss: customCss || '',
|
||||
});
|
||||
}, [plugins]);
|
||||
|
||||
@@ -49,7 +50,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
|
||||
const result = await markupToHtml.render(markupLanguage, md, theme, Object.assign({}, {
|
||||
codeTheme: theme.codeThemeCss,
|
||||
userCss: customCss || '',
|
||||
resources: resources,
|
||||
postMessageSyntax: 'ipcProxySendToHost',
|
||||
splitted: true,
|
||||
|
||||
@@ -125,6 +125,7 @@ class NoteListComponent extends React.Component {
|
||||
watchedNoteFiles: this.props.watchedNoteFiles,
|
||||
plugins: this.props.plugins,
|
||||
inConflictFolder: this.props.selectedFolderId === Folder.conflictFolderId(),
|
||||
customCss: this.props.customCss,
|
||||
});
|
||||
|
||||
menu.popup(bridge().window());
|
||||
@@ -513,6 +514,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
noteSortOrder: state.settings['notes.sortOrder.field'],
|
||||
highlightedWords: state.highlightedWords,
|
||||
plugins: state.pluginService.plugins,
|
||||
customCss: state.customCss,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -54,12 +54,14 @@ export default function NoteListControls(props: Props) {
|
||||
return (
|
||||
<ButtonContainer>
|
||||
<StyledButton
|
||||
className="new-todo-button"
|
||||
tooltip={CommandService.instance().label('newTodo')}
|
||||
iconName="far fa-check-square"
|
||||
level={ButtonLevel.Primary}
|
||||
onClick={onNewTodoButtonClick}
|
||||
/>
|
||||
<StyledButton
|
||||
className="new-note-button"
|
||||
tooltip={CommandService.instance().label('newNote')}
|
||||
iconName="icon-note"
|
||||
level={ButtonLevel.Primary}
|
||||
|
||||
@@ -110,6 +110,7 @@ function NoteListItem(props: NoteListItemProps, ref: any) {
|
||||
|
||||
let listItemTitleStyle = Object.assign({}, props.style.listItemTitle);
|
||||
listItemTitleStyle.paddingLeft = !item.is_todo ? hPadding : 4;
|
||||
if (item.is_shared) listItemTitleStyle.color = theme.colorWarn;
|
||||
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, props.style.listItemTitleCompleted);
|
||||
|
||||
const displayTitle = Note.displayTitle(item);
|
||||
@@ -149,12 +150,21 @@ function NoteListItem(props: NoteListItemProps, ref: any) {
|
||||
color: theme.color,
|
||||
};
|
||||
const watchedIcon = props.isWatched ? null : <i style={watchedIconStyle} className={'fa fa-share-square'}></i>;
|
||||
const classNames = [
|
||||
'list-item-container',
|
||||
props.isSelected && 'selected',
|
||||
item.todo_completed && 'todo-completed',
|
||||
item.is_todo ? 'todo-list-item' : 'note-list-item',
|
||||
(props.index + 1) % 2 === 0 ? 'even' : 'odd',
|
||||
]
|
||||
.filter(e => !!e)
|
||||
.join(' ');
|
||||
|
||||
// Need to include "todo_completed" in key so that checkbox is updated when
|
||||
// item is changed via sync.
|
||||
return (
|
||||
<StyledRoot
|
||||
className="list-item-container"
|
||||
className={classNames}
|
||||
onDragOver={props.onNoteDragOver}
|
||||
onDrop={props.onNoteDrop}
|
||||
width={props.width}
|
||||
|
||||
@@ -118,11 +118,11 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
||||
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
customCss: this.props.customCss ? this.props.customCss : '',
|
||||
});
|
||||
|
||||
const result = await markupToHtml.render(markupLanguage, noteBody, theme, {
|
||||
codeTheme: theme.codeThemeCss,
|
||||
userCss: this.props.customCss ? this.props.customCss : '',
|
||||
resources: await shared.attachedResources(noteBody),
|
||||
postMessageSyntax: 'ipcProxySendToHost',
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ class NoteSearchBarComponent extends React.Component {
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={this.props.style}>
|
||||
<div className="note-search-bar" style={this.props.style}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{closeButton}
|
||||
<input
|
||||
|
||||
@@ -221,11 +221,11 @@ class PromptDialog extends React.Component {
|
||||
let inputComp = null;
|
||||
|
||||
if (this.props.inputType === 'datetime') {
|
||||
inputComp = <Datetime value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
|
||||
inputComp = <Datetime className="datetime-picker" value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
|
||||
} else if (this.props.inputType === 'tags') {
|
||||
inputComp = <CreatableSelect styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
|
||||
inputComp = <CreatableSelect className="tag-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
|
||||
} else if (this.props.inputType === 'dropdown') {
|
||||
inputComp = <Select styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
|
||||
inputComp = <Select className="item-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
|
||||
} else {
|
||||
inputComp = <input style={styles.input} ref={this.answerInput_} value={this.state.answer} type="text" onChange={event => onChange(event)} onKeyDown={event => onKeyDown(event)} />;
|
||||
}
|
||||
@@ -254,8 +254,8 @@ class PromptDialog extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.modalLayer}>
|
||||
<div style={styles.promptDialog}>
|
||||
<div className="modal-layer" style={styles.modalLayer}>
|
||||
<div className="modal-dialog" style={styles.promptDialog}>
|
||||
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
|
||||
<div style={{ display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor }}>
|
||||
{inputComp}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import produce from 'immer';
|
||||
import iterateItems from './iterateItems';
|
||||
import { LayoutItem } from './types';
|
||||
import validateLayout from './validateLayout';
|
||||
|
||||
interface ItemToRemove {
|
||||
parent: LayoutItem;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export default function(layout: LayoutItem): LayoutItem {
|
||||
const itemsToRemove: ItemToRemove[] = [];
|
||||
|
||||
const output = produce(layout, (layoutDraft: LayoutItem) => {
|
||||
iterateItems(layoutDraft, (itemIndex: number, item: LayoutItem, parent: LayoutItem) => {
|
||||
if (!item.key) itemsToRemove.push({ parent, index: itemIndex });
|
||||
return true;
|
||||
});
|
||||
|
||||
itemsToRemove.sort((a: ItemToRemove, b: ItemToRemove) => {
|
||||
return a.index > b.index ? -1 : +1;
|
||||
});
|
||||
|
||||
for (const item of itemsToRemove) {
|
||||
item.parent.children.splice(item.index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
return output !== layout ? validateLayout(output) : layout;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ function SearchBar(props: Props) {
|
||||
}, [props.notesParentType, onExitSearch]);
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<Root className="search-bar">
|
||||
<SearchInput
|
||||
inputRef={props.inputRef}
|
||||
value={query}
|
||||
|
||||
@@ -10,19 +10,21 @@ import { reg } from '@joplin/lib/registry';
|
||||
import Dialog from './Dialog';
|
||||
import DialogTitle from './DialogTitle';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
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';
|
||||
const { clipboard } = require('electron');
|
||||
|
||||
interface ShareNoteDialogProps {
|
||||
interface Props {
|
||||
themeId: number;
|
||||
noteIds: Array<string>;
|
||||
onClose: Function;
|
||||
shares: StateShare[];
|
||||
}
|
||||
|
||||
interface SharesMap {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
function styles_(props: ShareNoteDialogProps) {
|
||||
function styles_(props: Props) {
|
||||
return buildStyle('ShareNoteDialog', props.themeId, (theme: any) => {
|
||||
return {
|
||||
noteList: {
|
||||
@@ -60,17 +62,21 @@ function styles_(props: ShareNoteDialogProps) {
|
||||
});
|
||||
}
|
||||
|
||||
export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
export function ShareNoteDialog(props: Props) {
|
||||
console.info('Render ShareNoteDialog');
|
||||
|
||||
const [notes, setNotes] = useState<any[]>([]);
|
||||
const [notes, setNotes] = useState<NoteEntity[]>([]);
|
||||
const [sharesState, setSharesState] = useState<string>('unknown');
|
||||
const [shares, setShares] = useState<SharesMap>({});
|
||||
// const [shares, setShares] = useState<SharesMap>({});
|
||||
|
||||
const noteCount = notes.length;
|
||||
const theme = themeStyle(props.themeId);
|
||||
const styles = styles_(props);
|
||||
|
||||
useEffect(() => {
|
||||
void ShareService.instance().refreshShares();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchNotes() {
|
||||
const result = [];
|
||||
@@ -87,9 +93,9 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
props.onClose();
|
||||
};
|
||||
|
||||
const copyLinksToClipboard = (shares: SharesMap) => {
|
||||
const copyLinksToClipboard = (shares: StateShare[]) => {
|
||||
const links = [];
|
||||
for (const n in shares) links.push(ShareService.instance().shareUrl(shares[n]));
|
||||
for (const share of shares) links.push(ShareService.instance().shareUrl(share));
|
||||
clipboard.writeText(links.join('\n'));
|
||||
};
|
||||
|
||||
@@ -109,15 +115,13 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
|
||||
setSharesState('creating');
|
||||
|
||||
const newShares = Object.assign({}, shares);
|
||||
const newShares: StateShare[] = [];
|
||||
|
||||
for (const note of notes) {
|
||||
const share = await service.shareNote(note.id);
|
||||
newShares[note.id] = share;
|
||||
newShares.push(share);
|
||||
}
|
||||
|
||||
setShares(newShares);
|
||||
|
||||
setSharesState('synchronizing');
|
||||
await reg.waitForSyncFinishedThenSync();
|
||||
setSharesState('creating');
|
||||
@@ -125,6 +129,8 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
copyLinksToClipboard(newShares);
|
||||
|
||||
setSharesState('created');
|
||||
|
||||
await ShareService.instance().refreshShares();
|
||||
} catch (error) {
|
||||
if (error.code === 404 && !hasSynced) {
|
||||
reg.logger().info('ShareNoteDialog: Note does not exist on server - trying to sync it.', error);
|
||||
@@ -142,34 +148,53 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const removeNoteButton_click = (event: any) => {
|
||||
const newNotes = [];
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
const n = notes[i];
|
||||
if (n.id === event.noteId) continue;
|
||||
newNotes.push(n);
|
||||
}
|
||||
setNotes(newNotes);
|
||||
// const removeNoteButton_click = (event: any) => {
|
||||
// const newNotes = [];
|
||||
// for (let i = 0; i < notes.length; i++) {
|
||||
// const n = notes[i];
|
||||
// if (n.id === event.noteId) continue;
|
||||
// newNotes.push(n);
|
||||
// }
|
||||
// setNotes(newNotes);
|
||||
// };
|
||||
|
||||
const unshareNoteButton_click = async (event: any) => {
|
||||
await ShareService.instance().unshareNote(event.noteId);
|
||||
await ShareService.instance().refreshShares();
|
||||
};
|
||||
|
||||
const renderNote = (note: any) => {
|
||||
const removeButton = notes.length <= 1 ? null : (
|
||||
<button onClick={() => removeNoteButton_click({ noteId: note.id })} style={styles.noteRemoveButton}>
|
||||
<i style={styles.noteRemoveButtonIcon} className={'fa fa-times'}></i>
|
||||
</button>
|
||||
const renderNote = (note: NoteEntity) => {
|
||||
const unshareButton = !props.shares.find(s => s.note_id === note.id) ? null : (
|
||||
<Button tooltip={_('Unshare note')} iconName="fas fa-share-alt" onClick={() => unshareNoteButton_click({ noteId: note.id })}/>
|
||||
);
|
||||
|
||||
// const removeButton = notes.length <= 1 ? null : (
|
||||
// <Button iconName="fa fa-times" onClick={() => removeNoteButton_click({ noteId: note.id })}/>
|
||||
// );
|
||||
|
||||
// const unshareButton = !shares[note.id] ? null : (
|
||||
// <button onClick={() => unshareNoteButton_click({ noteId: note.id })} style={styles.noteRemoveButton}>
|
||||
// <i style={styles.noteRemoveButtonIcon} className={'fas fa-share-alt'}></i>
|
||||
// </button>
|
||||
// );
|
||||
|
||||
// const removeButton = notes.length <= 1 ? null : (
|
||||
// <button onClick={() => removeNoteButton_click({ noteId: note.id })} style={styles.noteRemoveButton}>
|
||||
// <i style={styles.noteRemoveButtonIcon} className={'fa fa-times'}></i>
|
||||
// </button>
|
||||
// );
|
||||
|
||||
return (
|
||||
<div key={note.id} style={styles.note}>
|
||||
<span style={styles.noteTitle}>{note.title}</span>{removeButton}
|
||||
<span style={styles.noteTitle}>{note.title}</span>{unshareButton}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderNoteList = (notes: any) => {
|
||||
const noteComps = [];
|
||||
for (const noteId of Object.keys(notes)) {
|
||||
noteComps.push(renderNote(notes[noteId]));
|
||||
for (const note of notes) {
|
||||
noteComps.push(renderNote(note));
|
||||
}
|
||||
return <div style={styles.noteList}>{noteComps}</div>;
|
||||
};
|
||||
@@ -194,7 +219,12 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
<button disabled={['creating', 'synchronizing'].indexOf(sharesState) >= 0} style={styles.copyShareLinkButton} onClick={shareLinkButton_click}>{_n('Copy Shareable Link', 'Copy Shareable Links', noteCount)}</button>
|
||||
<div style={theme.textStyle}>{statusMessage(sharesState)}</div>
|
||||
{renderEncryptionWarningMessage()}
|
||||
<DialogButtonRow themeId={props.themeId} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||
<DialogButtonRow
|
||||
themeId={props.themeId}
|
||||
onClick={buttonRow_click}
|
||||
okButtonShow={false}
|
||||
cancelButtonLabel={_('Close')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -203,3 +233,11 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
<Dialog renderContent={renderContent}/>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
shares: state.shareService.shares.filter(s => !!s.note_id),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(ShareNoteDialog as any);
|
||||
|
||||
@@ -77,12 +77,12 @@ function ExpandLink(props: any) {
|
||||
function FolderItem(props: any) {
|
||||
const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props;
|
||||
|
||||
const noteCountComp = noteCount ? <StyledNoteCount>{noteCount}</StyledNoteCount> : null;
|
||||
const noteCountComp = noteCount ? <StyledNoteCount className="note-count-label">{noteCount}</StyledNoteCount> : null;
|
||||
|
||||
const shareIcon = shareId && !parentId ? <StyledShareIcon className="fas fa-share-alt"></StyledShareIcon> : null;
|
||||
|
||||
return (
|
||||
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
|
||||
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth} ${selected ? 'selected' : ''}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
|
||||
<ExpandLink themeId={props.themeId} hasChildren={hasChildren} folderId={folderId} onClick={onFolderToggleClick_} isExpanded={isExpanded}/>
|
||||
<StyledListItemAnchor
|
||||
ref={anchorRef}
|
||||
@@ -100,7 +100,8 @@ function FolderItem(props: any) {
|
||||
}}
|
||||
onDoubleClick={onFolderToggleClick_}
|
||||
>
|
||||
{folderTitle} {shareIcon} {noteCountComp}
|
||||
<span className="title">{folderTitle}</span>
|
||||
{shareIcon} {noteCountComp}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
@@ -382,7 +383,7 @@ class SidebarComponent extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
renderNoteCount(count: number) {
|
||||
return count ? <StyledNoteCount>{count}</StyledNoteCount> : null;
|
||||
return count ? <StyledNoteCount className="note-count-label">{count}</StyledNoteCount> : null;
|
||||
}
|
||||
|
||||
renderExpandIcon(isExpanded: boolean, isVisible: boolean = true) {
|
||||
@@ -394,7 +395,7 @@ class SidebarComponent extends React.Component<Props, State> {
|
||||
|
||||
renderAllNotesItem(selected: boolean) {
|
||||
return (
|
||||
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0'} isSpecialItem={true}>
|
||||
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0 all-notes'} isSpecialItem={true}>
|
||||
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
|
||||
<StyledAllNotesIcon className="icon-notes"/>
|
||||
<StyledListItemAnchor
|
||||
@@ -451,7 +452,12 @@ class SidebarComponent extends React.Component<Props, State> {
|
||||
const noteCount = Setting.value('showNoteCounts') ? this.renderNoteCount(tag.note_count) : '';
|
||||
|
||||
return (
|
||||
<StyledListItem selected={selected} className={'list-item-container'} key={tag.id} onDrop={this.onTagDrop_} data-tag-id={tag.id}>
|
||||
<StyledListItem selected={selected}
|
||||
className={`list-item-container ${selected ? 'selected' : ''}`}
|
||||
key={tag.id}
|
||||
onDrop={this.onTagDrop_}
|
||||
data-tag-id={tag.id}
|
||||
>
|
||||
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
|
||||
<StyledListItemAnchor
|
||||
ref={anchorRef}
|
||||
@@ -465,7 +471,8 @@ class SidebarComponent extends React.Component<Props, State> {
|
||||
this.tagItem_click(tag);
|
||||
}}
|
||||
>
|
||||
{Tag.displayTitle(tag)} {noteCount}
|
||||
<span className="tag-label">{Tag.displayTitle(tag)}</span>
|
||||
{noteCount}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
@@ -657,7 +664,11 @@ class SidebarComponent extends React.Component<Props, State> {
|
||||
const folderItems = [this.renderAllNotesItem(allNotesSelected)].concat(result.items);
|
||||
this.folderItemsOrder_ = result.order;
|
||||
items.push(
|
||||
<div className="folders" key="folder_items" style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}>
|
||||
<div
|
||||
className={`folders ${this.state.folderHeaderIsExpanded ? 'expanded' : ''}`}
|
||||
key="folder_items"
|
||||
style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}
|
||||
>
|
||||
{folderItems}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -69,7 +69,6 @@ function listItemTextColor(props: any) {
|
||||
|
||||
export const StyledListItemAnchor = styled.a`
|
||||
font-size: ${(props: any) => Math.round(props.theme.fontSize * 1.0833333)}px;
|
||||
// font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: ${(props: any) => listItemTextColor(props)};
|
||||
cursor: default;
|
||||
|
||||
@@ -87,12 +87,15 @@ function StatusScreen(props: Props) {
|
||||
|
||||
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
|
||||
|
||||
let currentListKey = '';
|
||||
let listItems: any[] = [];
|
||||
for (const n in section.body) {
|
||||
if (!section.body.hasOwnProperty(n)) continue;
|
||||
const item = section.body[n];
|
||||
let text = '';
|
||||
|
||||
let retryLink = null;
|
||||
let itemType = null;
|
||||
if (typeof item === 'object') {
|
||||
if (item.canRetry) {
|
||||
const onClick = async () => {
|
||||
@@ -107,18 +110,40 @@ function StatusScreen(props: Props) {
|
||||
);
|
||||
}
|
||||
text = item.text;
|
||||
itemType = item.type;
|
||||
} else {
|
||||
text = item;
|
||||
}
|
||||
|
||||
if (itemType === 'openList') {
|
||||
currentListKey = item.key;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (itemType === 'closeList') {
|
||||
itemsHtml.push(<ul key={currentListKey}>{listItems}</ul>);
|
||||
currentListKey = '';
|
||||
listItems = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!text) text = '\xa0';
|
||||
|
||||
itemsHtml.push(
|
||||
<div style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</div>
|
||||
);
|
||||
if (currentListKey) {
|
||||
listItems.push(
|
||||
<li style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
itemsHtml.push(
|
||||
<div style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (section.canRetryAll) {
|
||||
|
||||
@@ -17,7 +17,16 @@ export default function ToggleEditorsButton(props: Props) {
|
||||
const style = styles_(props);
|
||||
|
||||
return (
|
||||
<button style={style.button} disabled={!props.toolbarButtonInfo.enabled} aria-label={props.toolbarButtonInfo.tooltip} title={props.toolbarButtonInfo.tooltip} type="button" className="tox-tbtn" aria-pressed="false" onClick={props.toolbarButtonInfo.onClick}>
|
||||
<button
|
||||
style={style.button}
|
||||
disabled={!props.toolbarButtonInfo.enabled}
|
||||
aria-label={props.toolbarButtonInfo.tooltip}
|
||||
title={props.toolbarButtonInfo.tooltip}
|
||||
type="button"
|
||||
className={`tox-tbtn ${props.value}-active`}
|
||||
aria-pressed="false"
|
||||
onClick={props.toolbarButtonInfo.onClick}
|
||||
>
|
||||
<div style={style.leftInnerButton}>
|
||||
<i style={style.leftIcon} className="fab fa-markdown"></i>
|
||||
</div>
|
||||
|
||||
@@ -45,5 +45,7 @@ export default function() {
|
||||
'editor.swapLineUp',
|
||||
'editor.swapLineDown',
|
||||
'toggleSafeMode',
|
||||
'showShareNoteDialog',
|
||||
'showShareFolderDialog',
|
||||
];
|
||||
}
|
||||
|
||||
9
packages/app-desktop/gui/style/StyledLink.tsx
Normal file
9
packages/app-desktop/gui/style/StyledLink.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledLink = styled.a`
|
||||
font-size: ${props => props.theme.fontSize}px;
|
||||
color: ${props => props.theme.urlColor};
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
`;
|
||||
|
||||
export default StyledLink;
|
||||
@@ -7,6 +7,7 @@ const StyledMessage = styled.div`
|
||||
color: ${props => props.theme.color};
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
padding: ${props => props.type === 'error' ? props.theme.mainPadding : '0'}px;
|
||||
word-break: break-all;
|
||||
`;
|
||||
|
||||
export default StyledMessage;
|
||||
|
||||
@@ -22,6 +22,7 @@ interface ContextMenuProps {
|
||||
watchedNoteFiles: string[];
|
||||
plugins: PluginStates;
|
||||
inConflictFolder: boolean;
|
||||
customCss: string;
|
||||
}
|
||||
|
||||
export default class NoteListUtils {
|
||||
@@ -158,6 +159,7 @@ export default class NoteListUtils {
|
||||
sourceNoteIds: noteIds,
|
||||
includeConflicts: props.inConflictFolder,
|
||||
plugins: props.plugins,
|
||||
customCss: props.customCss,
|
||||
});
|
||||
},
|
||||
})
|
||||
|
||||
2
packages/app-desktop/package-lock.json
generated
2
packages/app-desktop/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.8.5",
|
||||
"version": "2.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.8.5",
|
||||
"version": "2.0.1",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
|
||||
@@ -246,7 +246,7 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
|
||||
markupToHtml() {
|
||||
if (this.markupToHtml_) return this.markupToHtml_;
|
||||
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
|
||||
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
|
||||
return this.markupToHtml_;
|
||||
}
|
||||
|
||||
@@ -432,7 +432,8 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
renderItem(item: SearchResult) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
const rowStyle = item.id === this.state.selectedItemId ? style.rowSelected : style.row;
|
||||
const isSelected = item.id === this.state.selectedItemId;
|
||||
const rowStyle = isSelected ? style.rowSelected : style.row;
|
||||
const titleHtml = item.fragments
|
||||
? `<span style="font-weight: bold; color: ${theme.colorBright};">${item.title}</span>`
|
||||
: surroundKeywords(this.state.keywords, item.title, `<span style="font-weight: bold; color: ${theme.colorBright};">`, '</span>', { escapeHtml: true });
|
||||
@@ -444,7 +445,7 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
const fragmentComp = !fragmentsHtml ? null : <div style={style.rowFragments} dangerouslySetInnerHTML={{ __html: (fragmentsHtml) }}></div>;
|
||||
|
||||
return (
|
||||
<div key={item.id} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
|
||||
<div key={item.id} className={isSelected ? 'selected' : null} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
|
||||
<div style={style.rowTitle} dangerouslySetInnerHTML={{ __html: titleHtml }}></div>
|
||||
{fragmentComp}
|
||||
{pathComp}
|
||||
@@ -521,11 +522,11 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
const helpComp = !this.state.showHelp ? null : <div style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
|
||||
const helpComp = !this.state.showHelp ? null : <div className="help-text" style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
|
||||
|
||||
return (
|
||||
<div onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
|
||||
<div style={style.dialogBox}>
|
||||
<div className="modal-layer" onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
|
||||
<div className="modal-dialog" style={style.dialogBox}>
|
||||
{helpComp}
|
||||
<div style={style.inputHelpWrapper}>
|
||||
<input autoFocus type="text" style={style.input} ref={this.inputRef} value={this.state.query} onChange={this.input_onChange} onKeyDown={this.input_onKeyDown} />
|
||||
|
||||
@@ -14,31 +14,35 @@ if [ "$1" == "" ]; then
|
||||
fi
|
||||
|
||||
USER_NUM=$1
|
||||
|
||||
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
|
||||
rm -f "$CMD_FILE"
|
||||
|
||||
USER_EMAIL="user$USER_NUM@example.com"
|
||||
RESET_ALL=$2
|
||||
PROFILE_DIR=~/.config/joplindev-desktop-$USER_NUM
|
||||
rm -rf "$PROFILE_DIR"
|
||||
|
||||
echo "config keychain.supported 0" >> "$CMD_FILE"
|
||||
echo "config sync.target 9" >> "$CMD_FILE"
|
||||
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
|
||||
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
|
||||
echo "config sync.9.password 123456" >> "$CMD_FILE"
|
||||
if [ "$RESET_ALL" == "1" ]; then
|
||||
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
|
||||
rm -f "$CMD_FILE"
|
||||
|
||||
if [ "$1" == "1" ]; then
|
||||
curl --data '{"action": "createTestUsers"}' http://localhost:22300/api/debug
|
||||
USER_EMAIL="user$USER_NUM@example.com"
|
||||
rm -rf "$PROFILE_DIR"
|
||||
|
||||
echo 'mkbook "shared"' >> "$CMD_FILE"
|
||||
echo 'mkbook "other"' >> "$CMD_FILE"
|
||||
echo 'use "shared"' >> "$CMD_FILE"
|
||||
echo 'mknote "note 1"' >> "$CMD_FILE"
|
||||
echo "config keychain.supported 0" >> "$CMD_FILE"
|
||||
echo "config sync.target 9" >> "$CMD_FILE"
|
||||
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
|
||||
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
|
||||
echo "config sync.9.password 123456" >> "$CMD_FILE"
|
||||
|
||||
if [ "$1" == "1" ]; then
|
||||
curl --data '{"action": "createTestUsers"}' -H 'Content-Type: application/json' http://localhost:22300/api/debug
|
||||
|
||||
echo 'mkbook "shared"' >> "$CMD_FILE"
|
||||
echo 'mkbook "other"' >> "$CMD_FILE"
|
||||
echo 'use "shared"' >> "$CMD_FILE"
|
||||
echo 'mknote "note 1"' >> "$CMD_FILE"
|
||||
echo 'mknote "note 2"' >> "$CMD_FILE"
|
||||
fi
|
||||
|
||||
cd "$ROOT_DIR/packages/app-cli"
|
||||
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
|
||||
fi
|
||||
|
||||
cd "$ROOT_DIR/packages/app-cli"
|
||||
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
|
||||
|
||||
cd "$ROOT_DIR/packages/app-desktop"
|
||||
npm start -- --profile "$PROFILE_DIR"
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { MarkupLanguageUtils as BaseMarkupLanguageUtils } from '@joplin/lib/markupLanguageUtils';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import { contentScriptsToRendererRules } from '@joplin/lib/services/plugins/utils/loadContentScripts';
|
||||
import { Options } from '@joplin/renderer/MarkupToHtml';
|
||||
|
||||
class MarkupLanguageUtils extends BaseMarkupLanguageUtils {
|
||||
|
||||
public newMarkupToHtml(plugins: PluginStates, options: any = null) {
|
||||
return super.newMarkupToHtml({
|
||||
public newMarkupToHtml(plugins: PluginStates = null, options: Options = null) {
|
||||
plugins = plugins || {};
|
||||
|
||||
return super.newMarkupToHtml(null, {
|
||||
extraRendererRules: contentScriptsToRendererRules(plugins),
|
||||
...options,
|
||||
});
|
||||
|
||||
@@ -141,8 +141,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097631
|
||||
versionName "1.8.5"
|
||||
versionCode 2097632
|
||||
versionName "2.0.1"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<!-- RN-NOTIFICATION -->
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<!-- /RN-NOTIFICATION -->
|
||||
|
||||
<!-- Make these features optional to enable Chromebooks -->
|
||||
<!-- https://github.com/laurent22/joplin/issues/37 -->
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
@@ -41,35 +35,6 @@
|
||||
android:resizeableActivity="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<!-- RN-NOTIFICATION -->
|
||||
<receiver
|
||||
android:name="com.emekalites.react.alarm.notification.AlarmReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="ACTION_DISMISS" />
|
||||
<action android:name="ACTION_SNOOZE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name="com.emekalites.react.alarm.notification.AlarmDismissReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true" />
|
||||
<receiver
|
||||
android:name="com.emekalites.react.alarm.notification.AlarmBootReceiver"
|
||||
android:directBootAware="true"
|
||||
android:enabled="false"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<!-- /RN-NOTIFICATION -->
|
||||
|
||||
<!--
|
||||
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification
|
||||
Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed.
|
||||
|
||||
@@ -3,8 +3,13 @@ const shared = require('@joplin/lib/components/shared/note-screen-shared');
|
||||
|
||||
export default function useOnMessage(onCheckboxChange: Function, noteBody: string, onMarkForDownload: Function, onJoplinLinkClick: Function, onResourceLongPress: Function) {
|
||||
return useCallback((event: any) => {
|
||||
// Since RN 58 (or 59) messages are now escaped twice???
|
||||
const msg = unescape(unescape(event.nativeEvent.data));
|
||||
// 2021-05-19: Historically this was unescaped twice as it was
|
||||
// apparently needed after an upgrade to RN 58 (or 59). However this is
|
||||
// no longer needed and in fact would break certain URLs so it can be
|
||||
// removed. Keeping the comment here anyway in case we find some URLs
|
||||
// that end up being broken after removing the double unescaping.
|
||||
// https://github.com/laurent22/joplin/issues/4494
|
||||
const msg = event.nativeEvent.data;
|
||||
|
||||
console.info('Got IPC message: ', msg);
|
||||
|
||||
|
||||
@@ -492,7 +492,7 @@
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 10.8.1;
|
||||
MARKETING_VERSION = 20.0.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -519,7 +519,7 @@
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 10.8.1;
|
||||
MARKETING_VERSION = 20.0.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -666,7 +666,7 @@
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 10.8.1;
|
||||
MARKETING_VERSION = 20.0.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
@@ -697,7 +697,7 @@
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 10.8.1;
|
||||
MARKETING_VERSION = 20.0.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -240,7 +240,7 @@ PODS:
|
||||
- React-cxxreact (= 0.63.3)
|
||||
- React-jsi (= 0.63.3)
|
||||
- React-jsinspector (0.63.3)
|
||||
- react-native-alarm-notification (1.7.1):
|
||||
- react-native-alarm-notification (1.0.3):
|
||||
- React
|
||||
- react-native-camera (3.40.0):
|
||||
- React-Core
|
||||
@@ -391,7 +391,7 @@ DEPENDENCIES:
|
||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-alarm-notification (from `../node_modules/react-native-alarm-notification`)
|
||||
- react-native-alarm-notification (from `../node_modules/joplin-rn-alarm-notification`)
|
||||
- react-native-camera (from `../node_modules/react-native-camera`)
|
||||
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
|
||||
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
|
||||
@@ -475,7 +475,7 @@ EXTERNAL SOURCES:
|
||||
React-jsinspector:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
react-native-alarm-notification:
|
||||
:path: "../node_modules/react-native-alarm-notification"
|
||||
:path: "../node_modules/joplin-rn-alarm-notification"
|
||||
react-native-camera:
|
||||
:path: "../node_modules/react-native-camera"
|
||||
react-native-document-picker:
|
||||
@@ -568,7 +568,7 @@ SPEC CHECKSUMS:
|
||||
React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
|
||||
React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
|
||||
React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
|
||||
react-native-alarm-notification: 8f2adb4521bf7ca867278d4c236553e154dbbbc6
|
||||
react-native-alarm-notification: 466e4ad56fbd948ecac26e657f292dca8bf483d5
|
||||
react-native-camera: 5c1fbfecf63b802b8ca4a71c60d30a71550fb348
|
||||
react-native-document-picker: b3e78a8f7fef98b5cb069f20fc35797d55e68e28
|
||||
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@react-native-community/slider": "^3.0.3",
|
||||
"buffer": "^5.0.8",
|
||||
"events": "^3.2.0",
|
||||
"joplin-rn-alarm-notification": "^1.0.3",
|
||||
"jsc-android": "241213.1.0",
|
||||
"md5": "^2.2.1",
|
||||
"prop-types": "^15.6.0",
|
||||
@@ -30,7 +31,6 @@
|
||||
"react": "16.13.1",
|
||||
"react-native": "0.63.3",
|
||||
"react-native-action-button": "^2.8.5",
|
||||
"react-native-alarm-notification": "^1.7.1",
|
||||
"react-native-camera": "^3.40.0",
|
||||
"react-native-dialogbox": "^0.6.10",
|
||||
"react-native-document-picker": "^4.0.0",
|
||||
|
||||
@@ -2,7 +2,7 @@ const React = require('react');
|
||||
import shim from '@joplin/lib/shim';
|
||||
shim.setReact(React);
|
||||
|
||||
import setUpQuickActions from './setUpQuickActions';
|
||||
import setupQuickActions from './setupQuickActions';
|
||||
import PluginAssetsLoader from './PluginAssetsLoader';
|
||||
import AlarmService from '@joplin/lib/services/AlarmService';
|
||||
import Alarm from '@joplin/lib/models/Alarm';
|
||||
@@ -98,6 +98,7 @@ 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';
|
||||
|
||||
let storeDispatch = function(_action: any) {};
|
||||
|
||||
@@ -715,7 +716,9 @@ class AppComponent extends React.Component {
|
||||
|
||||
await this.handleShareData();
|
||||
|
||||
setUpQuickActions(this.props.dispatch, this.props.selectedFolderId);
|
||||
setupQuickActions(this.props.dispatch, this.props.selectedFolderId);
|
||||
|
||||
await setupNotifications(this.props.dispatch);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { Notification } from '@joplin/lib/models/Alarm';
|
||||
|
||||
const ReactNativeAN = require('react-native-alarm-notification').default;
|
||||
const ReactNativeAN = require('joplin-rn-alarm-notification').default;
|
||||
|
||||
export default class AlarmServiceDriver {
|
||||
|
||||
@@ -30,9 +30,11 @@ export default class AlarmServiceDriver {
|
||||
|
||||
// Returns -1 if could not be found
|
||||
private alarmJoplinAlarmId(alarm: any): number {
|
||||
if (!alarm.data) return -1;
|
||||
const m = alarm.data.match(/joplinNotificationId==>(\d+)/);
|
||||
return m ? Number(m[1]) : -1;
|
||||
if (!alarm.data || !alarm.data.joplinNotificationId) {
|
||||
return -1;
|
||||
} else {
|
||||
return alarm.data.joplinNotificationId;
|
||||
}
|
||||
}
|
||||
|
||||
private async alarmByJoplinNotificationId(joplinNotificationId: number) {
|
||||
@@ -51,9 +53,12 @@ export default class AlarmServiceDriver {
|
||||
title: notification.title,
|
||||
message: notification.body ? notification.body : '-', // Required
|
||||
channel: 'net.cozic.joplin.notification',
|
||||
small_icon: 'ic_launcher',
|
||||
color: 'white',
|
||||
data: { joplinNotificationId: `${notification.id}` },
|
||||
small_icon: 'ic_launcher_foreground', // Android requires the icon to be transparent
|
||||
color: 'blue',
|
||||
data: {
|
||||
joplinNotificationId: `${notification.id}`,
|
||||
noteId: notification.noteId,
|
||||
},
|
||||
};
|
||||
|
||||
// ReactNativeAN expects a string as a date and it seems this utility
|
||||
|
||||
37
packages/app-mobile/utils/setupNotifications.ts
Normal file
37
packages/app-mobile/utils/setupNotifications.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
const { NativeEventEmitter, NativeModules, Platform } = require('react-native');
|
||||
|
||||
interface NotificationData {
|
||||
joplinNotificationId: string;
|
||||
noteId: string;
|
||||
}
|
||||
|
||||
export default async (dispatch: Dispatch) => {
|
||||
if (Platform.OS === 'android') {
|
||||
const RNAlarmNotification = NativeModules.RNAlarmNotification;
|
||||
const RNAlarmEmitter = new NativeEventEmitter(RNAlarmNotification);
|
||||
|
||||
const handleNotification = async (notification: NotificationData) => {
|
||||
if (notification) {
|
||||
const noteId = notification.noteId;
|
||||
if (noteId) {
|
||||
dispatch({ type: 'NAV_BACK' });
|
||||
dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||
dispatch({
|
||||
type: 'NAV_GO',
|
||||
noteId: noteId,
|
||||
routeName: 'Note',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// receive notification click events when the app is running
|
||||
RNAlarmEmitter.addListener('OnNotificationOpened', handleNotification);
|
||||
|
||||
// retrieve notification info if the app was started after the user clicked notification
|
||||
const notification = await RNAlarmNotification.getAlarmInfo();
|
||||
await handleNotification(notification);
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"id": "<%= pluginId %>",
|
||||
"app_min_version": "1.8",
|
||||
"app_min_version": "2.0",
|
||||
"version": "1.0.0",
|
||||
"name": "<%= pluginName %>",
|
||||
"description": "<%= pluginDescription %>",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "generator-joplin",
|
||||
"version": "1.8.1",
|
||||
"version": "2.0.0",
|
||||
"description": "Scaffolds out a new Joplin plugin",
|
||||
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
|
||||
"author": {
|
||||
@@ -34,4 +34,4 @@
|
||||
"repository": "https://github.com/laurent22/generator-joplin",
|
||||
"license": "MIT",
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
|
||||
const ArrayUtils = require('@joplin/lib/ArrayUtils');
|
||||
const ArrayUtils = require('./ArrayUtils');
|
||||
|
||||
describe('ArrayUtils', function() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import InMemoryCache from '@joplin/lib/InMemoryCache';
|
||||
import time from '@joplin/lib/time';
|
||||
import InMemoryCache from './InMemoryCache';
|
||||
import time from './time';
|
||||
|
||||
describe('InMemoryCache', function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
const StringUtils = require('@joplin/lib/string-utils');
|
||||
const StringUtils = require('./string-utils');
|
||||
|
||||
describe('StringUtils', function() {
|
||||
|
||||
@@ -28,6 +28,22 @@ interface RemoteItem {
|
||||
type_?: number;
|
||||
}
|
||||
|
||||
function isCannotSyncError(error: any): boolean {
|
||||
if (!error) return false;
|
||||
if (['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) return true;
|
||||
|
||||
// If the request times out we give up too because sometimes it's due to the
|
||||
// file being large or some other connection issues, and we don't want that
|
||||
// file to block the sync process. The user can choose to retry later on.
|
||||
//
|
||||
// message: "network timeout at: .....
|
||||
// name: "FetchError"
|
||||
// type: "request-timeout"
|
||||
if (error.type === 'request-timeout' || error.message.includes('network timeout')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export default class Synchronizer {
|
||||
|
||||
private db_: any;
|
||||
@@ -514,7 +530,7 @@ export default class Synchronizer {
|
||||
|
||||
await this.apiCall('put', remoteContentPath, null, { path: localResourceContentPath, source: 'file', shareId: local.share_id });
|
||||
} catch (error) {
|
||||
if (error && ['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) {
|
||||
if (isCannotSyncError(error)) {
|
||||
await handleCannotSyncItem(ItemClass, syncTargetId, local, error.message);
|
||||
action = null;
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
const { fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
|
||||
const TaskQueue = require('@joplin/lib/TaskQueue.js');
|
||||
const { setupDatabaseAndSynchronizer, sleep, switchClient } = require('./testing/test-utils.js');
|
||||
const TaskQueue = require('./TaskQueue.js');
|
||||
|
||||
describe('TaskQueue', function() {
|
||||
|
||||
22
packages/lib/database.test.js
Normal file
22
packages/lib/database.test.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('./testing/test-utils.js');
|
||||
const BaseModel = require('./BaseModel').default;
|
||||
|
||||
describe('database', function() {
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not modify cached field names', (async () => {
|
||||
const db = BaseModel.db();
|
||||
|
||||
const fieldNames = db.tableFieldNames('notes');
|
||||
const fieldCount = fieldNames.length;
|
||||
fieldNames.push('type_');
|
||||
|
||||
expect(fieldCount).toBeGreaterThan(0);
|
||||
expect(db.tableFieldNames('notes').length).toBe(fieldCount);
|
||||
}));
|
||||
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
const { checkThrow } = require('./test-utils.js');
|
||||
const eventManager = require('@joplin/lib/eventManager').default;
|
||||
const { checkThrow } = require('./testing/test-utils.js');
|
||||
const eventManager = require('./eventManager').default;
|
||||
|
||||
describe('eventManager', function() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import JoplinError from './JoplinError';
|
||||
import JoplinServerApi from './JoplinServerApi';
|
||||
import { trimSlashes } from './path-utils';
|
||||
|
||||
@@ -148,9 +149,17 @@ export default class FileApiDriverJoplinServer {
|
||||
}
|
||||
|
||||
public async put(path: string, content: any, options: any = null) {
|
||||
return this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
}, options);
|
||||
try {
|
||||
const output = await this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
}, options);
|
||||
return output;
|
||||
} catch (error) {
|
||||
if (error.code === 413) {
|
||||
throw new JoplinError(error.message, 'rejectedByTarget');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(path: string) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, fileApi } from './test-utils';
|
||||
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, fileApi } from './testing/test-utils';
|
||||
|
||||
describe('file-api-driver', function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import FsDriverNode from '@joplin/lib/fs-driver-node';
|
||||
import shim from '@joplin/lib/shim';
|
||||
const { expectThrow } = require('./test-utils.js');
|
||||
import FsDriverNode from './fs-driver-node';
|
||||
import shim from './shim';
|
||||
const { expectThrow } = require('./testing/test-utils.js');
|
||||
|
||||
// On Windows, path.resolve is going to convert a path such as
|
||||
// /tmp/file.txt to c:\tmp\file.txt
|
||||
28
packages/lib/htmlUtils.test.ts
Normal file
28
packages/lib/htmlUtils.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { plainTextToHtml } from './htmlUtils';
|
||||
|
||||
describe('htmlUtils', () => {
|
||||
|
||||
test('should convert a plain text string to its HTML equivalent', () => {
|
||||
const testCases = [
|
||||
[
|
||||
'',
|
||||
'',
|
||||
],
|
||||
[
|
||||
'line 1\nline 2',
|
||||
'<p>line 1</p><p>line 2</p>',
|
||||
],
|
||||
[
|
||||
'<img onerror="http://downloadmalware.com"/>',
|
||||
'<img onerror="http://downloadmalware.com"/>',
|
||||
],
|
||||
];
|
||||
|
||||
for (const t of testCases) {
|
||||
const [input, expected] = t;
|
||||
const actual = plainTextToHtml(input);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -2,6 +2,7 @@ const urlUtils = require('./urlUtils.js');
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
const htmlparser2 = require('@joplin/fork-htmlparser2');
|
||||
const { escapeHtml } = require('./string-utils.js');
|
||||
|
||||
// [\s\S] instead of . for multiline matching
|
||||
// https://stackoverflow.com/a/16119722/561309
|
||||
@@ -153,3 +154,16 @@ class HtmlUtils {
|
||||
}
|
||||
|
||||
export default new HtmlUtils();
|
||||
|
||||
export function plainTextToHtml(plainText: string): string {
|
||||
const lines = plainText
|
||||
.replace(/[\n\r]/g, '\n')
|
||||
.split('\n');
|
||||
|
||||
const lineOpenTag = lines.length > 1 ? '<p>' : '';
|
||||
const lineCloseTag = lines.length > 1 ? '</p>' : '';
|
||||
|
||||
return lines
|
||||
.map(line => lineOpenTag + escapeHtml(line) + lineCloseTag)
|
||||
.join('');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import htmlUtils from '@joplin/lib/htmlUtils';
|
||||
import htmlUtils from './htmlUtils';
|
||||
|
||||
describe('htmlUtils', function() {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { NoteEntity, ResourceEntity, TagEntity } from '@joplin/lib/services/database/types';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import { NoteEntity, ResourceEntity, TagEntity } from './services/database/types';
|
||||
import shim from './shim';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const { filename } = require('@joplin/lib/path-utils');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, expectNotThrow } = require('./test-utils.js');
|
||||
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
|
||||
const { importEnex } = require('@joplin/lib/import-enex');
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Tag from '@joplin/lib/models/Tag';
|
||||
import Resource from '@joplin/lib/models/Resource';
|
||||
const { filename } = require('./path-utils');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, expectNotThrow, supportDir } = require('./testing/test-utils.js');
|
||||
const { enexXmlToMd } = require('./import-enex-md-gen.js');
|
||||
const { importEnex } = require('./import-enex');
|
||||
import Note from './models/Note';
|
||||
import Tag from './models/Tag';
|
||||
import Resource from './models/Resource';
|
||||
|
||||
const enexSampleBaseDir = `${__dirname}/enex_to_md`;
|
||||
const enexSampleBaseDir = `${supportDir}/../enex_to_md`;
|
||||
|
||||
describe('EnexToMd', function() {
|
||||
|
||||
@@ -31,6 +31,7 @@ interface Section {
|
||||
interface ParserStateTag {
|
||||
name: string;
|
||||
visible: boolean;
|
||||
isCodeBlock: boolean;
|
||||
}
|
||||
|
||||
interface ParserStateList {
|
||||
@@ -443,6 +444,20 @@ function isInvisibleBlock(context: any, attributes: any) {
|
||||
return display && display.indexOf('none') === 0;
|
||||
}
|
||||
|
||||
function trimBlockOpenAndClose(lines: string[]): string[] {
|
||||
const output = lines.slice();
|
||||
|
||||
while (output.length && [BLOCK_OPEN, BLOCK_CLOSE, ''].includes(output[0])) {
|
||||
output.splice(0, 1);
|
||||
}
|
||||
|
||||
while (output.length && [BLOCK_OPEN, BLOCK_CLOSE, ''].includes(output[output.length - 1])) {
|
||||
output.pop();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function isSpanWithStyle(attributes: any) {
|
||||
if (attributes != undefined) {
|
||||
if ('style' in attributes) {
|
||||
@@ -484,6 +499,16 @@ function displaySaxWarning(context: any, message: string) {
|
||||
console.warn(line.join(': '));
|
||||
}
|
||||
|
||||
function isCodeBlock(context: any, nodeName: string, attributes: any) {
|
||||
if (nodeName === 'code') return true;
|
||||
|
||||
if (attributes && attributes.style) {
|
||||
const enCodeBlock = cssValue(context, attributes.style, '-en-codeblock');
|
||||
if (enCodeBlock && enCodeBlock.toLowerCase() === 'true') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// function removeSectionParent(section:Section | string) {
|
||||
// if (typeof section === 'string') return section;
|
||||
|
||||
@@ -575,11 +600,13 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
|
||||
const nodeAttributes = attributeToLowerCase(node);
|
||||
const n = node.name.toLowerCase();
|
||||
const isVisible = !isInvisibleBlock(this, nodeAttributes);
|
||||
|
||||
state.tags.push({
|
||||
const tagInfo: ParserStateTag = {
|
||||
name: n,
|
||||
visible: isVisible,
|
||||
});
|
||||
isCodeBlock: isCodeBlock(this, n, nodeAttributes),
|
||||
};
|
||||
|
||||
state.tags.push(tagInfo);
|
||||
|
||||
const currentList = state.lists && state.lists.length ? state.lists[state.lists.length - 1] : null;
|
||||
|
||||
@@ -673,6 +700,25 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
|
||||
lines: [],
|
||||
parent: section,
|
||||
};
|
||||
section.lines.push(newSection);
|
||||
section = newSection;
|
||||
} else if (tagInfo.isCodeBlock) {
|
||||
// state.inPre = false;
|
||||
|
||||
// const previousIsPre = state.tags.length ? state.tags[state.tags.length - 1].name === 'pre' : false;
|
||||
// if (previousIsPre) {
|
||||
// section.lines.pop();
|
||||
// }
|
||||
|
||||
state.inCode.push(true);
|
||||
state.currentCode = '';
|
||||
|
||||
const newSection: Section = {
|
||||
type: SectionType.Code,
|
||||
lines: [],
|
||||
parent: section,
|
||||
};
|
||||
|
||||
section.lines.push(newSection);
|
||||
section = newSection;
|
||||
} else if (isBlockTag(n)) {
|
||||
@@ -750,18 +796,6 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
|
||||
} else if (n == 'blockquote') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inQuote = true;
|
||||
} else if (n === 'code') {
|
||||
state.inCode.push(true);
|
||||
state.currentCode = '';
|
||||
|
||||
const newSection: Section = {
|
||||
type: SectionType.Code,
|
||||
lines: [],
|
||||
parent: section,
|
||||
};
|
||||
|
||||
section.lines.push(newSection);
|
||||
section = newSection;
|
||||
} else if (n === 'pre') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inPre = true;
|
||||
@@ -871,6 +905,28 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
|
||||
// End of note
|
||||
} else if (!poppedTag.visible) {
|
||||
if (section && section.parent) section = section.parent;
|
||||
} else if (poppedTag.isCodeBlock) {
|
||||
state.inCode.pop();
|
||||
|
||||
if (!state.inCode.length) {
|
||||
// When a codeblock is wrapped in <pre><code>, it will have
|
||||
// extra empty lines added by the "pre" logic, but since we
|
||||
// are in a codeblock we should actually trim those.
|
||||
const codeLines = trimBlockOpenAndClose(processMdArrayNewLines(section.lines).split('\n'));
|
||||
section.lines = [];
|
||||
if (codeLines.length > 1) {
|
||||
section.lines.push('\n\n```\n');
|
||||
for (let i = 0; i < codeLines.length; i++) {
|
||||
if (i > 0) section.lines.push('\n');
|
||||
section.lines.push(codeLines[i]);
|
||||
}
|
||||
section.lines.push('\n```\n\n');
|
||||
} else {
|
||||
section.lines.push(`\`${markdownUtils.escapeInlineCode(codeLines.join(''))}\``);
|
||||
}
|
||||
|
||||
if (section && section.parent) section = section.parent;
|
||||
}
|
||||
} else if (isNewLineOnlyEndTag(n)) {
|
||||
section.lines.push(BLOCK_CLOSE);
|
||||
} else if (n == 'td' || n == 'th') {
|
||||
@@ -897,23 +953,6 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
|
||||
} else if (n == 'blockquote') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inQuote = false;
|
||||
} else if (n === 'code') {
|
||||
state.inCode.pop();
|
||||
|
||||
if (!state.inCode.length) {
|
||||
const codeLines = processMdArrayNewLines(section.lines).split('\n');
|
||||
section.lines = [];
|
||||
if (codeLines.length > 1) {
|
||||
for (let i = 0; i < codeLines.length; i++) {
|
||||
if (i > 0) section.lines.push('\n');
|
||||
section.lines.push(`\t${codeLines[i]}`);
|
||||
}
|
||||
} else {
|
||||
section.lines.push(`\`${codeLines.join('')}\``);
|
||||
}
|
||||
|
||||
if (section && section.parent) section = section.parent;
|
||||
}
|
||||
} else if (n === 'pre') {
|
||||
state.inPre = false;
|
||||
section.lines.push(BLOCK_CLOSE);
|
||||
|
||||
@@ -10,4 +10,7 @@ module.exports = {
|
||||
],
|
||||
|
||||
testEnvironment: 'node',
|
||||
|
||||
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
|
||||
slowTestThreshold: 40,
|
||||
};
|
||||
|
||||
9
packages/lib/jest.setup.js
Normal file
9
packages/lib/jest.setup.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const { afterEachCleanUp } = require('./testing/test-utils.js');
|
||||
const { shimInit } = require('./shim-init-node.js');
|
||||
const sharp = require('sharp');
|
||||
|
||||
shimInit(sharp, null);
|
||||
|
||||
global.afterEach(async () => {
|
||||
await afterEachCleanUp();
|
||||
});
|
||||
@@ -578,7 +578,7 @@ function localesFromLanguageCode(languageCode: string, locales: string[]): strin
|
||||
});
|
||||
}
|
||||
|
||||
function _(s: string, ...args: any[]) {
|
||||
function _(s: string, ...args: any[]): string {
|
||||
const strings = localeStrings(currentLocale_);
|
||||
let result = strings[s];
|
||||
if (result === '' || result === undefined) result = s;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user