1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-30 20:39:46 +02:00

Compare commits

...

81 Commits

Author SHA1 Message Date
Laurent Cozic
1d035fcb37 Server v2.6.9 2021-11-11 16:38:37 +00:00
Laurent Cozic
4bfb4db5f1 Server: Fixed S3 connection string record in db 2021-11-11 16:36:43 +00:00
Laurent Cozic
4fc3bcbfd7 Server: Fixed S3 storage connection and improved connectiob checks 2021-11-11 16:32:34 +00:00
Laurent Cozic
01826d9eb1 ignore files 2021-11-11 16:31:37 +00:00
Laurent Cozic
73137cfb27 Server: Hide credentials from the log 2021-11-11 16:13:36 +00:00
Helmut K. C. Tessarek
b58ea0f202 Desktop: Add shortcut for bulleted list (#5698)
Ref: https://discourse.joplinapp.org/t/shortcut-for-lists/21646
2021-11-11 15:50:15 +00:00
Kenichi Kobayashi
d7e08770bd Desktop: Resolves #4827, Resolves #5652: Fixed and improve laggy scroll in text editor (#5606) 2021-11-11 15:43:32 +00:00
Kenichi Kobayashi
f495db1391 Desktop: Sort Order Buttons and Per-Notebook Sort Order (#5437) 2021-11-11 15:33:37 +00:00
Kenichi Kobayashi
e4d5e9cefb Desktop: Fixes #5582: Currently opened note is not updated after sync (5582) (#5711) 2021-11-11 15:31:20 +00:00
Laurent Cozic
72f336a5af Server v2.6.8 2021-11-11 15:24:21 +00:00
Laurent Cozic
dfa5f8b357 Server: Fixed S3 configuration 2021-11-11 15:22:30 +00:00
Laurent Cozic
3cdbd6dd15 Server v2.6.7 2021-11-11 14:44:06 +00:00
Laurent Cozic
c6dec0a045 Server: Added command to test a storage connection 2021-11-11 14:42:22 +00:00
Laurent Cozic
4879edc59a Server v2.6.6 2021-11-11 14:06:27 +00:00
Laurent Cozic
005f720f7b Server: Added command to migrate content to different storage 2021-11-11 13:59:05 +00:00
Laurent Cozic
725c79d1ec Desktop: Fixed button to upgrade a master key 2021-11-10 15:40:32 +00:00
Laurent Cozic
0de6e9ed11 All: Fixed issue that could cause application to needlessly lock the sync target 2021-11-10 14:47:26 +00:00
Laurent Cozic
7c3785e89d Doc: Update privacy policy 2021-11-10 12:51:41 +00:00
Laurent Cozic
5c2a0ed033 Doc: Update privacy policy 2021-11-10 12:20:43 +00:00
Laurent Cozic
fc64c8264c Server v2.6.5 2021-11-10 12:08:10 +00:00
Laurent Cozic
08f420ce06 Merge branch 'dev' into release-2.6 2021-11-10 12:07:32 +00:00
Laurent Cozic
cc23a8b70b Server v2.6.4 2021-11-10 12:07:06 +00:00
Laurent Cozic
9ffa29f658 Chore: Add tests for storage driver loading 2021-11-10 12:03:45 +00:00
Laurent Cozic
7431da9f3a Server: Lazy-load storage drivers 2021-11-10 11:48:06 +00:00
Laurent Cozic
4deeed0d5c Desktop, Mobile: Fixes #5687: Fixed issue with parts of HTML notes not being displayed in some cases 2021-11-09 18:33:28 +00:00
Laurent Cozic
f8d9601ff7 Tools: Restored HTML to ENEX tests that had been deleted in a refactoring 2021-11-09 17:49:05 +00:00
Laurent Cozic
89179c2776 Desktop, Cli: Add support for more style of highlighted texts when importing ENEX files 2021-11-09 16:42:50 +00:00
Laurent Cozic
d76646a609 Server v2.6.4 2021-11-09 16:12:29 +00:00
Laurent
485c0d0314 Server: Allow storing item content in database, filesystem or S3 (depending on config) (#5602) 2021-11-09 16:05:42 +00:00
agerardin
6b31609338 Plugins: Allow posting messages from plugin to webview (#5569) 2021-11-09 15:50:50 +00:00
Kenichi Kobayashi
200ba858dd Deskop: Fixes flickering when switching notes while the Note Search Bar is visible (#5548) 2021-11-09 15:47:25 +00:00
Laurent Cozic
7b3ad32103 Update translations 2021-11-09 15:28:38 +00:00
Laurent Cozic
3745cd7cb0 Tools: Do not process context when running build-translation tool 2021-11-09 15:26:45 +00:00
Laurent Cozic
920f2d9655 Revert "Update translations"
This reverts commit f800ca0269.

Reverting for now due to some translations being incorrectly marked as
fuzzy.
2021-11-09 13:49:20 +00:00
Laurent Cozic
f800ca0269 Update translations 2021-11-09 13:16:09 +00:00
Laurent Cozic
33be306d01 Tools: Fixed missing translations when running build-translation tool 2021-11-09 13:14:41 +00:00
Laurent Cozic
3782255c27 Tools: Fixed build-translation script to handle .ts and .tsx files directly 2021-11-09 12:20:07 +00:00
Laurent
0ab235273b Tools: Detect missing translation strings on CI (#5688) 2021-11-08 17:10:33 +00:00
Laurent Cozic
75256613cc Security: Ensure Markdown links that contain single quotes are correctly escaped 2021-11-08 15:39:45 +00:00
Helmut K. C. Tessarek
b328094033 update en_US.po 2021-11-08 10:30:18 -05:00
Helmut K. C. Tessarek
4f0f1af5d1 Update translations 2021-11-08 10:22:16 -05:00
Laurent Cozic
021ce14348 Server v2.6.3 2021-11-08 15:21:20 +00:00
Laurent Cozic
c4017e52dc Fixed a few strings 2021-11-08 10:00:11 +00:00
Laurent Cozic
ec2c1741a2 Tools: Fixed package version numbers 2021-11-07 17:36:48 +00:00
Laurent Cozic
3e5ad0a374 Desktop, Cli: Fixes #5653: Long resource filenames were being incorrectly cut 2021-11-07 16:41:39 +00:00
Laurent Cozic
05e390d48b Tools: Fixed build-translation script 2021-11-07 15:41:04 +00:00
Laurent Cozic
31ce0f46e0 Tools: Show more info in IOS release script 2021-11-07 11:55:34 +00:00
Laurent Cozic
d19551b984 Chore: Set correct iOS version ID 2021-11-07 11:55:11 +00:00
Laurent Cozic
dacd697f80 iOS: Fixes #5671: Fixed iOS 12 crash that prevents the app from starting 2021-11-07 11:53:39 +00:00
Laurent Cozic
70d5c7a648 Server: Set resource content size when viewing published note 2021-11-07 11:47:37 +00:00
Daniel Bretoi
42caab6bde Doc: CLI: update to include local filesystem sync (#5684)
* terminal doc update to include local filesystem sync

* fix typo
2021-11-07 00:32:18 -04:00
NiceYoyo
01b63ad263 All: Translation: Update de_DE.po (#5682)
* Update de_DE.po

* Update de_DE.po
2021-11-07 00:30:04 -04:00
felipeviggiano
ae4013d2f7 All: Translation: Update pt_BR.po (#5680)
* Updates on pt_BR.po file

* Updates on pt_BR.po file
2021-11-07 00:29:38 -04:00
Laurent Cozic
298e85f115 iOS: Set min supported iOS version to 13.0 2021-11-04 16:43:34 +00:00
Laurent Cozic
9e1cb9db2c Server: Immediately ask user to set password after Stripe checkout 2021-11-04 12:49:51 +00:00
Laurent Cozic
373c041aa6 Doc: Fixed H3 header size 2021-11-03 17:39:30 +00:00
Laurent
af19865865 All, Server: Add support for sharing notes when E2EE is enabled (#5529) 2021-11-03 16:24:40 +00:00
Laurent Cozic
a0d23046bf Migration can be null 2021-11-03 15:20:17 +00:00
Laurent Cozic
7ad73df170 Server: Display latest migration name after auto-migration 2021-11-03 15:18:20 +00:00
Laurent Cozic
ce5c5d6042 Server: Disable mailer service if no-reply email is not set 2021-11-03 14:11:13 +00:00
Laurent Cozic
8c6d78e01c Merge branch 'dev' into release-2.6 2021-11-03 12:55:28 +00:00
Laurent Cozic
a65c424233 Server v2.6.2 2021-11-03 12:55:01 +00:00
Laurent Cozic
e6d3396f42 lock files 2021-11-03 12:54:08 +00:00
Laurent Cozic
190550fe8e Tools: Fixed server tests 2021-11-03 12:53:25 +00:00
Laurent Cozic
030b18d7c7 Desktop release v2.6.1 2021-11-03 12:27:39 +00:00
Laurent Cozic
f1bfcfde81 Server v2.6.1 2021-11-03 12:27:28 +00:00
Laurent
47a31c4ef1 All, Server: Add support for faster built-in sync locks (#5662) 2021-11-03 12:26:26 +00:00
Kenichi Kobayashi
630a400181 Desktop: Resolves #2242: Implements Sync-Scroll for Markdown Editor and Viewer (#5512) 2021-11-03 12:10:46 +00:00
Laurent Cozic
725abbc167 Update website 2021-11-03 11:44:43 +00:00
Laurent Cozic
84ec845499 CLI v2.6.1 2021-11-03 11:34:14 +00:00
Laurent Cozic
5892a0678b Releasing sub-packages 2021-11-03 11:18:03 +00:00
Laurent Cozic
cb26ab414d Tools: Trying to fix Lerna publishing with automation token 2021-11-03 11:15:51 +00:00
Laurent Cozic
74fcd474af Releasing sub-packages 2021-11-03 11:10:06 +00:00
Laurent Cozic
c7406f397c Android 2.6.1 2021-11-02 20:56:26 +00:00
Laurent Cozic
d22a29ee3d ios-v12.5.1 2021-11-02 20:19:43 +00:00
Laurent Cozic
bcd568a496 Setup new release 2.6 2021-11-02 20:07:13 +00:00
Laurent Cozic
66e79ccb23 Mobile: Upgraded React Native from 0.64 to 0.66 2021-11-02 16:33:53 +00:00
Laurent Cozic
08ee2b200f Merge branch 'release-2.5' into dev 2021-11-02 14:47:19 +00:00
Laurent Cozic
57b8aa1789 Server v2.5.10 2021-11-02 14:46:52 +00:00
Laurent Cozic
b5d792c606 Server: Improved env variable handling to make it self documenting and enforce type checking 2021-11-02 12:51:59 +00:00
Laurent Cozic
370441333f Server: Improved logging and rendering of low level middleware errors 2021-11-01 19:20:36 +00:00
302 changed files with 225326 additions and 196812 deletions

View File

@@ -333,6 +333,15 @@ packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.js
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.js
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.js.map
packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.d.ts
packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.js
packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.js.map
packages/app-desktop/gui/MainScreen/commands/toggleSideBar.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleSideBar.js
packages/app-desktop/gui/MainScreen/commands/toggleSideBar.js.map
@@ -396,6 +405,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js.
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js.map
@@ -678,6 +690,9 @@ packages/app-desktop/gui/style/StyledTextInput.js.map
packages/app-desktop/gui/utils/NoteListUtils.d.ts
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/NoteListUtils.js.map
packages/app-desktop/gui/utils/SyncScrollMap.d.ts
packages/app-desktop/gui/utils/SyncScrollMap.js
packages/app-desktop/gui/utils/SyncScrollMap.js.map
packages/app-desktop/gui/utils/convertToScreenCoordinates.d.ts
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js.map
@@ -732,6 +747,18 @@ packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js.map
packages/app-desktop/services/share/invitationRespond.d.ts
packages/app-desktop/services/share/invitationRespond.js
packages/app-desktop/services/share/invitationRespond.js.map
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.d.ts
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js.map
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.d.ts
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js.map
packages/app-desktop/services/sortOrder/notesSortOrderUtils.d.ts
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js.map
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.d.ts
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js.map
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.d.ts
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js.map
@@ -1314,6 +1341,9 @@ packages/lib/services/interop/InteropService_Exporter_Jex.js.map
packages/lib/services/interop/InteropService_Exporter_Md.d.ts
packages/lib/services/interop/InteropService_Exporter_Md.js
packages/lib/services/interop/InteropService_Exporter_Md.js.map
packages/lib/services/interop/InteropService_Exporter_Md.test.d.ts
packages/lib/services/interop/InteropService_Exporter_Md.test.js
packages/lib/services/interop/InteropService_Exporter_Md.test.js.map
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.d.ts
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js.map
@@ -1848,6 +1878,9 @@ packages/renderer/MdToHtml/rules/mermaid.js.map
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
packages/renderer/MdToHtml/rules/sanitize_html.js
packages/renderer/MdToHtml/rules/sanitize_html.js.map
packages/renderer/MdToHtml/rules/source_map.d.ts
packages/renderer/MdToHtml/rules/source_map.js
packages/renderer/MdToHtml/rules/source_map.js.map
packages/renderer/MdToHtml/setupLinkify.d.ts
packages/renderer/MdToHtml/setupLinkify.js
packages/renderer/MdToHtml/setupLinkify.js.map

View File

@@ -81,7 +81,7 @@ fi
# release randomly fail.
# =============================================================================
if [ "$IS_PULL_REQUEST" == "1" ]; then
if [ "$IS_PULL_REQUEST" == "1" ] || [ "$IS_DEV_BRANCH" = "1" ]; then
echo "Step: Running linter..."
npm run linter-ci ./
@@ -109,6 +109,27 @@ if [ "$IS_PULL_REQUEST" == "1" ]; then
fi
fi
# =============================================================================
# Check that we didn't lose any string due to gettext not being able to parse
# newly modified or added scripts. This is convenient to quickly view on GitHub
# what commit may have broken translation building. We run this on macOS because
# we need the latest version of gettext (and stable Ubuntu doesn't have it).
# =============================================================================
if [ "$IS_PULL_REQUEST" == "1" ] || [ "$IS_DEV_BRANCH" = "1" ]; then
if [ "$IS_MACOS" == "1" ]; then
echo "Step: Checking for lost translation strings..."
xgettext --version
node packages/tools/build-translation.js --missing-strings-check-only
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
fi
fi
# =============================================================================
# Find out if we should run the build or not. Electron-builder gets stuck when
# building PRs so we disable it in this case. The Linux build should provide
@@ -124,13 +145,12 @@ if [ "$IS_PULL_REQUEST" == "1" ]; then
fi
# =============================================================================
# Prepare the Electron app and build it
# Build the Electron app or Docker image depending on the current tag.
#
# If the current tag is a desktop release tag (starts with "v", such as
# "v1.4.7"), we build and publish to github
#
# Otherwise we only build but don't publish to GitHub. It helps finding
# out any issue in pull requests and dev branch.
# "v1.4.7"), we build and publish to GitHub. Otherwise we only build but don't
# publish to GitHub. It helps finding out any issue in pull requests and dev
# branch.
# =============================================================================
cd "$ROOT_DIR/packages/app-desktop"

View File

@@ -19,6 +19,14 @@ jobs:
sudo apt-get update || true
sudo apt-get install -y gettext
sudo apt-get install -y libsecret-1-dev
sudo apt-get install -y translate-toolkit
- name: Install macOS dependencies
if: runner.os == 'macOS'
run: |
brew update
brew install gettext
brew install translate-toolkit
- name: Install Docker Engine
if: runner.os == 'Linux' && startsWith(github.ref, 'refs/tags/server-v')

33
.gitignore vendored
View File

@@ -316,6 +316,15 @@ packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.js
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.js
packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.js.map
packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.d.ts
packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.js
packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.js.map
packages/app-desktop/gui/MainScreen/commands/toggleSideBar.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleSideBar.js
packages/app-desktop/gui/MainScreen/commands/toggleSideBar.js.map
@@ -379,6 +388,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js.
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js.map
@@ -661,6 +673,9 @@ packages/app-desktop/gui/style/StyledTextInput.js.map
packages/app-desktop/gui/utils/NoteListUtils.d.ts
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/NoteListUtils.js.map
packages/app-desktop/gui/utils/SyncScrollMap.d.ts
packages/app-desktop/gui/utils/SyncScrollMap.js
packages/app-desktop/gui/utils/SyncScrollMap.js.map
packages/app-desktop/gui/utils/convertToScreenCoordinates.d.ts
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js.map
@@ -715,6 +730,18 @@ packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js.map
packages/app-desktop/services/share/invitationRespond.d.ts
packages/app-desktop/services/share/invitationRespond.js
packages/app-desktop/services/share/invitationRespond.js.map
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.d.ts
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js.map
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.d.ts
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js.map
packages/app-desktop/services/sortOrder/notesSortOrderUtils.d.ts
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js.map
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.d.ts
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js.map
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.d.ts
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js.map
@@ -1297,6 +1324,9 @@ packages/lib/services/interop/InteropService_Exporter_Jex.js.map
packages/lib/services/interop/InteropService_Exporter_Md.d.ts
packages/lib/services/interop/InteropService_Exporter_Md.js
packages/lib/services/interop/InteropService_Exporter_Md.js.map
packages/lib/services/interop/InteropService_Exporter_Md.test.d.ts
packages/lib/services/interop/InteropService_Exporter_Md.test.js
packages/lib/services/interop/InteropService_Exporter_Md.test.js.map
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.d.ts
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js.map
@@ -1831,6 +1861,9 @@ packages/renderer/MdToHtml/rules/mermaid.js.map
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
packages/renderer/MdToHtml/rules/sanitize_html.js
packages/renderer/MdToHtml/rules/sanitize_html.js.map
packages/renderer/MdToHtml/rules/source_map.d.ts
packages/renderer/MdToHtml/rules/source_map.js
packages/renderer/MdToHtml/rules/source_map.js.map
packages/renderer/MdToHtml/setupLinkify.d.ts
packages/renderer/MdToHtml/setupLinkify.js
packages/renderer/MdToHtml/setupLinkify.js.map

View File

@@ -188,6 +188,11 @@ h2 {
padding-bottom: 0.5em;
}
h3 {
font-size: 1.3em;
margin-bottom: 0.8rem;
}
.front-page h1 {
font-size: 3em;
}

View File

@@ -22,11 +22,11 @@ Three types of applications are available: for **desktop** (Windows, macOS and L
Operating System | Download
---|---
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-Setup-2.5.8.exe'><img alt='Get it on Windows' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-2.5.8.dmg'><img alt='Get it on macOS' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-2.5.8.AppImage'><img alt='Get it on Linux' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeLinux.png'/></a>
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-Setup-2.5.10.exe'><img alt='Get it on Windows' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-2.5.10.dmg'><img alt='Get it on macOS' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-2.5.10.AppImage'><img alt='Get it on Linux' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeLinux.png'/></a>
**On Windows**, you may also use the <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/JoplinPortable.exe'>Portable version</a>. The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
**On Windows**, you may also use the <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/JoplinPortable.exe'>Portable version</a>. The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
**On Linux**, the recommended way is to use the following installation script as it will handle the desktop icon too:
@@ -36,7 +36,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Jo
Operating System | Download | Alt. Download
---|---|---
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.4.3/joplin-v2.4.3.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.4.3/joplin-v2.4.3-32bit.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.6.1/joplin-v2.6.1.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.6.1/joplin-v2.6.1-32bit.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -64,7 +64,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
# Sponsors
<!-- SPONSORS-ORG -->
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://usrigging.com/"><img title="U.S. Ringing Supply" width="256" src="https://joplinapp.org/images/sponsors/RingingSupply.svg"/></a> <a href="https://tranio.com/italy/"><img title="Tranio" width="256" src="https://joplinapp.org/images/sponsors/Tranio.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-github&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a>
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://usrigging.com/"><img title="U.S. Ringing Supply" width="256" src="https://joplinapp.org/images/sponsors/RingingSupply.svg"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-github&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://tranio.com/italy/"><img title="Tranio" width="256" src="https://joplinapp.org/images/sponsors/Tranio.png"/></a>
<!-- SPONSORS-ORG -->
* * *
@@ -505,47 +505,47 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 99%
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 28%
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | 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) | 71%
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 55%
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 95%
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 27%
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | 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) | 68%
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 54%
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 94%
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | Mustafa Al-Dailemi (dailemi@hotmail.com)Language-Team: | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [marph91](mailto:martin.d@andix.de) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 54%
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 91%
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [MrKanister](mailto:s.robin@tutanota.de) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 52%
<img src="https://joplinapp.org/images/flags/country-4x3/gb.png" width="16px"/> | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
<img src="https://joplinapp.org/images/flags/country-4x3/us.png" width="16px"/> | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Mora](mailto:francisco.m.collao@gmail.com) | 95%
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 31%
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Nicolas Viviani | 100%
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 36%
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | 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%
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Albano Battistella](mailto:albano_battistella@hotmail.com) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 83%
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 86%
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 90%
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | Alexander Dawson | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 67%
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Nicolas Suzuki](mailto:nicolas.suzuki@pm.me) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 62%
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 90%
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 42%
<img src="https://joplinapp.org/images/flags/country-4x3/vi.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 92%
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 80%
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [南宫小骏](mailto:jackytsu@vip.qq.com) | 100%
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [SiderealArt](mailto:nelson22768384@gmail.com) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 100%
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 94%
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Mora](mailto:francisco.m.collao@gmail.com) | 96%
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 30%
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Nicolas Viviani | 96%
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 35%
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | 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) | 94%
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Albano Battistella](mailto:albano_battistella@hotmail.com) | 92%
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 80%
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 83%
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 87%
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | Alexander Dawson | 93%
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 65%
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 86%
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Felipe Viggiano](mailto:felipeviggiano@gmail.com) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 86%
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 60%
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | 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%
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 43%
<img src="https://joplinapp.org/images/flags/country-4x3/vi.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 93%
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 85%
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 78%
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [南宫小骏](mailto:jackytsu@vip.qq.com) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [SiderealArt](mailto:nelson22768384@gmail.com) | 92%
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 91%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Contributors

View File

@@ -291,6 +291,11 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog.md
<p><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=E8JMYD2LQ8MMA&amp;lc=GB&amp;item_name=Joplin+Development&amp;currency_code=EUR&amp;bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-PayPal-green.svg" alt="Donate using PayPal"></a> <a href="https://github.com/sponsors/laurent22/"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/GitHub-Badge.svg" alt="Sponsor on GitHub"></a> <a href="https://www.patreon.com/joplin"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Patreon-Badge.svg" alt="Become a patron"></a> <a href="https://joplinapp.org/donate/#donations"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-IBAN.svg" alt="Donate using IBAN"></a></p>
</div>
<h1>Joplin changelog<a name="joplin-changelog" href="#joplin-changelog" class="heading-anchor">🔗</a></h1>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.10">v2.5.10</a> - 2021-11-01T08:22:42Z<a name="v2-5-10-https-github-com-laurent22-joplin-releases-tag-v2-5-10-2021-11-01t08-22-42z" href="#v2-5-10-https-github-com-laurent22-joplin-releases-tag-v2-5-10-2021-11-01t08-22-42z" class="heading-anchor">🔗</a></h2>
<ul>
<li>Fixed: Fixed crash on certain Linux distributions when importing or exporting a file (6012783)</li>
<li>Fixed: Fixed potential infinite loop when Joplin Server session is invalid (c5569ef)</li>
</ul>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.8">v2.5.8</a> - 2021-10-31T11:38:03Z<a name="v2-5-8-https-github-com-laurent22-joplin-releases-tag-v2-5-8-2021-10-31t11-38-03z" href="#v2-5-8-https-github-com-laurent22-joplin-releases-tag-v2-5-8-2021-10-31t11-38-03z" class="heading-anchor">🔗</a></h2>
<ul>
<li>Improved: Enable safe mode for Markdown editor too (<a href="https://github.com/laurent22/joplin/issues/5593">#5593</a>)</li>

View File

@@ -291,6 +291,11 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog_android.md
<p><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=E8JMYD2LQ8MMA&amp;lc=GB&amp;item_name=Joplin+Development&amp;currency_code=EUR&amp;bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-PayPal-green.svg" alt="Donate using PayPal"></a> <a href="https://github.com/sponsors/laurent22/"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/GitHub-Badge.svg" alt="Sponsor on GitHub"></a> <a href="https://www.patreon.com/joplin"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Patreon-Badge.svg" alt="Become a patron"></a> <a href="https://joplinapp.org/donate/#donations"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-IBAN.svg" alt="Donate using IBAN"></a></p>
</div>
<h1>Joplin Android app changelog<a name="joplin-android-app-changelog" href="#joplin-android-app-changelog" class="heading-anchor">🔗</a></h1>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/android-v2.6.1">android-v2.6.1</a> (Pre-release) - 2021-11-02T20:49:53Z<a name="android-v2-6-1-https-github-com-laurent22-joplin-releases-tag-android-v2-6-1-pre-release-2021-11-02t20-49-53z" href="#android-v2-6-1-https-github-com-laurent22-joplin-releases-tag-android-v2-6-1-pre-release-2021-11-02t20-49-53z" class="heading-anchor">🔗</a></h2>
<ul>
<li>Improved: Upgraded React Native from 0.64 to 0.66 (66e79cc)</li>
<li>Fixed: Fixed potential infinite loop when Joplin Server session is invalid (c5569ef)</li>
</ul>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/android-v2.5.5">android-v2.5.5</a> (Pre-release) - 2021-10-31T11:03:16Z<a name="android-v2-5-5-https-github-com-laurent22-joplin-releases-tag-android-v2-5-5-pre-release-2021-10-31t11-03-16z" href="#android-v2-5-5-https-github-com-laurent22-joplin-releases-tag-android-v2-5-5-pre-release-2021-10-31t11-03-16z" class="heading-anchor">🔗</a></h2>
<ul>
<li>New: Add padding around beta text editor (365e152)</li>

View File

@@ -291,6 +291,20 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog_cli.md
<p><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=E8JMYD2LQ8MMA&amp;lc=GB&amp;item_name=Joplin+Development&amp;currency_code=EUR&amp;bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-PayPal-green.svg" alt="Donate using PayPal"></a> <a href="https://github.com/sponsors/laurent22/"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/GitHub-Badge.svg" alt="Sponsor on GitHub"></a> <a href="https://www.patreon.com/joplin"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Patreon-Badge.svg" alt="Become a patron"></a> <a href="https://joplinapp.org/donate/#donations"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-IBAN.svg" alt="Donate using IBAN"></a></p>
</div>
<h1>Joplin terminal app changelog<a name="joplin-terminal-app-changelog" href="#joplin-terminal-app-changelog" class="heading-anchor">🔗</a></h1>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/cli-v2.6.1">cli-v2.6.1</a> - 2021-11-03T11:33:18Z<a name="cli-v2-6-1-https-github-com-laurent22-joplin-releases-tag-cli-v2-6-1-2021-11-03t11-33-18z" href="#cli-v2-6-1-https-github-com-laurent22-joplin-releases-tag-cli-v2-6-1-2021-11-03t11-33-18z" class="heading-anchor">🔗</a></h2>
<ul>
<li>New: Add support for public-private key pairs and improved master password support (#5438)</li>
<li>New: Added mechanism to migrate default settings to new values (72db8e4)</li>
<li>Improved: Add Markdown + Front Matter exporter/importer (#5465) (#5224 by <a href="https://github.com/CalebJohn">@CalebJohn</a>)</li>
<li>Improved: Ensure that shared notebook children are not deleted when shared, unshared and shared again, and a conflict happens (ccf9882)</li>
<li>Improved: Improved Joplin Server configuration check to better handle disabled accounts (72c1235)</li>
<li>Improved: Improved handling of expired sessions when using Joplin Server (33249ca) (ace1118)</li>
<li>Fixed: Certain attachments were not being automatically deleted (#932)</li>
<li>Fixed: Fix default sync target (4b39d30)</li>
<li>Fixed: Fixed potential infinite loop when Joplin Server session is invalid (c5569ef)</li>
<li>Fixed: Fixed running out of memory when importing large ENEX files (#5543)</li>
<li>Fixed: Ignore newline between quotes while spliting batch (#5540) (#5341 by Kingsley Yung)</li>
</ul>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/cli-v2.4.1">cli-v2.4.1</a> - 2021-09-29T15:28:01Z<a name="cli-v2-4-1-https-github-com-laurent22-joplin-releases-tag-cli-v2-4-1-2021-09-29t15-28-01z" href="#cli-v2-4-1-https-github-com-laurent22-joplin-releases-tag-cli-v2-4-1-2021-09-29t15-28-01z" class="heading-anchor">🔗</a></h2>
<ul>
<li>New: Add a way to disable a master key (7faa58e)</li>

View File

@@ -291,6 +291,11 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog_server.md
<p><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=E8JMYD2LQ8MMA&amp;lc=GB&amp;item_name=Joplin+Development&amp;currency_code=EUR&amp;bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-PayPal-green.svg" alt="Donate using PayPal"></a> <a href="https://github.com/sponsors/laurent22/"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/GitHub-Badge.svg" alt="Sponsor on GitHub"></a> <a href="https://www.patreon.com/joplin"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Patreon-Badge.svg" alt="Become a patron"></a> <a href="https://joplinapp.org/donate/#donations"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/badges/Donate-IBAN.svg" alt="Donate using IBAN"></a></p>
</div>
<h1>Joplin Server Changelog<a name="joplin-server-changelog" href="#joplin-server-changelog" class="heading-anchor">🔗</a></h1>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/server-v2.5.10">server-v2.5.10</a> - 2021-11-02T14:45:54Z<a name="server-v2-5-10-https-github-com-laurent22-joplin-releases-tag-server-v2-5-10-2021-11-02t14-45-54z" href="#server-v2-5-10-https-github-com-laurent22-joplin-releases-tag-server-v2-5-10-2021-11-02t14-45-54z" class="heading-anchor">🔗</a></h2>
<ul>
<li>New: Add unique constraint on name and owner ID of items table (f7a18ba)</li>
<li>Fixed: Fixed issue that could cause server to return empty items in some rare cases (99ea4b7)</li>
</ul>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/server-v2.5.9">server-v2.5.9</a> - 2021-10-28T19:43:41Z<a name="server-v2-5-9-https-github-com-laurent22-joplin-releases-tag-server-v2-5-9-2021-10-28t19-43-41z" href="#server-v2-5-9-https-github-com-laurent22-joplin-releases-tag-server-v2-5-9-2021-10-28t19-43-41z" class="heading-anchor">🔗</a></h2>
<ul>
<li>Improved: Remove session expiration for now (4a2af32)</li>

View File

@@ -134,7 +134,7 @@ Your download of <span class="downloaded-filename">Joplin</span> is in progress.
<div class="get-it-desktop">
<h2>Joplin App for Desktop<a name="joplin-app-for-desktop" href="#joplin-app-for-desktop" class="heading-anchor">🔗</a></h2>
<p>Access your notes on Windows, macOS or Linux.</p>
<!-- DESKTOP-DOWNLOAD-LINKS --><a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-Setup-2.5.8.exe'><img alt='Get it on Windows' width="134px" src='/images/BadgeWindows.png'/></a> <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-2.5.8.dmg'><img alt='Get it on macOS' width="134px" src='/images/BadgeMacOS.png'/></a> <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-2.5.8.AppImage'><img alt='Get it on Linux' width="134px" src='/images/BadgeLinux.png'/></a><!-- DESKTOP-DOWNLOAD-LINKS -->
<!-- DESKTOP-DOWNLOAD-LINKS --><a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-Setup-2.5.10.exe'><img alt='Get it on Windows' width="134px" src='/images/BadgeWindows.png'/></a> <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-2.5.10.dmg'><img alt='Get it on macOS' width="134px" src='/images/BadgeMacOS.png'/></a> <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-2.5.10.AppImage'><img alt='Get it on Linux' width="134px" src='/images/BadgeLinux.png'/></a><!-- DESKTOP-DOWNLOAD-LINKS -->
</div>
<h2>Joplin App for Mobile<a name="joplin-app-for-mobile" href="#joplin-app-for-mobile" class="heading-anchor">🔗</a></h2>
<p>Access your notes on your phone or tablet from the Android and iOS apps.</p>

View File

@@ -309,19 +309,19 @@ https://github.com/laurent22/joplin/blob/dev/README.md
<tbody>
<tr>
<td>Windows (32 and 64-bit)</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-Setup-2.5.8.exe'><img alt='Get it on Windows' width="134px" src='/images/BadgeWindows.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-Setup-2.5.10.exe'><img alt='Get it on Windows' width="134px" src='/images/BadgeWindows.png'/></a></td>
</tr>
<tr>
<td>macOS</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-2.5.8.dmg'><img alt='Get it on macOS' width="134px" src='/images/BadgeMacOS.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-2.5.10.dmg'><img alt='Get it on macOS' width="134px" src='/images/BadgeMacOS.png'/></a></td>
</tr>
<tr>
<td>Linux</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/Joplin-2.5.8.AppImage'><img alt='Get it on Linux' width="134px" src='/images/BadgeLinux.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/Joplin-2.5.10.AppImage'><img alt='Get it on Linux' width="134px" src='/images/BadgeLinux.png'/></a></td>
</tr>
</tbody>
</table>
<p><strong>On Windows</strong>, you may also use the <a href='https://github.com/laurent22/joplin/releases/download/v2.5.8/JoplinPortable.exe'>Portable version</a>. The <a href="https://en.wikipedia.org/wiki/Portable_application">portable application</a> allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called &quot;JoplinProfile&quot; next to the executable file.</p>
<p><strong>On Windows</strong>, you may also use the <a href='https://github.com/laurent22/joplin/releases/download/v2.5.10/JoplinPortable.exe'>Portable version</a>. The <a href="https://en.wikipedia.org/wiki/Portable_application">portable application</a> allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called &quot;JoplinProfile&quot; next to the executable file.</p>
<p><strong>On Linux</strong>, the recommended way is to use the following installation script as it will handle the desktop icon too:</p>
<pre><code style="word-break: break-all">wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh | bash</code></pre>
<h2>Mobile applications<a name="mobile-applications" href="#mobile-applications" class="heading-anchor">🔗</a></h2>
@@ -337,7 +337,7 @@ https://github.com/laurent22/joplin/blob/dev/README.md
<tr>
<td>Android</td>
<td><a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='/images/BadgeAndroid.png'/></a></td>
<td>or download the APK file: <a href="https://github.com/laurent22/joplin-android/releases/download/android-v2.4.3/joplin-v2.4.3.apk">64-bit</a> <a href="https://github.com/laurent22/joplin-android/releases/download/android-v2.4.3/joplin-v2.4.3-32bit.apk">32-bit</a></td>
<td>or download the APK file: <a href="https://github.com/laurent22/joplin-android/releases/download/android-v2.6.1/joplin-v2.6.1.apk">64-bit</a> <a href="https://github.com/laurent22/joplin-android/releases/download/android-v2.6.1/joplin-v2.6.1-32bit.apk">32-bit</a></td>
</tr>
<tr>
<td>iOS</td>
@@ -383,7 +383,7 @@ https://github.com/laurent22/joplin/blob/dev/README.md
<p>The Web Clipper is a browser extension that allows you to save web pages and screenshots from your browser. For more information on how to install and use it, see the <a href="/clipper/">Web Clipper Help Page</a>.</p>
<h1>Sponsors<a name="sponsors" href="#sponsors" class="heading-anchor">🔗</a></h1>
<!-- SPONSORS-ORG -->
<p><a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://usrigging.com/"><img title="U.S. Ringing Supply" width="256" src="https://joplinapp.org/images/sponsors/RingingSupply.svg"/></a> <a href="https://tranio.com/italy/"><img title="Tranio" width="256" src="https://joplinapp.org/images/sponsors/Tranio.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-github&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a></p>
<p><a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://usrigging.com/"><img title="U.S. Ringing Supply" width="256" src="https://joplinapp.org/images/sponsors/RingingSupply.svg"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-github&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://tranio.com/italy/"><img title="Tranio" width="256" src="https://joplinapp.org/images/sponsors/Tranio.png"/></a></p>
<!-- SPONSORS-ORG -->
<hr>
<!-- SPONSORS-GITHUB -->

View File

@@ -568,10 +568,10 @@
<br />
<div class="text-center sponsors-org">
<a class="sponsor-org-item" href="https:&#x2F;&#x2F;usrigging.com&#x2F;"><img title="U.S. Ringing Supply" src="&#x2F;images/sponsors/RingingSupply.svg"></a>
<a class="sponsor-org-item" href="https:&#x2F;&#x2F;www.hosting.de&#x2F;nextcloud&#x2F;?mtm_campaign&#x3D;managed-nextcloud&amp;mtm_kwd&#x3D;joplinapp&amp;mtm_source&#x3D;joplinapp-webseite&amp;mtm_medium&#x3D;banner"><img title="Hosting.de" src="&#x2F;images/sponsors/HostingDe.png"></a>
<a class="sponsor-org-item" href="https:&#x2F;&#x2F;tranio.com&#x2F;italy&#x2F;"><img title="Tranio" src="&#x2F;images/sponsors/Tranio.png"></a>
<a class="sponsor-org-item" href="https:&#x2F;&#x2F;seirei.ne.jp"><img title="Serei Network" src="&#x2F;images/sponsors/SeireiNetwork.png"></a>
<a class="sponsor-org-item" href="https:&#x2F;&#x2F;www.hosting.de&#x2F;nextcloud&#x2F;?mtm_campaign&#x3D;managed-nextcloud&amp;mtm_kwd&#x3D;joplinapp&amp;mtm_source&#x3D;joplinapp-webseite&amp;mtm_medium&#x3D;banner"><img title="Hosting.de" src="&#x2F;images/sponsors/HostingDe.png"></a>
<a class="sponsor-org-item" href="https:&#x2F;&#x2F;usrigging.com&#x2F;"><img title="U.S. Ringing Supply" src="&#x2F;images/sponsors/RingingSupply.svg"></a>
</div>
<div class="text-center sponsors-github">

View File

@@ -301,15 +301,15 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tbody>
<tr>
<td>Total Windows downloads</td>
<td>1,827,011</td>
<td>1,853,529</td>
</tr>
<tr>
<td>Total macOs downloads</td>
<td>715,041</td>
<td>729,364</td>
</tr>
<tr>
<td>Total Linux downloads</td>
<td>596,105</td>
<td>600,066</td>
</tr>
<tr>
<td>Windows %</td>
@@ -339,276 +339,284 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
</thead>
<tbody>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.10">v2.5.10</a></td>
<td>2021-11-01T08:22:42Z</td>
<td>13,675</td>
<td>7,900</td>
<td>1,724</td>
<td>23,299</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.8">v2.5.8</a></td>
<td>2021-10-31T11:38:03Z</td>
<td>1</td>
<td>2</td>
<td>0</td>
<td>3</td>
<td>11,979</td>
<td>6,264</td>
<td>2,144</td>
<td>20,387</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.7">v2.5.7</a> (p)</td>
<td>2021-10-29T14:47:33Z</td>
<td>466</td>
<td>167</td>
<td>120</td>
<td>753</td>
<td>504</td>
<td>181</td>
<td>123</td>
<td>808</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.6">v2.5.6</a> (p)</td>
<td>2021-10-28T22:03:09Z</td>
<td>406</td>
<td>148</td>
<td>76</td>
<td>630</td>
<td>437</td>
<td>149</td>
<td>79</td>
<td>665</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.4">v2.5.4</a> (p)</td>
<td>2021-10-19T10:10:54Z</td>
<td>1,572</td>
<td>522</td>
<td>508</td>
<td>2,602</td>
<td>1,616</td>
<td>536</td>
<td>523</td>
<td>2,675</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.12">v2.4.12</a></td>
<td>2021-10-13T17:24:34Z</td>
<td>42,267</td>
<td>19,724</td>
<td>9,652</td>
<td>71,643</td>
<td>42,599</td>
<td>19,831</td>
<td>9,679</td>
<td>72,109</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.5.1">v2.5.1</a> (p)</td>
<td>2021-10-02T09:51:58Z</td>
<td>2,772</td>
<td>2,794</td>
<td>870</td>
<td>913</td>
<td>4,555</td>
<td>4,577</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.9">v2.4.9</a></td>
<td>2021-09-29T19:08:58Z</td>
<td>55,191</td>
<td>23,123</td>
<td>15,733</td>
<td>94,047</td>
<td>55,238</td>
<td>23,129</td>
<td>15,738</td>
<td>94,105</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.8">v2.4.8</a> (p)</td>
<td>2021-09-22T19:01:46Z</td>
<td>6,770</td>
<td>6,790</td>
<td>1,745</td>
<td>501</td>
<td>9,016</td>
<td>9,036</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.7">v2.4.7</a> (p)</td>
<td>2021-09-19T12:53:22Z</td>
<td>920</td>
<td>937</td>
<td>229</td>
<td>177</td>
<td>1,326</td>
<td>1,343</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.6">v2.4.6</a> (p)</td>
<td>2021-09-09T18:57:17Z</td>
<td>1,512</td>
<td>1,516</td>
<td>435</td>
<td>487</td>
<td>2,434</td>
<td>488</td>
<td>2,439</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.5">v2.4.5</a> (p)</td>
<td>2021-09-06T18:03:28Z</td>
<td>991</td>
<td>996</td>
<td>245</td>
<td>201</td>
<td>1,437</td>
<td>1,442</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.4">v2.4.4</a> (p)</td>
<td>2021-08-30T16:02:51Z</td>
<td>1,279</td>
<td>1,282</td>
<td>354</td>
<td>334</td>
<td>1,967</td>
<td>1,970</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.3">v2.4.3</a> (p)</td>
<td>2021-08-28T15:27:32Z</td>
<td>785</td>
<td>791</td>
<td>178</td>
<td>142</td>
<td>1,105</td>
<td>1,111</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.2">v2.4.2</a> (p)</td>
<td>2021-08-27T17:13:21Z</td>
<td>501</td>
<td>504</td>
<td>123</td>
<td>69</td>
<td>693</td>
<td>696</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.4.1">v2.4.1</a> (p)</td>
<td>2021-08-21T11:52:30Z</td>
<td>1,391</td>
<td>1,394</td>
<td>349</td>
<td>311</td>
<td>2,051</td>
<td>2,054</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.3.5">v2.3.5</a></td>
<td>2021-08-17T06:43:30Z</td>
<td>80,483</td>
<td>31,303</td>
<td>32,969</td>
<td>144,755</td>
<td>80,493</td>
<td>31,304</td>
<td>32,978</td>
<td>144,775</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.3.3">v2.3.3</a></td>
<td>2021-08-14T09:19:40Z</td>
<td>14,076</td>
<td>14,081</td>
<td>6,844</td>
<td>4,022</td>
<td>24,942</td>
<td>24,947</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.2.7">v2.2.7</a></td>
<td>2021-08-11T11:03:26Z</td>
<td>14,564</td>
<td>7,466</td>
<td>14,570</td>
<td>7,467</td>
<td>2,552</td>
<td>24,582</td>
<td>24,589</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.2.6">v2.2.6</a> (p)</td>
<td>2021-08-09T19:29:20Z</td>
<td>7,369</td>
<td>7,373</td>
<td>4,596</td>
<td>934</td>
<td>12,899</td>
<td>12,903</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.2.5">v2.2.5</a> (p)</td>
<td>2021-08-07T10:35:24Z</td>
<td>1,098</td>
<td>1,101</td>
<td>253</td>
<td>183</td>
<td>1,534</td>
<td>1,537</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.2.4">v2.2.4</a> (p)</td>
<td>2021-08-05T16:42:48Z</td>
<td>804</td>
<td>808</td>
<td>183</td>
<td>109</td>
<td>1,096</td>
<td>1,100</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.2.2">v2.2.2</a> (p)</td>
<td>2021-07-19T10:28:35Z</td>
<td>2,717</td>
<td>2,720</td>
<td>713</td>
<td>623</td>
<td>4,053</td>
<td>4,056</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.1.9">v2.1.9</a></td>
<td>2021-07-19T10:28:43Z</td>
<td>45,587</td>
<td>45,591</td>
<td>18,744</td>
<td>16,668</td>
<td>80,999</td>
<td>81,003</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.2.1">v2.2.1</a> (p)</td>
<td>2021-07-09T17:38:25Z</td>
<td>2,058</td>
<td>392</td>
<td>369</td>
<td>2,819</td>
<td>2,064</td>
<td>393</td>
<td>370</td>
<td>2,827</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.1.8">v2.1.8</a></td>
<td>2021-07-03T08:25:16Z</td>
<td>29,383</td>
<td>12,142</td>
<td>12,657</td>
<td>54,182</td>
<td>29,392</td>
<td>12,143</td>
<td>12,658</td>
<td>54,193</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.1.7">v2.1.7</a></td>
<td>2021-06-26T19:48:55Z</td>
<td>13,479</td>
<td>13,484</td>
<td>6,373</td>
<td>3,598</td>
<td>23,450</td>
<td>23,455</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.1.5">v2.1.5</a> (p)</td>
<td>2021-06-23T15:08:52Z</td>
<td>1,143</td>
<td>1,146</td>
<td>223</td>
<td>174</td>
<td>1,540</td>
<td>1,543</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.1.3">v2.1.3</a> (p)</td>
<td>2021-06-19T16:32:51Z</td>
<td>1,287</td>
<td>1,290</td>
<td>287</td>
<td>192</td>
<td>1,766</td>
<td>1,769</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.11">v2.0.11</a></td>
<td>2021-06-16T17:55:49Z</td>
<td>22,689</td>
<td>22,696</td>
<td>9,209</td>
<td>9,768</td>
<td>41,666</td>
<td>41,673</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.10">v2.0.10</a></td>
<td>2021-06-16T07:58:29Z</td>
<td>2,124</td>
<td>2,127</td>
<td>909</td>
<td>359</td>
<td>3,392</td>
<td>3,395</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.9">v2.0.9</a> (p)</td>
<td>2021-06-12T09:30:30Z</td>
<td>1,191</td>
<td>1,194</td>
<td>284</td>
<td>870</td>
<td>2,345</td>
<td>2,348</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.8">v2.0.8</a> (p)</td>
<td>2021-06-10T16:15:08Z</td>
<td>828</td>
<td>831</td>
<td>218</td>
<td>564</td>
<td>1,610</td>
<td>1,613</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.4">v2.0.4</a> (p)</td>
<td>2021-06-02T12:54:17Z</td>
<td>1,371</td>
<td>1,372</td>
<td>383</td>
<td>368</td>
<td>2,122</td>
<td>2,123</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.2">v2.0.2</a> (p)</td>
<td>2021-05-21T18:07:48Z</td>
<td>2,352</td>
<td>2,355</td>
<td>484</td>
<td>1,657</td>
<td>4,493</td>
<td>4,496</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v2.0.1">v2.0.1</a> (p)</td>
@@ -621,58 +629,58 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.8.5">v1.8.5</a></td>
<td>2021-05-10T11:58:14Z</td>
<td>37,377</td>
<td>16,228</td>
<td>19,369</td>
<td>72,974</td>
<td>37,386</td>
<td>16,229</td>
<td>19,370</td>
<td>72,985</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.8.4">v1.8.4</a> (p)</td>
<td>2021-05-09T18:05:05Z</td>
<td>874</td>
<td>877</td>
<td>130</td>
<td>448</td>
<td>1,452</td>
<td>1,455</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.8.3">v1.8.3</a> (p)</td>
<td>2021-05-04T10:38:16Z</td>
<td>1,630</td>
<td>1,634</td>
<td>300</td>
<td>930</td>
<td>2,860</td>
<td>2,864</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.8.2">v1.8.2</a> (p)</td>
<td>2021-04-25T10:50:51Z</td>
<td>1,860</td>
<td>1,866</td>
<td>431</td>
<td>1,276</td>
<td>3,567</td>
<td>3,573</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.8.1">v1.8.1</a> (p)</td>
<td>2021-03-29T10:46:41Z</td>
<td>3,254</td>
<td>3,257</td>
<td>820</td>
<td>2,441</td>
<td>6,515</td>
<td>6,518</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.7.11">v1.7.11</a></td>
<td>2021-02-03T12:50:01Z</td>
<td>115,205</td>
<td>115,220</td>
<td>42,711</td>
<td>64,219</td>
<td>222,135</td>
<td>222,150</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.7.10">v1.7.10</a></td>
<td>2021-01-30T13:25:29Z</td>
<td>13,920</td>
<td>13,922</td>
<td>4,845</td>
<td>4,456</td>
<td>23,221</td>
<td>23,223</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.7.9">v1.7.9</a> (p)</td>
@@ -709,10 +717,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.6.8">v1.6.8</a></td>
<td>2021-01-20T18:11:34Z</td>
<td>18,813</td>
<td>18,825</td>
<td>7,682</td>
<td>7,594</td>
<td>34,089</td>
<td>34,101</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.7.3">v1.7.3</a> (p)</td>
@@ -725,26 +733,26 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.6.7">v1.6.7</a></td>
<td>2021-01-11T23:20:33Z</td>
<td>10,889</td>
<td>10,895</td>
<td>4,630</td>
<td>4,540</td>
<td>20,059</td>
<td>20,065</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.6.6">v1.6.6</a></td>
<td>2021-01-09T16:15:31Z</td>
<td>12,465</td>
<td>12,467</td>
<td>3,415</td>
<td>4,792</td>
<td>20,672</td>
<td>20,674</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.6.5">v1.6.5</a> (p)</td>
<td>2021-01-09T01:24:32Z</td>
<td>943</td>
<td>949</td>
<td>71</td>
<td>305</td>
<td>1,319</td>
<td>1,325</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.6.4">v1.6.4</a> (p)</td>
@@ -765,10 +773,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.5.14">v1.5.14</a></td>
<td>2020-12-30T01:48:46Z</td>
<td>11,360</td>
<td>11,369</td>
<td>5,201</td>
<td>5,521</td>
<td>22,082</td>
<td>22,091</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.6.1">v1.6.1</a> (p)</td>
@@ -822,9 +830,9 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.5.8">v1.5.8</a> (p)</td>
<td>2020-12-20T09:45:19Z</td>
<td>563</td>
<td>161</td>
<td>163</td>
<td>638</td>
<td>1,362</td>
<td>1,364</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.5.7">v1.5.7</a> (p)</td>
@@ -845,18 +853,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.19">v1.4.19</a></td>
<td>2020-12-01T11:11:16Z</td>
<td>26,074</td>
<td>13,412</td>
<td>11,652</td>
<td>51,138</td>
<td>26,084</td>
<td>13,414</td>
<td>11,654</td>
<td>51,152</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.18">v1.4.18</a></td>
<td>2020-11-28T12:21:41Z</td>
<td>11,232</td>
<td>11,235</td>
<td>3,877</td>
<td>3,120</td>
<td>18,229</td>
<td>3,123</td>
<td>18,235</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.16">v1.4.16</a></td>
@@ -877,18 +885,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.12">v1.4.12</a></td>
<td>2020-11-23T18:58:07Z</td>
<td>3,014</td>
<td>3,015</td>
<td>1,323</td>
<td>1,298</td>
<td>5,635</td>
<td>5,636</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.11">v1.4.11</a> (p)</td>
<td>2020-11-19T23:06:51Z</td>
<td>1,414</td>
<td>1,421</td>
<td>154</td>
<td>589</td>
<td>2,157</td>
<td>2,164</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.10">v1.4.10</a> (p)</td>
@@ -901,10 +909,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.9">v1.4.9</a> (p)</td>
<td>2020-11-11T14:23:17Z</td>
<td>623</td>
<td>625</td>
<td>139</td>
<td>400</td>
<td>1,162</td>
<td>1,164</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.7">v1.4.7</a> (p)</td>
@@ -917,10 +925,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.18">v1.3.18</a></td>
<td>2020-11-06T12:07:02Z</td>
<td>31,303</td>
<td>31,314</td>
<td>11,331</td>
<td>10,510</td>
<td>53,144</td>
<td>53,155</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.17">v1.3.17</a> (p)</td>
@@ -933,18 +941,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.6">v1.4.6</a> (p)</td>
<td>2020-11-05T22:44:12Z</td>
<td>450</td>
<td>451</td>
<td>91</td>
<td>52</td>
<td>593</td>
<td>594</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.15">v1.3.15</a></td>
<td>2020-11-04T12:22:50Z</td>
<td>2,354</td>
<td>2,355</td>
<td>1,296</td>
<td>844</td>
<td>4,494</td>
<td>4,495</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.11">v1.3.11</a> (p)</td>
@@ -1021,10 +1029,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.6">v1.2.6</a></td>
<td>2020-10-09T13:56:59Z</td>
<td>44,885</td>
<td>44,894</td>
<td>17,729</td>
<td>14,040</td>
<td>76,654</td>
<td>76,663</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.4">v1.2.4</a> (p)</td>
@@ -1045,18 +1053,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.2">v1.2.2</a> (p)</td>
<td>2020-09-22T20:31:55Z</td>
<td>893</td>
<td>895</td>
<td>204</td>
<td>637</td>
<td>1,734</td>
<td>1,736</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.1.4">v1.1.4</a></td>
<td>2020-09-21T11:20:09Z</td>
<td>27,748</td>
<td>13,499</td>
<td>27,750</td>
<td>13,500</td>
<td>7,750</td>
<td>48,997</td>
<td>49,000</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.1.3">v1.1.3</a> (p)</td>
@@ -1085,10 +1093,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.245">v1.0.245</a></td>
<td>2020-09-09T12:56:10Z</td>
<td>21,493</td>
<td>21,498</td>
<td>10,007</td>
<td>5,639</td>
<td>37,139</td>
<td>37,144</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.242">v1.0.242</a></td>
@@ -1101,18 +1109,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.241">v1.0.241</a></td>
<td>2020-09-04T18:06:00Z</td>
<td>24,310</td>
<td>24,325</td>
<td>5,797</td>
<td>5,047</td>
<td>35,154</td>
<td>35,169</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.239">v1.0.239</a> (p)</td>
<td>2020-09-01T21:56:36Z</td>
<td>715</td>
<td>716</td>
<td>230</td>
<td>403</td>
<td>1,348</td>
<td>1,349</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.237">v1.0.237</a> (p)</td>
@@ -1133,26 +1141,26 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.235">v1.0.235</a> (p)</td>
<td>2020-08-18T22:08:01Z</td>
<td>1,787</td>
<td>1,788</td>
<td>493</td>
<td>924</td>
<td>3,204</td>
<td>3,205</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.234">v1.0.234</a> (p)</td>
<td>2020-08-17T23:13:02Z</td>
<td>554</td>
<td>555</td>
<td>129</td>
<td>102</td>
<td>785</td>
<td>786</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.233">v1.0.233</a></td>
<td>2020-08-01T14:51:15Z</td>
<td>43,801</td>
<td>18,197</td>
<td>43,812</td>
<td>18,198</td>
<td>12,361</td>
<td>74,359</td>
<td>74,371</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.232">v1.0.232</a> (p)</td>
@@ -1165,10 +1173,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.227">v1.0.227</a></td>
<td>2020-07-07T20:44:54Z</td>
<td>40,714</td>
<td>40,716</td>
<td>15,282</td>
<td>9,634</td>
<td>65,630</td>
<td>65,632</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.226">v1.0.226</a> (p)</td>
@@ -1205,10 +1213,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.220">v1.0.220</a></td>
<td>2020-06-13T18:26:22Z</td>
<td>32,011</td>
<td>32,013</td>
<td>9,924</td>
<td>6,416</td>
<td>48,351</td>
<td>48,353</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.218">v1.0.218</a></td>
@@ -1229,10 +1237,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.216">v1.0.216</a></td>
<td>2020-05-24T14:21:01Z</td>
<td>37,782</td>
<td>37,790</td>
<td>14,281</td>
<td>10,184</td>
<td>62,247</td>
<td>62,255</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.214">v1.0.214</a> (p)</td>
@@ -1277,10 +1285,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.201">v1.0.201</a></td>
<td>2020-04-15T22:55:13Z</td>
<td>53,771</td>
<td>53,778</td>
<td>20,049</td>
<td>18,182</td>
<td>92,002</td>
<td>92,009</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.200">v1.0.200</a></td>
@@ -1301,18 +1309,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.197">v1.0.197</a></td>
<td>2020-03-30T17:21:22Z</td>
<td>22,446</td>
<td>9,561</td>
<td>5,835</td>
<td>37,842</td>
<td>22,454</td>
<td>9,568</td>
<td>5,843</td>
<td>37,865</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.195">v1.0.195</a></td>
<td>2020-03-22T19:56:12Z</td>
<td>18,993</td>
<td>18,994</td>
<td>7,953</td>
<td>4,508</td>
<td>31,454</td>
<td>31,455</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.194">v1.0.194</a> (p)</td>
@@ -1325,10 +1333,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.193">v1.0.193</a></td>
<td>2020-03-08T08:58:53Z</td>
<td>28,668</td>
<td>28,669</td>
<td>10,911</td>
<td>7,400</td>
<td>46,979</td>
<td>46,980</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.192">v1.0.192</a> (p)</td>
@@ -1365,10 +1373,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.179">v1.0.179</a></td>
<td>2020-01-24T22:42:41Z</td>
<td>71,286</td>
<td>28,604</td>
<td>22,547</td>
<td>122,437</td>
<td>71,290</td>
<td>28,605</td>
<td>22,548</td>
<td>122,443</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.178">v1.0.178</a></td>
@@ -1397,18 +1405,18 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.175">v1.0.175</a></td>
<td>2019-12-08T11:48:47Z</td>
<td>72,809</td>
<td>72,811</td>
<td>16,925</td>
<td>16,532</td>
<td>106,266</td>
<td>16,539</td>
<td>106,275</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.174">v1.0.174</a></td>
<td>2019-11-12T18:20:58Z</td>
<td>30,482</td>
<td>30,484</td>
<td>11,733</td>
<td>8,223</td>
<td>50,438</td>
<td>50,440</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.173">v1.0.173</a></td>
@@ -1421,10 +1429,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.170">v1.0.170</a></td>
<td>2019-10-13T22:13:04Z</td>
<td>27,576</td>
<td>27,579</td>
<td>8,759</td>
<td>7,679</td>
<td>44,014</td>
<td>44,017</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.169">v1.0.169</a></td>
@@ -1575,8 +1583,8 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>2019-03-10T20:59:58Z</td>
<td>13,634</td>
<td>4,175</td>
<td>3,280</td>
<td>21,089</td>
<td>3,282</td>
<td>21,091</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.139">v1.0.139</a> (p)</td>
@@ -1605,10 +1613,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.135">v1.0.135</a></td>
<td>2019-02-27T23:36:57Z</td>
<td>12,552</td>
<td>12,553</td>
<td>3,963</td>
<td>4,079</td>
<td>20,594</td>
<td>20,595</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.134">v1.0.134</a></td>
@@ -1743,8 +1751,8 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>2018-09-16T19:51:07Z</td>
<td>7,155</td>
<td>2,141</td>
<td>1,710</td>
<td>11,006</td>
<td>1,711</td>
<td>11,007</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.106">v1.0.106</a></td>
@@ -1869,10 +1877,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.83">v1.0.83</a></td>
<td>2018-04-04T19:43:58Z</td>
<td>5,043</td>
<td>5,047</td>
<td>2,536</td>
<td>2,661</td>
<td>10,240</td>
<td>10,244</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.82">v1.0.82</a></td>
@@ -2151,8 +2159,8 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>2017-11-24T14:27:49Z</td>
<td>152</td>
<td>701</td>
<td>6,515</td>
<td>7,368</td>
<td>6,516</td>
<td>7,369</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v0.10.23">v0.10.23</a></td>
@@ -2191,8 +2199,8 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>2017-11-20T18:59:48Z</td>
<td>23</td>
<td>650</td>
<td>18</td>
<td>691</td>
<td>20</td>
<td>693</td>
</tr>
</tbody>
</table>

View File

@@ -2,26 +2,24 @@ const gulp = require('gulp');
const utils = require('./packages/tools/gulp/utils');
const tasks = {
// copyLib: require('./packages/tools/gulp/tasks/copyLib'),
// tsc: require('./packages/tools/gulp/tasks/tsc'),
updateIgnoredTypeScriptBuild: require('./packages/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
buildCommandIndex: require('./packages/tools/gulp/tasks/buildCommandIndex'),
// deleteBuildDirs: require('./packages/tools/gulp/tasks/deleteBuildDirs'),
completePublishAll: {
fn: async () => {
// await utils.execCommandVerbose('git pull');
await utils.execCommandVerbose('git', ['add', '-A']);
await utils.execCommandVerbose('git', ['commit', '-m', 'Releasing sub-packages']);
await utils.execCommandVerbose('lerna', ['publish', 'from-package', '-y']);
// Lerna does some unnecessary auth check that doesn't work with
// automation tokens, thus the --no-verify-access. Automation token
// is still used for access when publishing even with this flag
// (publishing would fail otherwise).
// https://github.com/lerna/lerna/issues/2788
await utils.execCommandVerbose('lerna', ['publish', 'from-package', '-y', '--no-verify-access']);
await utils.execCommandVerbose('git', ['push']);
},
},
};
utils.registerGulpTasks(gulp, tasks);
// gulp.task('build', gulp.series('copyLib', 'tsc', 'updateIgnoredTypeScriptBuild'));
// // The clean task removes build directories and copy back the library. This is useful
// // when switching from one branch to another.
// gulp.task('clean', gulp.series('deleteBuildDirs', 'copyLib'));

View File

@@ -1,12 +1,12 @@
{
"name": "joplin",
"version": "2.5.0",
"version": "2.6.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "joplin",
"version": "2.5.0",
"version": "2.6.1",
"license": "MIT",
"dependencies": {
"aws-sdk": "^2.588.0",

View File

@@ -33,7 +33,7 @@
],
"owner": "Laurent Cozic"
},
"version": "2.5.0",
"version": "2.6.1",
"bin": {
"joplin": "./main.js"
},
@@ -41,8 +41,8 @@
"node": ">=10.0.0"
},
"dependencies": {
"@joplin/lib": "~2.5",
"@joplin/renderer": "~2.5",
"@joplin/lib": "~2.6",
"@joplin/renderer": "~2.6",
"aws-sdk": "^2.588.0",
"chalk": "^4.1.0",
"compare-version": "^0.1.2",
@@ -68,7 +68,7 @@
"yargs-parser": "^7.0.0"
},
"devDependencies": {
"@joplin/tools": "~2.5",
"@joplin/tools": "~2.6",
"@types/fs-extra": "^9.0.6",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",

View File

@@ -234,4 +234,18 @@ describe('MdToHtml', function() {
}
}));
it('should return attributes of line numbers', (async () => {
const mdToHtml = newTestMdToHtml();
// Mapping information between source lines and html elements is
// annotated.
{
const input = '# Head\nFruits\n- Apple\n';
const result = await mdToHtml.render(input, null, { bodyOnly: true, mapsToLine: true });
expect(result.html.trim()).toBe('<h1 id="head" class="maps-to-line" source-line="0">Head</h1>\n' +
'<p class="maps-to-line" source-line="1">Fruits</p>\n' +
'<ul>\n<li class="maps-to-line" source-line="2">Apple</li>\n</ul>'
);
}
}));
});

View File

@@ -0,0 +1,3 @@
<en-note>
<h1 style="box-sizing:inherit;font-family:&quot;Guardian TextSans Web&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;margin-top:0.2em;margin-bottom:0.35em;font-size:2.125em;font-weight:600;line-height:1.3;">Association Between mRNA Vaccination and COVID-19 Hospitalization and Disease Severity</h1>
</en-note>

View File

@@ -0,0 +1,3 @@
<en-note>
<h1 style="box-sizing:inherit;font-family:&quot;Guardian TextSans Web&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;margin-top:0.2em;margin-bottom:0.35em;font-size:2.125em;font-weight:600;line-height:1.3;">Association Between mRNA Vaccination and COVID-19 Hospitalization and Disease Severity</h1>
</en-note>

View File

@@ -1 +1,3 @@
<span style="background-color: rgb(255, 250, 165);-evernote-highlight:true;">I&apos;ll highlight some text.</span>
<span style="background-color: rgb(255, 250, 165);-evernote-highlight:true;">I&apos;ll highlight some text.</span>
<br/>
<span style="--en-highlight:yellow;background-color: #ffef9e;">this text is yellow</span>

View File

@@ -1 +1,2 @@
==I'll highlight some text.==
==I'll highlight some text.==
==this text is yellow==

View File

@@ -0,0 +1 @@
<a data-from-md href='#'>test</a>

View File

@@ -0,0 +1 @@
<a>test</a>

View File

@@ -50,6 +50,20 @@ async function setupWebviewPanel() {
console.info('PostMessagePlugin (Webview): Responding with:', response);
return response;
});
panels.show(view, true);
var intervalID = setInterval(
() => {
console.info('check if webview is ready...');
if(panels.visible(view)) {
console.info('plugin: sending message to webview. ');
panels.postMessage(view, 'testingPluginMessage');
}
clearInterval(intervalID);
}
, 500
);
}
joplin.plugins.register({

View File

@@ -5,6 +5,10 @@ document.addEventListener('click', async (event) => {
console.info('webview.js: sending message');
const response = await webviewApi.postMessage('testingWebviewMessage');
console.info('webiew.js: got response:', response);
console.info('webview.js: got response:', response);
}
})
})
console.info('webview.js: registering message listener');
webviewApi.onMessage((message) => console.info('webview.js: got message:', message));

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "2.5.0",
"version": "2.6.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'",

View File

@@ -58,6 +58,7 @@ const commands = mainScreenCommands
const globalCommands = appCommands.concat(libCommands);
import editorCommandDeclarations from './gui/NoteEditor/editorCommandDeclarations';
import PerFolderSortOrderService from './services/sortOrder/PerFolderSortOrderService';
import ShareService from '@joplin/lib/services/share/ShareService';
import checkForUpdates from './checkForUpdates';
import { AppState } from './app.reducer';
@@ -388,6 +389,8 @@ class Application extends BaseApplication {
this.initRedux();
PerFolderSortOrderService.initialize();
CommandService.instance().initialize(this.store(), Setting.value('env') == 'dev', stateToWhenClauseContext);
for (const command of commands) {
@@ -558,7 +561,6 @@ class Application extends BaseApplication {
// });
// }, 2000);
// setTimeout(() => {
// this.dispatch({
// type: 'DIALOG_OPEN',

View File

@@ -33,9 +33,9 @@ const StyledTitle = styled.span`
`;
// const buttonHeight = 32;
// const buttonSizePx = 32;
const buttonHeight = (props: Props) => {
export const buttonSizePx = (props: Props) => {
if (!props.size || props.size === ButtonSize.Normal) return 32;
if (props.size === ButtonSize.Small) return 26;
throw new Error(`Unknown size: ${props.size}`);
@@ -45,13 +45,13 @@ const StyledButtonBase = styled.button`
display: flex;
align-items: center;
flex-direction: row;
height: ${(props: Props) => buttonHeight(props)}px;
min-height: ${(props: Props) => buttonHeight(props)}px;
max-height: ${(props: Props) => buttonHeight(props)}px;
width: ${(props: any) => props.iconOnly ? `${buttonHeight}px` : 'auto'};
${(props: any) => props.iconOnly ? `min-width: ${buttonHeight}px;` : ''}
height: ${(props: Props) => buttonSizePx(props)}px;
min-height: ${(props: Props) => buttonSizePx(props)}px;
max-height: ${(props: Props) => buttonSizePx(props)}px;
width: ${(props: any) => props.iconOnly ? `${buttonSizePx}px` : 'auto'};
${(props: any) => props.iconOnly ? `min-width: ${buttonSizePx}px;` : ''}
${(props: any) => !props.iconOnly ? 'min-width: 100px;' : ''}
${(props: any) => props.iconOnly ? `max-width: ${buttonHeight}px;` : ''}
${(props: any) => props.iconOnly ? `max-width: ${buttonSizePx}px;` : ''}
box-sizing: border-box;
border-radius: 3px;
border-style: solid;

View File

@@ -5,7 +5,7 @@ import { _ } from '@joplin/lib/locale';
import time from '@joplin/lib/time';
import shim from '@joplin/lib/shim';
import dialogs from '../dialogs';
import { decryptedStatText, dontReencryptData, enableEncryptionConfirmationMessages, onSavePasswordClick, onToggleEnabledClick, reencryptData, upgradeMasterKey, useInputPasswords, useNeedMasterPassword, usePasswordChecker, useStats, useToggleShowDisabledMasterKeys } from '@joplin/lib/components/EncryptionConfigScreen/utils';
import { decryptedStatText, determineKeyPassword, dontReencryptData, enableEncryptionConfirmationMessages, onSavePasswordClick, onToggleEnabledClick, reencryptData, upgradeMasterKey, useInputPasswords, useNeedMasterPassword, usePasswordChecker, useStats, useToggleShowDisabledMasterKeys } from '@joplin/lib/components/EncryptionConfigScreen/utils';
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
import { getEncryptionEnabled, masterKeyEnabled, SyncInfo } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { getDefaultMasterKey, getMasterPasswordStatusMessage, masterPasswordIsValid, toggleAndSetupEncryption } from '@joplin/lib/services/e2ee/utils';
@@ -41,9 +41,11 @@ const EncryptionConfigScreen = (props: Props) => {
const { showDisabledMasterKeys, toggleShowDisabledMasterKeys } = useToggleShowDisabledMasterKeys();
const needMasterPassword = useNeedMasterPassword(passwordChecks, props.masterKeys);
const onUpgradeMasterKey = useCallback((mk: MasterKeyEntity) => {
void upgradeMasterKey(mk, passwordChecks, props.passwords);
}, [passwordChecks, props.passwords]);
const onUpgradeMasterKey = useCallback(async (mk: MasterKeyEntity) => {
const password = determineKeyPassword(mk.id, masterPasswordKeys, props.masterPassword, props.passwords);
const result = await upgradeMasterKey(mk, password);
alert(result);
}, [props.passwords, masterPasswordKeys, props.masterPassword]);
const renderNeedUpgradeSection = () => {
if (!shim.isElectron()) return null;
@@ -167,7 +169,7 @@ const EncryptionConfigScreen = (props: Props) => {
mkComps.push(renderMasterKey(mk));
}
const headerComp = isEnabledMasterKeys ? <h2>{_('Encryption Keys')}</h2> : <a onClick={() => toggleShowDisabledMasterKeys() } style={{ ...theme.urlStyle, display: 'inline-block', marginBottom: 10 }} href="#">{showTable ? _('Hide disabled keys') : _('Show disabled keys')}</a>;
const headerComp = isEnabledMasterKeys ? <h2>{_('Encryption keys')}</h2> : <a onClick={() => toggleShowDisabledMasterKeys() } style={{ ...theme.urlStyle, display: 'inline-block', marginBottom: 10 }} href="#">{showTable ? _('Hide disabled keys') : _('Show disabled keys')}</a>;
const infoComp: any = null; // isEnabledMasterKeys ? <p>{'Note: Only one key is going to be used for encryption (the one marked as "active"). Any of the keys might be used for decryption, depending on how the notes or notebooks were originally encrypted.'}</p> : null;
const tableComp = !showTable ? null : (
<table>
@@ -247,7 +249,7 @@ const EncryptionConfigScreen = (props: Props) => {
{_('Encryption:')} <strong>{props.encryptionEnabled ? _('Enabled') : _('Disabled')}</strong>
</p>
<p>
{_('Public-Private Key Pair:')} <strong>{props.ppk ? _('Generated') : _('Not generated')}</strong>
{_('Public-private key pair:')} <strong>{props.ppk ? _('Generated') : _('Not generated')}</strong>
</p>
{decryptedItemsInfo}
{toggleButton}
@@ -319,7 +321,7 @@ const EncryptionConfigScreen = (props: Props) => {
nonExistingMasterKeySection = (
<div className="section">
<h2>{_('Missing Keys')}</h2>
<h2>{_('Missing keys')}</h2>
<p>{_('The keys with these IDs are used to encrypt some of your items, however the application does not currently have access to them. It is likely they will eventually be downloaded via synchronisation.')}</p>
<table>
<tbody>

View File

@@ -37,6 +37,7 @@ import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncIn
import { parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import ElectronAppWrapper from '../../ElectronAppWrapper';
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
import { MasterKeyEntity } from '../../../lib/services/e2ee/types';
import commands from './commands/index';
import invitationRespond from '../../services/share/invitationRespond';
const { connect } = require('react-redux');
@@ -564,8 +565,8 @@ class MainScreenComponent extends React.Component<Props, State> {
bridge().restart();
};
const onInvitationRespond = async (shareUserId: string, folderId: string, accept: boolean) => {
await invitationRespond(shareUserId, folderId, accept);
const onInvitationRespond = async (shareUserId: string, folderId: string, masterKey: MasterKeyEntity, accept: boolean) => {
await invitationRespond(shareUserId, folderId, masterKey, accept);
};
let msg = null;
@@ -610,9 +611,9 @@ class MainScreenComponent extends React.Component<Props, State> {
msg = this.renderNotificationMessage(
_('%s (%s) would like to share a notebook with you.', sharer.full_name, sharer.email),
_('Accept'),
() => onInvitationRespond(invitation.id, invitation.share.folder_id, true),
() => onInvitationRespond(invitation.id, invitation.share.folder_id, invitation.master_key, true),
_('Reject'),
() => onInvitationRespond(invitation.id, invitation.share.folder_id, false)
() => onInvitationRespond(invitation.id, invitation.share.folder_id, invitation.master_key, false)
);
} else if (this.props.hasDisabledSyncItems) {
msg = this.renderNotificationMessage(

View File

@@ -28,6 +28,9 @@ import * as showSpellCheckerMenu from './showSpellCheckerMenu';
import * as toggleEditors from './toggleEditors';
import * as toggleLayoutMoveMode from './toggleLayoutMoveMode';
import * as toggleNoteList from './toggleNoteList';
import * as toggleNotesSortOrderField from './toggleNotesSortOrderField';
import * as toggleNotesSortOrderReverse from './toggleNotesSortOrderReverse';
import * as togglePerFolderSortOrder from './togglePerFolderSortOrder';
import * as toggleSideBar from './toggleSideBar';
import * as toggleVisiblePanes from './toggleVisiblePanes';
@@ -61,6 +64,9 @@ const index:any[] = [
toggleEditors,
toggleLayoutMoveMode,
toggleNoteList,
toggleNotesSortOrderField,
toggleNotesSortOrderReverse,
togglePerFolderSortOrder,
toggleSideBar,
toggleVisiblePanes,
];

View File

@@ -0,0 +1,26 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import { setNotesSortOrder } from '../../../services/sortOrder/notesSortOrderUtils';
import { _ } from '@joplin/lib/locale';
export const declaration: CommandDeclaration = {
name: 'toggleNotesSortOrderField',
label: () => _('Toggle sort order field'),
parentLabel: () => _('Notes'),
};
export const runtime = (): CommandRuntime => {
return {
execute: async (_context: CommandContext, field?: string | Array<any>, reverse?: boolean) => {
// field: Sort order's field. undefined means switching a field.
// reverse: whether the sort order is reversed or not. undefined means toggling.
//
// To support CommandService.scheduleExecute(), field accepts an size-two Array,
// which means [field, reverse].
if (typeof field !== 'object') {
setNotesSortOrder(field, reverse);
} else {
setNotesSortOrder(field[0], field[1]);
}
},
};
};

View File

@@ -0,0 +1,19 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import Setting from '@joplin/lib/models/Setting';
import { _ } from '@joplin/lib/locale';
import { setNotesSortOrder } from '../../../services/sortOrder/notesSortOrderUtils';
export const declaration: CommandDeclaration = {
name: 'toggleNotesSortOrderReverse',
label: () => _('Reverse sort order'),
parentLabel: () => _('Notes'),
};
export const runtime = (): CommandRuntime => {
return {
execute: async (_context: CommandContext) => {
const reverse = Setting.value('notes.sortOrder.reverse');
setNotesSortOrder(undefined, !reverse);
},
};
};

View File

@@ -0,0 +1,18 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import PerFolderSortOrderService from '../../../services/sortOrder/PerFolderSortOrderService';
export const declaration: CommandDeclaration = {
name: 'togglePerFolderSortOrder',
label: () => _('Toggle own sort order'),
};
export const runtime = (): CommandRuntime => {
return {
enabledCondition: 'oneFolderSelected',
execute: async (_context: CommandContext, folderId?: string, own?: boolean) => {
PerFolderSortOrderService.set(folderId, own);
},
};
};

View File

@@ -10,6 +10,7 @@ import { getMasterPasswordStatus, getMasterPasswordStatusMessage, checkHasMaster
import { reg } from '@joplin/lib/registry';
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import KvStore from '@joplin/lib/services/KvStore';
import ShareService from '@joplin/lib/services/share/ShareService';
interface Props {
themeId: number;
@@ -60,7 +61,7 @@ export default function(props: Props) {
if (mode === Mode.Set) {
await updateMasterPassword(currentPassword, password1);
} else if (mode === Mode.Reset) {
await resetMasterPassword(EncryptionService.instance(), KvStore.instance(), password1);
await resetMasterPassword(EncryptionService.instance(), KvStore.instance(), ShareService.instance(), password1);
} else {
throw new Error(`Unknown mode: ${mode}`);
}
@@ -200,7 +201,7 @@ export default function(props: Props) {
}
}
const dialogTitle = mode === Mode.Set ? _('Manager master password') : `⚠️ ${_('Reset master password')} ⚠️`;
const dialogTitle = mode === Mode.Set ? _('Manage master password') : `⚠️ ${_('Reset master password')} ⚠️`;
const okButtonLabel = mode === Mode.Set ? _('Save') : `⚠️ ${_('Reset master password')} ⚠️`;
function renderDialogWrapper() {

View File

@@ -136,7 +136,8 @@ function useMenuStates(menu: any, props: Props) {
menuItemSetChecked(`sort:${type}:${field}`, (props as any)[`${type}.sortOrder.field`] === field);
}
menuItemSetChecked(`sort:${type}:reverse`, (props as any)[`${type}.sortOrder.reverse`]);
const id = type == 'notes' ? 'toggleNotesSortOrderReverse' : `sort:${type}:reverse`;
menuItemSetChecked(id, (props as any)[`${type}.sortOrder.reverse`]);
}
applySortItemCheckState('notes');
@@ -267,22 +268,33 @@ function useMenu(props: Props) {
type: 'checkbox',
// checked: Setting.value(`${type}.sortOrder.field`) === field,
click: () => {
Setting.setValue(`${type}.sortOrder.field`, field);
if (type === 'notes') {
void CommandService.instance().execute('toggleNotesSortOrderField', field);
} else {
Setting.setValue(`${type}.sortOrder.field`, field);
}
},
});
}
sortItems.push({ type: 'separator' });
sortItems.push({
id: `sort:${type}:reverse`,
label: Setting.settingMetadata(`${type}.sortOrder.reverse`).label(),
type: 'checkbox',
// checked: Setting.value(`${type}.sortOrder.reverse`),
click: () => {
Setting.setValue(`${type}.sortOrder.reverse`, !Setting.value(`${type}.sortOrder.reverse`));
},
});
if (type == 'notes') {
sortItems.push(
{ ...menuItemDic.toggleNotesSortOrderReverse, type: 'checkbox' },
{ ...menuItemDic.toggleNotesSortOrderField, visible: false }
);
} else {
sortItems.push({
id: `sort:${type}:reverse`,
label: Setting.settingMetadata(`${type}.sortOrder.reverse`).label(),
type: 'checkbox',
// checked: Setting.value(`${type}.sortOrder.reverse`),
click: () => {
Setting.setValue(`${type}.sortOrder.reverse`, !Setting.value(`${type}.sortOrder.reverse`));
},
});
}
return sortItems;
};

View File

@@ -6,7 +6,8 @@ import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
import { commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
import { ScrollOptions, ScrollOptionTypes } from '../../utils/types';
import { CommandValue } from '../../utils/types';
import { useScrollHandler, usePrevious, cursorPositionToTextOffset } from './utils';
import { usePrevious, cursorPositionToTextOffset } from './utils';
import useScrollHandler, { translateScrollPercentToEditor, translateScrollPercentToViewer } from './utils/useScrollHandler';
import useElementSize from '@joplin/lib/hooks/useElementSize';
import Toolbar from './Toolbar';
import styles_ from './styles';
@@ -114,9 +115,10 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
if (!webviewRef.current) return;
webviewRef.current.wrappedInstance.send('scrollToHash', options.value as string);
} else if (options.type === ScrollOptionTypes.Percent) {
const p = options.value as number;
setEditorPercentScroll(p);
setViewerPercentScroll(p);
const editorPercent = options.value as number;
setEditorPercentScroll(editorPercent);
const viewerPercent = translateScrollPercentToViewer(editorRef, webviewRef, editorPercent);
setViewerPercentScroll(viewerPercent);
} else {
throw new Error(`Unsupported scroll options: ${options.type}`);
}
@@ -579,7 +581,17 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
editorRef.current.updateBody(newBody);
}
} else if (msg === 'percentScroll') {
setEditorPercentScroll(arg0);
const viewerPercent = arg0;
const editorPercent = translateScrollPercentToEditor(editorRef, webviewRef, viewerPercent);
setEditorPercentScroll(editorPercent);
} else if (msg === 'syncViewerScrollWithEditor') {
const force = !!arg0;
webviewRef.current?.wrappedInstance?.refreshSyncScrollMap(force);
const editorPercent = Math.max(0, Math.min(1, editorRef.current?.getScrollPercent()));
if (!isNaN(editorPercent)) {
const viewerPercent = translateScrollPercentToViewer(editorRef, webviewRef, editorPercent);
setViewerPercentScroll(viewerPercent);
}
} else {
props.onMessage(event);
}
@@ -604,6 +616,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
const result = await props.markupToHtml(props.contentMarkupLanguage, bodyToRender, markupRenderOptions({
resourceInfos: props.resourceInfos,
contentMaxWidth: props.contentMaxWidth,
mapsToLine: true,
}));
if (cancelled) return;
@@ -639,6 +652,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
// Since we can't do much about it we just print an error.
if (webviewRef.current && webviewRef.current.wrappedInstance) {
webviewRef.current.wrappedInstance.send('setHtml', renderedBody.html, options);
webviewRef.current.wrappedInstance.refreshSyncScrollMap(true);
} else {
console.error('Trying to set HTML on an undefined webview ref');
}

View File

@@ -1,5 +1,4 @@
import { useEffect, useCallback, useRef } from 'react';
import shim from '@joplin/lib/shim';
import { useEffect, useRef } from 'react';
export function cursorPositionToTextOffset(cursorPos: any, body: string) {
if (!body) return 0;
@@ -28,64 +27,3 @@ export function usePrevious(value: any): any {
});
return ref.current;
}
export function useScrollHandler(editorRef: any, webviewRef: any, onScroll: Function) {
const ignoreNextEditorScrollEvent_ = useRef(false);
const scrollTimeoutId_ = useRef<any>(null);
const scheduleOnScroll = useCallback((event: any) => {
if (scrollTimeoutId_.current) {
shim.clearTimeout(scrollTimeoutId_.current);
scrollTimeoutId_.current = null;
}
scrollTimeoutId_.current = shim.setTimeout(() => {
scrollTimeoutId_.current = null;
onScroll(event);
}, 10);
}, [onScroll]);
const setEditorPercentScroll = useCallback((p: number) => {
ignoreNextEditorScrollEvent_.current = true;
if (editorRef.current) {
editorRef.current.setScrollPercent(p);
scheduleOnScroll({ percent: p });
}
}, [scheduleOnScroll]);
const setViewerPercentScroll = useCallback((p: number) => {
if (webviewRef.current) {
webviewRef.current.wrappedInstance.send('setPercentScroll', p);
scheduleOnScroll({ percent: p });
}
}, [scheduleOnScroll]);
const editor_scroll = useCallback(() => {
if (ignoreNextEditorScrollEvent_.current) {
ignoreNextEditorScrollEvent_.current = false;
return;
}
if (editorRef.current) {
const percent = editorRef.current.getScrollPercent();
if (!isNaN(percent)) {
// when switching to another note, the percent can sometimes be NaN
// this is coming from `gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.ts`
// when CodeMirror returns scroll info with heigth == clientHeigth
// https://github.com/laurent22/joplin/issues/4797
setViewerPercentScroll(percent);
}
}
}, [setViewerPercentScroll]);
const resetScroll = useCallback(() => {
if (editorRef.current) {
editorRef.current.setScrollPercent(0);
}
}, []);
return { resetScroll, setEditorPercentScroll, setViewerPercentScroll, editor_scroll };
}

View File

@@ -0,0 +1,125 @@
import { useCallback, useRef } from 'react';
import shim from '@joplin/lib/shim';
import { SyncScrollMap } from '../../../../utils/SyncScrollMap';
export default function useScrollHandler(editorRef: any, webviewRef: any, onScroll: Function) {
const ignoreNextEditorScrollEvent_ = useRef(false);
const scrollTimeoutId_ = useRef<any>(null);
const scheduleOnScroll = useCallback((event: any) => {
if (scrollTimeoutId_.current) {
shim.clearTimeout(scrollTimeoutId_.current);
scrollTimeoutId_.current = null;
}
scrollTimeoutId_.current = shim.setTimeout(() => {
scrollTimeoutId_.current = null;
onScroll(event);
}, 10);
}, [onScroll]);
const setEditorPercentScroll = useCallback((p: number) => {
ignoreNextEditorScrollEvent_.current = true;
if (editorRef.current) {
editorRef.current.setScrollPercent(p);
scheduleOnScroll({ percent: p });
}
}, [scheduleOnScroll]);
const setViewerPercentScroll = useCallback((p: number) => {
if (webviewRef.current) {
webviewRef.current.wrappedInstance.send('setPercentScroll', p);
scheduleOnScroll({ percent: p });
}
}, [scheduleOnScroll]);
const editor_scroll = useCallback(() => {
if (ignoreNextEditorScrollEvent_.current) {
ignoreNextEditorScrollEvent_.current = false;
return;
}
if (editorRef.current) {
const editorPercent = Math.max(0, Math.min(1, editorRef.current.getScrollPercent()));
if (!isNaN(editorPercent)) {
// when switching to another note, the percent can sometimes be NaN
// this is coming from `gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.ts`
// when CodeMirror returns scroll info with heigth == clientHeigth
// https://github.com/laurent22/joplin/issues/4797
const viewerPercent = translateScrollPercentToViewer(editorRef, webviewRef, editorPercent);
setViewerPercentScroll(viewerPercent);
}
}
}, [setViewerPercentScroll]);
const resetScroll = useCallback(() => {
if (editorRef.current) {
editorRef.current.setScrollPercent(0);
}
}, []);
return { resetScroll, setEditorPercentScroll, setViewerPercentScroll, editor_scroll };
}
const translateScrollPercent_ = (editorRef: any, webviewRef: any, percent: number, editorToViewer: boolean) => {
// If the input is out of (0,1) or not number, it is not translated.
if (!(0 < percent && percent < 1)) return percent;
const map: SyncScrollMap = webviewRef.current?.wrappedInstance.getSyncScrollMap();
const cm = editorRef.current;
if (!map || map.line.length <= 2 || !cm) return percent; // No translation
const lineCount = cm.lineCount();
if (map.line[map.line.length - 2] >= lineCount) {
// Discarded a obsolete map and use no translation.
webviewRef.current.wrappedInstance.refreshSyncScrollMap(false);
return percent;
}
const info = cm.getScrollInfo();
const height = Math.max(1, info.height - info.clientHeight);
let values = map.percent, target = percent;
if (editorToViewer) {
const top = percent * height;
const line = cm.lineAtHeight(top, 'local');
values = map.line;
target = line;
}
// Binary search (rightmost): finds where map[r-1][field] <= target < map[r][field]
let l = 1, r = values.length - 1;
while (l < r) {
const m = Math.floor(l + (r - l) / 2);
if (target < values[m]) r = m; else l = m + 1;
}
const lineU = map.line[r - 1];
const lineL = Math.min(lineCount, map.line[r]);
const ePercentU = r == 1 ? 0 : Math.min(1, cm.heightAtLine(lineU, 'local') / height);
const ePercentL = Math.min(1, cm.heightAtLine(lineL, 'local') / height);
const vPercentU = map.percent[r - 1];
const vPercentL = ePercentL == 1 ? 1 : map.percent[r];
let result;
if (editorToViewer) {
const linInterp = (percent - ePercentU) / (ePercentL - ePercentU);
result = vPercentU + (vPercentL - vPercentU) * linInterp;
} else {
const linInterp = (percent - vPercentU) / (vPercentL - vPercentU);
result = ePercentU + (ePercentL - ePercentU) * linInterp;
}
return Math.max(0, Math.min(1, result));
};
// translateScrollPercentToEditor() and translateScrollPercentToViewer() are
// the translation functions between Editor's scroll percent and Viewer's scroll
// percent. They are used for synchronous scrolling between Editor and Viewer.
// They use a SyncScrollMap provided by Viewer for its translation.
// To see the detail of synchronous scrolling, refer the following design document.
// https://github.com/laurent22/joplin/pull/5512#issuecomment-931277022
export const translateScrollPercentToEditor = (editorRef: any, webviewRef: any, viewerPercent: number) => {
const editorPercent = translateScrollPercent_(editorRef, webviewRef, viewerPercent, false);
return editorPercent;
};
export const translateScrollPercentToViewer = (editorRef: any, webviewRef: any, editorPercent: number) => {
const viewerPercent = translateScrollPercent_(editorRef, webviewRef, editorPercent, true);
return viewerPercent;
};

View File

@@ -110,7 +110,7 @@ function NoteEditor(props: NoteEditorProps) {
const savedNote: any = await Note.save(note);
setFormNote((prev: FormNote) => {
return { ...prev, user_updated_time: savedNote.user_updated_time };
return { ...prev, user_updated_time: savedNote.user_updated_time, hasChanged: false };
});
void ExternalEditWatcher.instance().updateNoteFile(savedNote);

View File

@@ -19,6 +19,7 @@ export interface MarkupToHtmlOptions {
contentMaxWidth?: number;
plugins?: Record<string, any>;
bodyOnly?: boolean;
mapsToLine?: boolean;
}
export default function useMarkupToHtml(deps: HookDependencies) {

View File

@@ -1,13 +1,19 @@
import { AppState } from '../../app.reducer';
import * as React from 'react';
import { useEffect, useRef } from 'react';
import SearchBar from '../SearchBar/SearchBar';
import Button, { ButtonLevel } from '../Button/Button';
import Button, { ButtonLevel, ButtonSize, buttonSizePx } from '../Button/Button';
import CommandService from '@joplin/lib/services/CommandService';
import { runtime as focusSearchRuntime } from './commands/focusSearch';
const { connect } = require('react-redux');
const styled = require('styled-components').default;
interface Props {
showNewNoteButtons: boolean;
sortOrderButtonsVisible: boolean;
sortOrderField: string;
sortOrderReverse: boolean;
notesParentType: string;
height: number;
}
@@ -28,12 +34,27 @@ const StyledButton = styled(Button)`
min-height: 26px;
`;
const StyledPairButtonL = styled(Button)`
margin-left: 8px;
border-radius: 5px 0 0 5px;
min-width: ${(props: any) => buttonSizePx(props)}px;
max-width: ${(props: any) => buttonSizePx(props)}px;
`;
const StyledPairButtonR = styled(Button)`
min-width: 8px;
margin-left: 0px;
border-radius: 0 5px 5px 0;
border-width: 1px 1px 1px 0;
width: auto;
`;
const ButtonContainer = styled.div`
display: flex;
flex-direction: row;
`;
export default function NoteListControls(props: Props) {
function NoteListControls(props: Props) {
const searchBarRef = useRef(null);
useEffect(function() {
@@ -52,16 +73,66 @@ export default function NoteListControls(props: Props) {
void CommandService.instance().execute('newNote');
}
function onSortOrderFieldButtonClick() {
void CommandService.instance().execute('toggleNotesSortOrderField');
}
function onSortOrderReverseButtonClick() {
void CommandService.instance().execute('toggleNotesSortOrderReverse');
}
function sortOrderFieldIcon() {
const field = props.sortOrderField;
const iconMap: any = {
user_updated_time: 'far fa-calendar-alt',
user_created_time: 'far fa-calendar-plus',
title: 'fas fa-font',
order: 'fas fa-wrench',
};
return `${iconMap[field] || iconMap['title']} ${field}`;
}
function sortOrderReverseIcon() {
return props.sortOrderReverse ? 'fas fa-long-arrow-alt-up' : 'fas fa-long-arrow-alt-down';
}
function showsSortOrderButtons() {
let visible = props.sortOrderButtonsVisible;
if (props.notesParentType === 'Search') visible = false;
return visible;
}
function renderNewNoteButtons() {
if (!props.showNewNoteButtons) return null;
return (
<ButtonContainer>
{showsSortOrderButtons() &&
<StyledPairButtonL
className="sort-order-field-button"
tooltip={CommandService.instance().label('toggleNotesSortOrderField')}
iconName={sortOrderFieldIcon()}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onSortOrderFieldButtonClick}
/>
}
{showsSortOrderButtons() &&
<StyledPairButtonR
className="sort-order-reverse-button"
tooltip={CommandService.instance().label('toggleNotesSortOrderReverse')}
iconName={sortOrderReverseIcon()}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onSortOrderReverseButtonClick}
/>
}
<StyledButton
className="new-todo-button"
tooltip={CommandService.instance().label('newTodo')}
iconName="far fa-check-square"
level={ButtonLevel.Primary}
size={ButtonSize.Small}
onClick={onNewTodoButtonClick}
/>
<StyledButton
@@ -69,6 +140,7 @@ export default function NoteListControls(props: Props) {
tooltip={CommandService.instance().label('newNote')}
iconName="icon-note"
level={ButtonLevel.Primary}
size={ButtonSize.Small}
onClick={onNewNoteButtonClick}
/>
</ButtonContainer>
@@ -82,3 +154,14 @@ export default function NoteListControls(props: Props) {
</StyledRoot>
);
}
const mapStateToProps = (state: AppState) => {
return {
sortOrderButtonsVisible: state.settings['notes.sortOrder.buttonsVisible'],
sortOrderField: state.settings['notes.sortOrder.field'],
sortOrderReverse: state.settings['notes.sortOrder.reverse'],
notesParentType: state.notesParentType,
};
};
export default connect(mapStateToProps)(NoteListControls);

View File

@@ -2,6 +2,7 @@ import PostMessageService, { MessageResponse, ResponderComponentType } from '@jo
import * as React from 'react';
const { connect } = require('react-redux');
import { reg } from '@joplin/lib/registry';
import { SyncScrollMap, SyncScrollMapper } from './utils/SyncScrollMap';
interface Props {
onDomReady: Function;
@@ -167,6 +168,17 @@ class NoteTextViewerComponent extends React.Component<Props, any> {
}
}
private syncScrollMapper_ = new SyncScrollMapper;
refreshSyncScrollMap(forced: boolean) {
return this.syncScrollMapper_.refresh(forced);
}
getSyncScrollMap(): SyncScrollMap {
const doc = this.webviewRef_.current?.contentWindow?.document;
return this.syncScrollMapper_.get(doc);
}
// ----------------------------------------------------------------
// Wrap WebView functions (END)
// ----------------------------------------------------------------

View File

@@ -171,7 +171,7 @@ function ShareFolderDialog(props: Props) {
try {
setLatestError(null);
const share = await ShareService.instance().shareFolder(props.folderId);
await ShareService.instance().addShareRecipient(share.id, recipientEmail);
await ShareService.instance().addShareRecipient(share.id, share.master_key_id, recipientEmail);
await Promise.all([
ShareService.instance().refreshShares(),
ShareService.instance().refreshShareUsers(share.id),

View File

@@ -20,6 +20,7 @@ import Logger from '@joplin/lib/Logger';
import { FolderEntity } from '@joplin/lib/services/database/types';
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
import { store } from '@joplin/lib/reducer';
import PerFolderSortOrderService from '../../services/sortOrder/PerFolderSortOrderService';
import { getFolderCallbackUrl, getTagCallbackUrl } from '@joplin/lib/callbackUrlUtils';
const { connect } = require('react-redux');
const shared = require('@joplin/lib/components/shared/side-menu-shared.js');
@@ -332,6 +333,13 @@ class SidebarComponent extends React.Component<Props, State> {
submenu: exportMenu,
})
);
if (Setting.value('notes.perFolderSortOrderEnabled')) {
menu.append(new MenuItem({
...menuUtils.commandToStatefulMenuItem('togglePerFolderSortOrder', itemId),
type: 'checkbox',
checked: PerFolderSortOrderService.isSet(itemId),
}));
}
}
if (itemType === BaseModel.TYPE_FOLDER) {

View File

@@ -29,9 +29,13 @@ export default function() {
'textLink',
'textPaste',
'textSelectAll',
'textBulletedList',
'toggleExternalEditing',
'toggleLayoutMoveMode',
'toggleNoteList',
'toggleNotesSortOrderField',
'toggleNotesSortOrderReverse',
'togglePerFolderSortOrder',
'toggleSideBar',
'toggleVisiblePanes',
'editor.deleteLine',

View File

@@ -102,14 +102,7 @@
// images are being displayed then restored while images are being reloaded, the new scrollTop might be changed
// so that it is not greater than contentHeight. On the other hand, with percentScroll it is possible to restore
// it at any time knowing that it's not going to be changed because the content height has changed.
// To restore percentScroll the "checkScrollIID" interval is used. It constantly resets the scroll position during
// one second after the content has been updated.
//
// ignoreNextScroll is used to differentiate between scroll event from the users and those that are the result
// of programmatically changing scrollTop. We only want to respond to events initiated by the user.
let percentScroll_ = 0;
let checkScrollIID_ = null;
// This variable provides a way to skip scroll events for a certain duration.
// In general, it should be set whenever the scroll value is set explicitely (programmatically)
@@ -195,7 +188,66 @@
return true;
}
let checkAllImageLoadedIID_ = null;
let alreadyAllImagesLoaded = false;
// During a note is being rendered, its height is varying. To keep scroll
// consistency, observing the height of the content element and updating its
// scroll position is required. For the purpose, 'ResizeObserver' is used.
// ResizeObserver is standard and an element's counterpart to 'window.resize'
// event. It's overhead is cheaper than observation using an interval timer.
//
// To observe the scroll height of the content element, adding, removing and
// resizing of its children should be observed. So, the combination of
// ResizeObserver (used for resizing) and MutationObserver (used for ading
// and removing) is used.
//
// References:
// https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
//
// By using them, this observeRendering() function provides a efficient way
// to observe the changes of the scroll height of the content element
// using a callback approach.
function observeRendering(callback, compress = false) {
let previousHeight = 0;
const fn = (cause) => {
const height = contentElement.scrollHeight;
const heightChanged = height != previousHeight;
if (!compress || heightChanged) {
previousHeight = height;
callback(cause, height, heightChanged);
}
};
// 'resized' means DOM Layout change or Window resize event
let resizeObserver = new ResizeObserver(() => fn('resized'));
// An HTML document to be rendered is added and removed as a child of
// the content element for each setHtml() invocation.
let mutationObserver = new MutationObserver(entries => {
const e = entries[0];
e.removedNodes.forEach(n => n instanceof Element && resizeObserver.unobserve(n));
e.addedNodes.forEach(n => n instanceof Element && resizeObserver.observe(n));
if (e.removedNodes.length + e.addedNodes.length) fn('dom-changed');
});
mutationObserver.observe(contentElement, { childList: true });
return { mutationObserver, resizeObserver };
};
// A callback anonymous function invoked when the scroll height changes.
const onRendering = observeRendering((cause, height, heightChanged) => {
if (!alreadyAllImagesLoaded) {
const loaded = allImagesLoaded();
if (loaded) {
alreadyAllImagesLoaded = true;
ipcProxySendToHost('syncViewerScrollWithEditor', true);
ipcProxySendToHost('noteRenderComplete');
return;
}
}
if (heightChanged) {
// When the scroll height changes, sync is needed.
ipcProxySendToHost('syncViewerScrollWithEditor');
}
});
ipc.focus = (event) => {
const dummyID = 'joplin-content-focus-dummy';
@@ -217,23 +269,10 @@
contentElement.innerHTML = html;
let previousContentHeight = contentElement.scrollHeight;
let startTime = Date.now();
restorePercentScroll();
restorePercentScroll(); // First, a quick treatment is applied.
ipcProxySendToHost('syncViewerScrollWithEditor');
if (!checkScrollIID_) {
checkScrollIID_ = setInterval(() => {
const h = contentElement.scrollHeight;
if (h !== previousContentHeight) {
previousContentHeight = h;
restorePercentScroll();
}
if (Date.now() - startTime >= 1000) {
clearInterval(checkScrollIID_);
checkScrollIID_ = null;
}
}, 1);
}
alreadyAllImagesLoaded = false;
addPluginAssets(event.options.pluginAssets);
@@ -242,25 +281,10 @@
}
document.dispatchEvent(new Event('joplin-noteDidUpdate'));
if (checkAllImageLoadedIID_) clearInterval(checkAllImageLoadedIID_);
checkAllImageLoadedIID_ = setInterval(() => {
if (!allImagesLoaded()) return;
clearInterval(checkAllImageLoadedIID_);
ipcProxySendToHost('noteRenderComplete');
}, 100);
}
ipc.setPercentScroll = (event) => {
const percent = event.percent;
if (checkScrollIID_) {
clearInterval(checkScrollIID_);
checkScrollIID_ = null;
}
lastScrollEventTime = Date.now();
setPercentScroll(percent);
}
@@ -275,16 +299,16 @@
const markJsHackMarker_ = ' ';
for (let i = 0; i < elements.length; i++) {
if (!type) {
elements[i].innerHTML = elements[i].innerHTML + markJsHackMarker_;
elements[i].insertAdjacentHTML('beforeend', markJsHackMarker_);
} else if (type === 'insertBefore') {
elements[i].insertAdjacentHTML('beforeBegin', markJsHackMarker_);
}
}
}
prepareElementsForMarkJs(document.getElementsByTagName('p'));
prepareElementsForMarkJs(document.getElementsByTagName('div'));
prepareElementsForMarkJs(document.getElementsByTagName('br'), 'insertBefore');
prepareElementsForMarkJs(contentElement.getElementsByTagName('p'));
prepareElementsForMarkJs(contentElement.getElementsByTagName('div'));
prepareElementsForMarkJs(contentElement.getElementsByTagName('br'), 'insertBefore');
markJsHackMarkerInserted_ = true;
}
@@ -357,6 +381,10 @@
return Math.max(0, contentElement.scrollHeight - contentElement.clientHeight);
}
function maxScrollLeft() {
return Math.max(0, contentElement.scrollWidth - contentElement.clientWidth);
}
// The body element needs to have a fixed height for the content to be scrollable
function updateBodyHeight() {
document.getElementById('joplin-container-body').style.height = window.innerHeight + 'px';
@@ -365,7 +393,70 @@
function currentPercentScroll() {
const m = maxScrollTop();
return m ? contentElement.scrollTop / m : 0;
// As of 2021, if zoomFactor != 1, underlying Chrome returns scrollTop with
// some numerical error. It can be more than maxScrollTop().
return m ? Math.min(1, contentElement.scrollTop / m) : 0;
}
// If zoom factor is not 1, Electron/Chromium calculates scrollTop incorrectly.
// This is automatically set.
let zoomFactorIsNotOne = false;
// When custom smooth scrolling is ongoing, remainedScrollDx/Dy keep the remaining
// amount of scrolling.
let remainedScrollDx = 0, remainedScrollDy = 0, remainedScrollTimerId = null;
function resetSmoothScroll() { remainedScrollDx = 0; remainedScrollDy = 0; }
// To avoid Electron/Chromium's scrolling bug when zoom fator is not 1,
// Custom scrolling is implemented. This is used only when zoom factor is not 1.
// If smoothly argument is true, smooth scrolling is performed.
// See https://github.com/laurent22/joplin/pull/5606#issuecomment-964293459
function customScroll(wheelEvent, smoothly) {
const linePixels = 100 / 3;
const pagePixelsX = Math.max(linePixels, contentElement.clientWidth);
const pagePixelsY = Math.max(linePixels, contentElement.clientHeight);
let pixelsPerUnitX = 1, pixelsPerUnitY = 1; // for WheelEvent.DOM_DELTA_PIXEL
if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
pixelsPerUnitX = pixelsPerUnitY = linePixels;
} else if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
pixelsPerUnitX = pagePixelsX;
pixelsPerUnitY = pagePixelsY;
}
if (!smoothly) {
if (wheelEvent.deltaX) {
const dx = wheelEvent.deltaX * pixelsPerUnitX;
contentElement.scrollLeft = Math.max(0, Math.min(maxScrollLeft(), contentElement.scrollLeft + dx));
}
if (wheelEvent.deltaY) {
const dy = wheelEvent.deltaY * pixelsPerUnitY;
contentElement.scrollTop = Math.max(0, Math.min(maxScrollTop(), contentElement.scrollTop + dy));
}
} else {
if (Math.sign(remainedScrollDx) !== Math.sign(wheelEvent.deltaX)) remainedScrollDx = 0;
if (Math.sign(remainedScrollDy) !== Math.sign(wheelEvent.deltaY)) remainedScrollDy = 0;
remainedScrollDx += wheelEvent.deltaX * pixelsPerUnitX;
remainedScrollDy += wheelEvent.deltaY * pixelsPerUnitY;
const maxDx = Math.max(8.5, Math.min(pagePixelsX, Math.abs(remainedScrollDx)) / 5);
const maxDy = Math.max(8.5, Math.min(pagePixelsY, Math.abs(remainedScrollDy)) / 5);
const f = () => {
if (remainedScrollTimerId) {
clearTimeout(remainedScrollTimerId);
remainedScrollTimerId = null;
}
if (remainedScrollDx) {
const dx = Math.max(-maxDx, Math.min(maxDx, remainedScrollDx));
remainedScrollDx -= dx;
contentElement.scrollLeft = Math.max(0, Math.min(maxScrollLeft(), contentElement.scrollLeft + dx));
}
if (remainedScrollDy) {
const dy = Math.max(-maxDy, Math.min(maxDy, remainedScrollDy));
remainedScrollDy -= dy;
contentElement.scrollTop = Math.max(0, Math.min(maxScrollTop(), contentElement.scrollTop + dy));
}
if (remainedScrollDx || remainedScrollDy) remainedScrollTimerId = setTimeout(f, 20);
};
f();
}
}
contentElement.addEventListener('wheel', webviewLib.logEnabledEventHandler(e => {
@@ -375,8 +466,14 @@
// To avoid this problem, prevent the upstream from calculating scrollTop and
// calculate by yourself by accumulating wheel events.
// https://github.com/laurent22/joplin/pull/5496
contentElement.scrollTop = Math.max(0, Math.min(maxScrollTop(), contentElement.scrollTop + e.deltaY));
e.preventDefault();
// When the Electron/Chromium bug is fixed, remove this listener.
// If scrollTop ever has a fraction part, zoomFactor is not 1.
if (zoomFactorIsNotOne || !Number.isInteger(contentElement.scrollTop)) {
zoomFactorIsNotOne = true;
customScroll(e, true);
e.preventDefault();
}
}));
contentElement.addEventListener('scroll', webviewLib.logEnabledEventHandler(e => {
@@ -473,6 +570,9 @@
window.addEventListener('resize', webviewLib.logEnabledEventHandler(() => {
updateBodyHeight();
// When zoomFactor is changed, resize event happens.
zoomFactorIsNotOne = false;
resetSmoothScroll();
}));
// Prevent middle-click as that would open the URL in an Electron window

View File

@@ -0,0 +1,92 @@
import shim from '@joplin/lib/shim';
// SyncScrollMap is used for synchronous scrolling between Markdown Editor and Viewer.
// It has the mapping information between the line numbers of a Markdown text and
// the scroll positions (percents) of the elements in the HTML document transformed
// from the Markdown text.
// To see the detail of synchronous scrolling, refer the following design document.
// https://github.com/laurent22/joplin/pull/5512#issuecomment-931277022
export interface SyncScrollMap {
line: number[];
percent: number[];
viewHeight: number;
}
// Map creation utility class
export class SyncScrollMapper {
private map_: SyncScrollMap = null;
private refreshTimeoutId_: any = null;
private refreshTime_ = 0;
// Invalidates an outdated SyncScrollMap.
// For a performance reason, too frequent refresh requests are
// skippend and delayed. If forced is true, refreshing is immediately performed.
public refresh(forced: boolean) {
const elapsed = this.refreshTime_ ? Date.now() - this.refreshTime_ : 10 * 1000;
if (!forced && (elapsed < 200 || this.refreshTimeoutId_)) {
// to avoid too frequent recreations of a sync-scroll map.
if (this.refreshTimeoutId_) {
shim.clearTimeout(this.refreshTimeoutId_);
this.refreshTimeoutId_ = null;
}
this.refreshTimeoutId_ = shim.setTimeout(() => {
this.refreshTimeoutId_ = null;
this.map_ = null;
this.refreshTime_ = Date.now();
}, 200);
} else {
this.map_ = null;
this.refreshTime_ = Date.now();
}
}
// Creates a new SyncScrollMap or reuses an existing one.
public get(doc: Document): SyncScrollMap {
// Returns a cached translation map between editor's scroll percenet
// and viewer's scroll percent. Both attributes (line and percent) of
// the returned map are sorted respectively.
// Since creating this map is costly for each scroll event, it is cached.
// When some update events which outdate it such as switching a note or
// editing a note, it has to be invalidated (using refresh()),
// and a new map will be created at a next scroll event.
if (!doc) return null;
const contentElement = doc.getElementById('joplin-container-content');
if (!contentElement) return null;
const height = Math.max(1, contentElement.scrollHeight - contentElement.clientHeight);
if (this.map_) {
// check whether map_ is obsolete
if (this.map_.viewHeight === height) return this.map_;
this.map_ = null;
}
// Since getBoundingClientRect() returns a relative position,
// the offset of the origin is needed to get its aboslute position.
const offset = doc.getElementById('rendered-md').getBoundingClientRect().top;
if (!offset) return null;
// Mapping information between editor's lines and viewer's elements is
// embedded into elements by the renderer.
// See also renderer/MdToHtml/rules/source_map.ts.
const elems = doc.getElementsByClassName('maps-to-line');
const map: SyncScrollMap = { line: [0], percent: [0], viewHeight: height };
// Each map entry is total-ordered.
let last = 0;
for (let i = 0; i < elems.length; i++) {
const top = elems[i].getBoundingClientRect().top - offset;
const line = Number(elems[i].getAttribute('source-line'));
const percent = Math.max(0, Math.min(1, top / height));
if (map.line[last] < line && map.percent[last] < percent) {
map.line.push(line);
map.percent.push(percent);
last += 1;
}
}
if (map.percent[last] < 1) {
map.line.push(1e10);
map.percent.push(1);
} else {
map.line[last] = 1e10;
}
this.map_ = map;
return map;
}
}

View File

@@ -162,7 +162,6 @@ h2 {
}
}
.form {
display: flex;
flex-direction: column;

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/app-desktop",
"version": "2.5.10",
"version": "2.6.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/app-desktop",
"version": "2.5.10",
"version": "2.6.1",
"license": "MIT",
"dependencies": {
"@electron/remote": "^2.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.5.10",
"version": "2.6.1",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,
@@ -105,7 +105,7 @@
},
"homepage": "https://github.com/laurent22/joplin#readme",
"devDependencies": {
"@joplin/tools": "~2.5",
"@joplin/tools": "~2.6",
"@testing-library/react-hooks": "^3.4.2",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",
@@ -136,8 +136,8 @@
"dependencies": {
"@electron/remote": "^2.0.1",
"@fortawesome/fontawesome-free": "^5.13.0",
"@joplin/lib": "~2.5",
"@joplin/renderer": "~2.5",
"@joplin/lib": "~2.6",
"@joplin/renderer": "~2.6",
"async-mutex": "^0.1.3",
"codemirror": "^5.56.0",
"color": "^3.1.2",

View File

@@ -1,5 +1,6 @@
// This is the API that JS files loaded from the webview can see
const webviewApiPromises_ = {};
let viewMessageHandler_ = () => {};
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const webviewApi = {
@@ -22,6 +23,13 @@ const webviewApi = {
return promise;
},
onMessage: function(viewMessageHandler) {
viewMessageHandler_ = viewMessageHandler;
window.postMessage({
target: 'postMessageService.registerViewMessageHandler',
});
},
};
(function() {
@@ -117,7 +125,7 @@ const webviewApi = {
const message = event.message;
const promise = webviewApiPromises_[message.responseId];
if (!promise) {
console.warn('postMessageService.response: could not find callback for message', message);
console.warn('postMessageService.response: Could not find recorded promise to process message response', message);
return;
}
@@ -127,8 +135,17 @@ const webviewApi = {
promise.resolve(message.response);
}
},
'postMessageService.plugin_message': (message) => {
if (!viewMessageHandler_) {
console.warn('postMessageService.plugin_message: Could not process message because no onMessage handler was defined', message);
return;
}
viewMessageHandler_(message);
},
};
// respond to window.postMessage({})
window.addEventListener('message', ((event) => {
if (!event.data || event.data.target !== 'webview') return;

View File

@@ -16,13 +16,22 @@ export default function(frameWindow: any, isReady: boolean, pluginId: string, vi
if (!frameWindow) return () => {};
function onMessage_(event: any) {
if (!event.data || event.data.target !== 'postMessageService.message') return;
void PostMessageService.instance().postMessage({
pluginId,
viewId,
...event.data.message,
});
if (!event.data || !event.data.target) {
return;
}
if (event.data.target === 'postMessageService.registerViewMessageHandler') {
PostMessageService.instance().registerViewMessageHandler(ResponderComponentType.UserWebview, viewId, (message: MessageResponse) => {
postMessage('postMessageService.plugin_message', { message });
});
} else if (event.data.target === 'postMessageService.message') {
void PostMessageService.instance().postMessage({
pluginId,
viewId,
...event.data.message,
});
}
}
frameWindow.addEventListener('message', onMessage_);

View File

@@ -3,17 +3,18 @@ import Logger from '@joplin/lib/Logger';
import Folder from '@joplin/lib/models/Folder';
import { reg } from '@joplin/lib/registry';
import { _ } from '@joplin/lib/locale';
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
const logger = Logger.create('invitationRespond');
export default async function(shareUserId: string, folderId: string, accept: boolean) {
export default async function(shareUserId: string, folderId: string, masterKey: MasterKeyEntity, accept: boolean) {
// The below functions can take a bit of time to complete so in the
// meantime we hide the notification so that the user doesn't click
// multiple times on the Accept link.
ShareService.instance().setProcessingShareInvitationResponse(true);
try {
await ShareService.instance().respondInvitation(shareUserId, accept);
await ShareService.instance().respondInvitation(shareUserId, masterKey, accept);
} catch (error) {
logger.error(error);
alert(_('Could not respond to the invitation. Please try again, or check with the notebook owner if they are still sharing it.\n\nThe error was: "%s"', error.message));

View File

@@ -0,0 +1,45 @@
import PerFolderSortOrderService from './PerFolderSortOrderService';
import { setNotesSortOrder } from './notesSortOrderUtils';
import Setting from '@joplin/lib/models/Setting';
const { shimInit } = require('@joplin/lib/shim-init-node.js');
const folderId1 = 'aa012345678901234567890123456789';
const folderId2 = 'bb012345678901234567890123456789';
beforeAll(async (done) => {
shimInit();
Setting.autoSaveEnabled = false;
PerFolderSortOrderService.initialize();
Setting.setValue('notes.perFolderSortOrderEnabled', true);
done();
});
describe('PerFolderSortOrderService', () => {
test('get(), isSet() and set()', async (done) => {
// Clear all per-folder sort order
expect(PerFolderSortOrderService.isSet(folderId1)).toBe(false);
expect(PerFolderSortOrderService.isSet(folderId2)).toBe(false);
// Set shared sort order
setNotesSortOrder('user_created_time', false);
expect(Setting.value('notes.sortOrder.field')).toBe('user_created_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
// Manipulate per-folder sort order
PerFolderSortOrderService.set(folderId1, true);
expect(PerFolderSortOrderService.isSet(folderId1)).toBe(true);
PerFolderSortOrderService.set(folderId1, false);
expect(PerFolderSortOrderService.isSet(folderId1)).toBe(false);
PerFolderSortOrderService.set(folderId1);
expect(PerFolderSortOrderService.isSet(folderId1)).toBe(true);
// Get per-folder sort order from a folder with per-folder sort order
expect(PerFolderSortOrderService.get(folderId1)).toBeDefined();
// Folder without per-folder sort order has no per-folder sort order
expect(PerFolderSortOrderService.get(folderId2)).toBeUndefined();
done();
});
});

View File

@@ -0,0 +1,198 @@
import Setting from '@joplin/lib/models/Setting';
import eventManager from '@joplin/lib/eventManager';
import { notesSortOrderFieldArray, setNotesSortOrder } from './notesSortOrderUtils';
const SUFFIX_FIELD = '$field';
const SUFFIX_REVERSE = '$reverse';
export interface SortOrder {
field: string;
reverse: boolean;
}
interface FolderState {
notesParentType: string;
selectedFolderId: string;
}
interface SortOrderPool {
[key: string]: string | boolean;
}
export default class PerFolderSortOrderService {
private static previousFolderId: string = null;
private static folderState: FolderState = { notesParentType: '', selectedFolderId: '' };
// Since perFolderSortOrders and sharedSortOrder is persisted using Setting,
// their structures are not nested.
private static perFolderSortOrders: SortOrderPool = null;
private static sharedSortOrder: SortOrder & SortOrderPool = {
field: 'user_updated_time',
reverse: true,
user_updated_time: true,
user_created_time: true,
title: false,
order: false,
};
public static initialize() {
this.loadPerFolderSortOrders();
this.loadSharedSortOrder();
eventManager.appStateOn('notesParentType', this.onFolderSelectionMayChange.bind(this, 'notesParentType'));
eventManager.appStateOn('selectedFolderId', this.onFolderSelectionMayChange.bind(this, 'selectedFolderId'));
}
public static isSet(folderId: string): boolean {
return folderId && this.perFolderSortOrders && this.perFolderSortOrders.hasOwnProperty(folderId + SUFFIX_FIELD);
}
public static get(folderId: string): SortOrder {
if (folderId && this.perFolderSortOrders) {
const field = this.perFolderSortOrders[folderId + SUFFIX_FIELD] as string;
const reverse = this.perFolderSortOrders[folderId + SUFFIX_REVERSE] as boolean;
if (field) return { field, reverse };
}
return undefined;
}
public static set(folderId?: string, own?: boolean) {
let targetId = folderId;
const selectedId = this.getSelectedFolderId();
if (!targetId) {
targetId = selectedId; // default: selected folder
if (!targetId) return;
}
const targetOwn = this.isSet(targetId);
let newOwn;
if (typeof own === 'undefined') {
newOwn = !targetOwn; // default: toggling
} else {
newOwn = !!own;
if (newOwn === targetOwn) return;
}
if (newOwn) {
let field: string, reverse: boolean;
if (!this.isSet(selectedId)) {
field = Setting.value('notes.sortOrder.field');
reverse = Setting.value('notes.sortOrder.reverse');
} else {
field = this.sharedSortOrder.field;
if (Setting.value('notes.perFieldReversalEnabled')) {
reverse = this.sharedSortOrder[field] as boolean;
} else {
reverse = this.sharedSortOrder.reverse;
}
}
PerFolderSortOrderService.setPerFolderSortOrder(targetId, field, reverse);
} else {
PerFolderSortOrderService.deletePerFolderSortOrder(targetId);
}
}
private static onFolderSelectionMayChange(cause: string, event: any) {
if (cause !== 'notesParentType' && cause !== 'selectedFolderId') return;
this.folderState[cause] = event.value;
const selectedId = this.getSelectedFolderId();
if (this.previousFolderId === selectedId) return;
const field: string = Setting.value('notes.sortOrder.field');
const reverse: boolean = Setting.value('notes.sortOrder.reverse');
let previousFolderHasPerFolderSortOrder = false;
if (this.previousFolderId !== null) {
previousFolderHasPerFolderSortOrder = this.isSet(this.previousFolderId);
if (previousFolderHasPerFolderSortOrder) {
this.setPerFolderSortOrder(this.previousFolderId, field, reverse);
} else {
this.setSharedSortOrder(field, reverse);
}
}
this.previousFolderId = selectedId;
let next: SortOrder;
if (this.isSet(selectedId)) {
next = this.get(selectedId);
} else if (previousFolderHasPerFolderSortOrder) {
next = this.sharedSortOrder;
} else {
return;
}
if (Setting.value('notes.perFolderSortOrderEnabled')) {
if (next.field !== field || next.reverse !== reverse) {
setNotesSortOrder(next.field, next.reverse);
}
}
}
private static getSelectedFolderId(): string {
if (this.folderState.notesParentType === 'Folder') {
return this.folderState.selectedFolderId;
} else {
return '';
}
}
private static loadPerFolderSortOrders() {
this.perFolderSortOrders = { ...Setting.value('notes.perFolderSortOrders') };
}
private static loadSharedSortOrder() {
const validFields = notesSortOrderFieldArray();
const value = Setting.value('notes.sharedSortOrder');
for (const key in this.sharedSortOrder) {
if (value.hasOwnProperty(key)) {
if (key !== 'field' || validFields.includes(value.field)) {
this.sharedSortOrder[key] = value[key];
}
}
}
}
private static setPerFolderSortOrder(folderId: string, field: string, reverse: boolean) {
const old = this.get(folderId);
let dirty = false;
if (!(old?.field === field)) {
this.perFolderSortOrders[folderId + SUFFIX_FIELD] = field;
dirty = true;
}
if (!(old?.reverse === reverse)) {
this.perFolderSortOrders[folderId + SUFFIX_REVERSE] = reverse;
dirty = true;
}
if (dirty) {
Setting.setValue('notes.perFolderSortOrders', { ...this.perFolderSortOrders });
}
}
private static deletePerFolderSortOrder(folderId: string) {
let dirty = false;
if (this.perFolderSortOrders.hasOwnProperty(folderId + SUFFIX_FIELD)) {
delete this.perFolderSortOrders[folderId + SUFFIX_FIELD];
dirty = true;
}
if (this.perFolderSortOrders.hasOwnProperty(folderId + SUFFIX_REVERSE)) {
delete this.perFolderSortOrders[folderId + SUFFIX_REVERSE];
dirty = true;
}
if (dirty) {
Setting.setValue('notes.perFolderSortOrders', { ...this.perFolderSortOrders });
}
}
private static setSharedSortOrder(field: string, reverse: boolean) {
let dirty = false;
if (this.sharedSortOrder.field !== field) {
this.sharedSortOrder.field = field;
dirty = true;
}
if (this.sharedSortOrder.reverse !== reverse) {
this.sharedSortOrder.reverse = reverse;
dirty = true;
}
if (this.sharedSortOrder[field] !== reverse) {
this.sharedSortOrder[field] = reverse;
dirty = true;
}
if (dirty) {
Setting.setValue('notes.sharedSortOrder', { ...this.sharedSortOrder });
}
}
}

View File

@@ -0,0 +1,85 @@
import { notesSortOrderFieldArray, notesSortOrderNextField, setNotesSortOrder } from './notesSortOrderUtils';
import Setting from '@joplin/lib/models/Setting';
const { shimInit } = require('@joplin/lib/shim-init-node.js');
beforeAll(() => {
shimInit();
Setting.autoSaveEnabled = false;
});
describe('notesSortOrderUtils', () => {
it('should always provide the same ordered fields', async () => {
const expected = ['user_updated_time', 'user_created_time', 'title', 'order'];
expect(notesSortOrderFieldArray()).toStrictEqual(expected);
expect(notesSortOrderFieldArray()).toStrictEqual(expected);
});
it('should provide the next field cyclicly', async () => {
expect(notesSortOrderNextField('user_updated_time')).toBe('user_created_time');
expect(notesSortOrderNextField('order')).toBe('user_updated_time');
});
test('setNoteSortOrder(), when perFieldReversalEnabled is false', async () => {
Setting.setValue('notes.perFieldReversalEnabled', false);
// It should set field and reverse of sort order.
setNotesSortOrder('user_created_time', false);
expect(Setting.value('notes.sortOrder.field')).toBe('user_created_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
setNotesSortOrder('user_updated_time', true);
expect(Setting.value('notes.sortOrder.field')).toBe('user_updated_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(true);
setNotesSortOrder('title', true);
expect(Setting.value('notes.sortOrder.field')).toBe('title');
expect(Setting.value('notes.sortOrder.reverse')).toBe(true);
// It should affect the current field of sort order, if arg1 is undefined.
setNotesSortOrder(undefined, false);
expect(Setting.value('notes.sortOrder.field')).toBe('title');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
// it should only set field of sort order, if arg2 is undefined.
setNotesSortOrder('user_updated_time');
expect(Setting.value('notes.sortOrder.field')).toBe('user_updated_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
// It should select the next field, if arg1 and arg2 are undefined.
setNotesSortOrder();
expect(Setting.value('notes.sortOrder.field')).toBe('user_created_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
});
test('setNoteSortOrder(), when perFieldReversalEnabled is true', async () => {
Setting.setValue('notes.perFieldReversalEnabled', true);
// It should set field and reverse of sort order.
setNotesSortOrder('user_created_time', false);
expect(Setting.value('notes.sortOrder.field')).toBe('user_created_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
setNotesSortOrder('user_updated_time', true);
expect(Setting.value('notes.sortOrder.field')).toBe('user_updated_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(true);
setNotesSortOrder('title', true);
expect(Setting.value('notes.sortOrder.field')).toBe('title');
expect(Setting.value('notes.sortOrder.reverse')).toBe(true);
// it should affect the current field of sort order, if arg1 is undefined.
setNotesSortOrder(undefined, false);
expect(Setting.value('notes.sortOrder.field')).toBe('title');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
// It should remember a reverse state, if arg2 is undefined.
setNotesSortOrder('user_updated_time');
expect(Setting.value('notes.sortOrder.field')).toBe('user_updated_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(true);
// It should select the next field and remember a reverse state, if arg1 and arg2 are undefined.
setNotesSortOrder();
expect(Setting.value('notes.sortOrder.field')).toBe('user_created_time');
expect(Setting.value('notes.sortOrder.reverse')).toBe(false);
});
it('should not accept an invalid field name', async () => {
expect(() => setNotesSortOrder('hoge', true)).toThrow();
});
});

View File

@@ -0,0 +1,66 @@
import Setting from '@joplin/lib/models/Setting';
let fields: string[] = null;
let perFieldReverse: { [field: string]: boolean } = null;
export const notesSortOrderFieldArray = (): string[] => {
// The order of the fields is strictly determinate.
if (fields == null) {
fields = Setting.enumOptionValues('notes.sortOrder.field').sort().reverse();
}
return fields;
};
export const notesSortOrderNextField = (currentField: string) => {
const fields = notesSortOrderFieldArray();
const index = fields.indexOf(currentField);
if (index < 0) {
return currentField;
} else {
return fields[(index + 1) % fields.length];
}
};
export const setNotesSortOrder = (field?: string, reverse?: boolean) => {
// field: Sort order's field. undefined means changing a field cyclicly.
// reverse: whether the sort order is reversed or not. undefined means toggling.
let nextField = field;
let nextReverse = reverse;
const currentField = Setting.value('notes.sortOrder.field');
const currentReverse = Setting.value('notes.sortOrder.reverse');
const enabled = Setting.value('notes.perFieldReversalEnabled');
if (enabled) {
if (perFieldReverse === null) {
perFieldReverse = { ...Setting.value('notes.perFieldReverse') };
}
}
if (typeof field === 'undefined') {
if (typeof reverse === 'undefined') {
// If both arguments are undefined, the next field is selected.
nextField = notesSortOrderNextField(currentField);
} else {
nextField = currentField;
}
}
if (typeof reverse === 'undefined') {
if (enabled && perFieldReverse.hasOwnProperty(nextField)) {
nextReverse = !!perFieldReverse[nextField];
} else {
nextReverse = currentReverse;
}
}
if (currentField !== nextField) {
Setting.setValue('notes.sortOrder.field', nextField);
}
if (currentReverse !== nextReverse) {
Setting.setValue('notes.sortOrder.reverse', nextReverse);
}
if (enabled) {
// nextField is sane here.
nextReverse = !!nextReverse;
if (perFieldReverse[nextField] !== nextReverse) {
perFieldReverse[nextField] = nextReverse;
Setting.setValue('notes.perFieldReverse', { ...perFieldReverse });
}
}
};

View File

@@ -28,6 +28,7 @@ build/
.gradle
local.properties
*.iml
*.hprof
# node.js
#

View File

@@ -123,6 +123,11 @@ def jscFlavor = 'org.webkit:android-jsc-intl:+'
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for in debug.
*/
def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures")
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -141,8 +146,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097656
versionName "2.5.5"
versionCode 2097657
versionName "2.6.1"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
@@ -180,6 +185,11 @@ android {
buildTypes {
debug {
signingConfig signingConfigs.debug
if (nativeArchitectures) {
ndk {
abiFilters nativeArchitectures.split(',')
}
}
}
release {
// Caution! In production, you need to generate your own keystore file.
@@ -215,7 +225,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
@@ -242,7 +252,7 @@ dependencies {
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
from configurations.implementation
into 'libs'
}

View File

@@ -3,7 +3,6 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:textColor">#000000</item>
</style>
</resources>

View File

@@ -2,18 +2,18 @@
buildscript {
ext {
buildToolsVersion = "29.0.3"
buildToolsVersion = "30.0.2"
minSdkVersion = 21
compileSdkVersion = 29
targetSdkVersion = 29
ndkVersion = "20.1.5948944"
compileSdkVersion = 30
targetSdkVersion = 30
ndkVersion = "21.4.7075529"
}
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:4.1.0")
classpath("com.android.tools.build:gradle:4.2.2")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -21,6 +21,7 @@ buildscript {
allprojects {
repositories {
mavenCentral()
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
@@ -32,7 +33,6 @@ allprojects {
}
google()
jcenter()
maven { url 'https://www.jitpack.io' }
}
}

View File

@@ -26,4 +26,4 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.75.1
FLIPPER_VERSION=0.99.0

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -492,13 +492,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 74;
CURRENT_PROJECT_VERSION = 77;
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 12.5.0;
MARKETING_VERSION = 12.6.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -521,12 +521,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 74;
CURRENT_PROJECT_VERSION = 77;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 12.5.0;
MARKETING_VERSION = 12.6.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -588,7 +588,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = (
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
@@ -641,7 +641,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = (
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
@@ -667,14 +667,14 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 74;
CURRENT_PROJECT_VERSION = 77;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 12.5.0;
MARKETING_VERSION = 12.6.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
@@ -698,14 +698,14 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 74;
CURRENT_PROJECT_VERSION = 77;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 12.5.0;
MARKETING_VERSION = 12.6.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,9 +1,12 @@
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
# Note: it was 13.4 to get @react-native-community/datetimepicker to work
# but it's probably not necessary actually. Just needed to upgrade XCode.
platform :ios, '10.0'
# Note: it was 13.4 to get @react-native-community/datetimepicker to work but
# it's probably not necessary actually. Just needed to upgrade XCode.
#
# 2021-11-04: Set to 13.0 because it crashes with 12.x
# https://github.com/laurent22/joplin/issues/5671
platform :ios, '13.0'
target 'Joplin' do
config = use_native_modules!
@@ -24,6 +27,7 @@ target 'Joplin' do
# use_flipper!()
# post_install do |installer|
# react_native_post_install(installer)
# __apply_Xcode_12_5_M1_post_install_workaround(installer)
# end
# RN 0.63:

View File

@@ -1,212 +1,218 @@
PODS:
- boost-for-react-native (1.63.0)
- boost (1.76.0)
- DoubleConversion (1.1.6)
- FBLazyVector (0.64.2)
- FBReactNativeSpec (0.64.2):
- RCT-Folly (= 2020.01.13.00)
- RCTRequired (= 0.64.2)
- RCTTypeSafety (= 0.64.2)
- React-Core (= 0.64.2)
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- FBLazyVector (0.66.1)
- FBReactNativeSpec (0.66.1):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.66.1)
- RCTTypeSafety (= 0.66.1)
- React-Core (= 0.66.1)
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- fmt (6.2.1)
- glog (0.3.5)
- JoplinCommonShareExtension (1.0.0)
- JoplinRNShareExtension (1.0.0):
- JoplinCommonShareExtension
- React (= 0.64.2)
- RCT-Folly (2020.01.13.00):
- boost-for-react-native
- React (= 0.66.1)
- RCT-Folly (2021.06.28.00-v2):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCT-Folly/Default (= 2021.06.28.00-v2)
- RCT-Folly/Default (2021.06.28.00-v2):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCTRequired (0.66.1)
- RCTTypeSafety (0.66.1):
- FBLazyVector (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.66.1)
- React-Core (= 0.66.1)
- React (0.66.1):
- React-Core (= 0.66.1)
- React-Core/DevSupport (= 0.66.1)
- React-Core/RCTWebSocket (= 0.66.1)
- React-RCTActionSheet (= 0.66.1)
- React-RCTAnimation (= 0.66.1)
- React-RCTBlob (= 0.66.1)
- React-RCTImage (= 0.66.1)
- React-RCTLinking (= 0.66.1)
- React-RCTNetwork (= 0.66.1)
- React-RCTSettings (= 0.66.1)
- React-RCTText (= 0.66.1)
- React-RCTVibration (= 0.66.1)
- React-callinvoker (0.66.1)
- React-Core (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.66.1)
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/CoreModulesHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/Default (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/DevSupport (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.66.1)
- React-Core/RCTWebSocket (= 0.66.1)
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-jsinspector (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTActionSheetHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTAnimationHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTBlobHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTImageHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTLinkingHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTNetworkHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTSettingsHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTTextHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTVibrationHeaders (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-Core/RCTWebSocket (0.66.1):
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.66.1)
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsiexecutor (= 0.66.1)
- React-perflogger (= 0.66.1)
- Yoga
- React-CoreModules (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.66.1)
- React-Core/CoreModulesHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- React-RCTImage (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-cxxreact (0.66.1):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly/Default (= 2020.01.13.00)
- RCT-Folly/Default (2020.01.13.00):
- boost-for-react-native
- RCT-Folly (= 2021.06.28.00-v2)
- React-callinvoker (= 0.66.1)
- React-jsi (= 0.66.1)
- React-jsinspector (= 0.66.1)
- React-logger (= 0.66.1)
- React-perflogger (= 0.66.1)
- React-runtimeexecutor (= 0.66.1)
- React-jsi (0.66.1):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCTRequired (0.64.2)
- RCTTypeSafety (0.64.2):
- FBLazyVector (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- RCTRequired (= 0.64.2)
- React-Core (= 0.64.2)
- React (0.64.2):
- React-Core (= 0.64.2)
- React-Core/DevSupport (= 0.64.2)
- React-Core/RCTWebSocket (= 0.64.2)
- React-RCTActionSheet (= 0.64.2)
- React-RCTAnimation (= 0.64.2)
- React-RCTBlob (= 0.64.2)
- React-RCTImage (= 0.64.2)
- React-RCTLinking (= 0.64.2)
- React-RCTNetwork (= 0.64.2)
- React-RCTSettings (= 0.64.2)
- React-RCTText (= 0.64.2)
- React-RCTVibration (= 0.64.2)
- React-callinvoker (0.64.2)
- React-Core (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default (= 0.64.2)
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/CoreModulesHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/Default (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/DevSupport (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default (= 0.64.2)
- React-Core/RCTWebSocket (= 0.64.2)
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-jsinspector (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTActionSheetHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTAnimationHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTBlobHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTImageHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTLinkingHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTNetworkHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTSettingsHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTTextHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTVibrationHeaders (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-Core/RCTWebSocket (0.64.2):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-Core/Default (= 0.64.2)
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsiexecutor (= 0.64.2)
- React-perflogger (= 0.64.2)
- Yoga
- React-CoreModules (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.64.2)
- React-Core/CoreModulesHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- React-RCTImage (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-cxxreact (0.64.2):
- boost-for-react-native (= 1.63.0)
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsi/Default (= 0.66.1)
- React-jsi/Default (0.66.1):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly (= 2020.01.13.00)
- React-callinvoker (= 0.64.2)
- React-jsi (= 0.64.2)
- React-jsinspector (= 0.64.2)
- React-perflogger (= 0.64.2)
- React-runtimeexecutor (= 0.64.2)
- React-jsi (0.64.2):
- boost-for-react-native (= 1.63.0)
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsiexecutor (0.66.1):
- DoubleConversion
- glog
- RCT-Folly (= 2020.01.13.00)
- React-jsi/Default (= 0.64.2)
- React-jsi/Default (0.64.2):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- RCT-Folly (= 2021.06.28.00-v2)
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-perflogger (= 0.66.1)
- React-jsinspector (0.66.1)
- React-logger (0.66.1):
- glog
- RCT-Folly (= 2020.01.13.00)
- React-jsiexecutor (0.64.2):
- DoubleConversion
- glog
- RCT-Folly (= 2020.01.13.00)
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-perflogger (= 0.64.2)
- React-jsinspector (0.64.2)
- react-native-alarm-notification (1.0.3):
- React
- react-native-camera (3.40.0):
@@ -237,70 +243,71 @@ PODS:
- React-Core
- react-native-webview (10.9.2):
- React-Core
- React-perflogger (0.64.2)
- React-RCTActionSheet (0.64.2):
- React-Core/RCTActionSheetHeaders (= 0.64.2)
- React-RCTAnimation (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.64.2)
- React-Core/RCTAnimationHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-RCTBlob (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- React-Core/RCTBlobHeaders (= 0.64.2)
- React-Core/RCTWebSocket (= 0.64.2)
- React-jsi (= 0.64.2)
- React-RCTNetwork (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-RCTImage (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.64.2)
- React-Core/RCTImageHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- React-RCTNetwork (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-RCTLinking (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- React-Core/RCTLinkingHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-RCTNetwork (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.64.2)
- React-Core/RCTNetworkHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-RCTSettings (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.64.2)
- React-Core/RCTSettingsHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-RCTText (0.64.2):
- React-Core/RCTTextHeaders (= 0.64.2)
- React-RCTVibration (0.64.2):
- FBReactNativeSpec (= 0.64.2)
- RCT-Folly (= 2020.01.13.00)
- React-Core/RCTVibrationHeaders (= 0.64.2)
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (= 0.64.2)
- React-runtimeexecutor (0.64.2):
- React-jsi (= 0.64.2)
- ReactCommon/turbomodule/core (0.64.2):
- React-perflogger (0.66.1)
- React-RCTActionSheet (0.66.1):
- React-Core/RCTActionSheetHeaders (= 0.66.1)
- React-RCTAnimation (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.66.1)
- React-Core/RCTAnimationHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-RCTBlob (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/RCTBlobHeaders (= 0.66.1)
- React-Core/RCTWebSocket (= 0.66.1)
- React-jsi (= 0.66.1)
- React-RCTNetwork (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-RCTImage (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.66.1)
- React-Core/RCTImageHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- React-RCTNetwork (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-RCTLinking (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- React-Core/RCTLinkingHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-RCTNetwork (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.66.1)
- React-Core/RCTNetworkHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-RCTSettings (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.66.1)
- React-Core/RCTSettingsHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-RCTText (0.66.1):
- React-Core/RCTTextHeaders (= 0.66.1)
- React-RCTVibration (0.66.1):
- FBReactNativeSpec (= 0.66.1)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/RCTVibrationHeaders (= 0.66.1)
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (= 0.66.1)
- React-runtimeexecutor (0.66.1):
- React-jsi (= 0.66.1)
- ReactCommon/turbomodule/core (0.66.1):
- DoubleConversion
- glog
- RCT-Folly (= 2020.01.13.00)
- React-callinvoker (= 0.64.2)
- React-Core (= 0.64.2)
- React-cxxreact (= 0.64.2)
- React-jsi (= 0.64.2)
- React-perflogger (= 0.64.2)
- RCT-Folly (= 2021.06.28.00-v2)
- React-callinvoker (= 0.66.1)
- React-Core (= 0.66.1)
- React-cxxreact (= 0.66.1)
- React-jsi (= 0.66.1)
- React-logger (= 0.66.1)
- React-perflogger (= 0.66.1)
- rn-fetch-blob (0.12.0):
- React-Core
- RNCClipboard (1.5.0):
@@ -317,13 +324,14 @@ PODS:
- React
- RNSecureRandom (1.0.0-rc.0):
- React
- RNShare (5.1.5):
- RNShare (7.2.1):
- React-Core
- RNVectorIcons (7.1.0):
- React
- Yoga (1.14.0)
DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
@@ -343,6 +351,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-logger (from `../node_modules/react-native/ReactCommon/logger`)
- 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`)
@@ -381,9 +390,11 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- boost-for-react-native
- fmt
EXTERNAL SOURCES:
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
FBLazyVector:
@@ -418,6 +429,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
react-native-alarm-notification:
:path: "../node_modules/joplin-rn-alarm-notification"
react-native-camera:
@@ -490,24 +503,26 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
FBReactNativeSpec: 009b310a5134a345e702b4402de70b5ee2bb4832
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
boost: a7c83b31436843459a1961bfd74b96033dc77234
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: 500821d196c3d1bd10e7e828bc93ce075234080f
FBReactNativeSpec: 74c869e2cffa2ffec685cd1bac6788c021da6005
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 5337263514dd6f09803962437687240c5dc39aa4
JoplinCommonShareExtension: 270b4f8eb4e22828eeda433a04ed689fc1fd09b5
JoplinRNShareExtension: 7137e9787374e1b0797ecbef9103d1588d90e403
RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c
RCTRequired: 6d3e854f0e7260a648badd0d44fc364bc9da9728
RCTTypeSafety: c1f31d19349c6b53085766359caac425926fafaa
React: bda6b6d7ae912de97d7a61aa5c160db24aa2ad69
React-callinvoker: 9840ea7e8e88ed73d438edb725574820b29b5baa
React-Core: b5e385da7ce5f16a220fc60fd0749eae2c6120f0
React-CoreModules: 17071a4e2c5239b01585f4aa8070141168ab298f
React-cxxreact: 9be7b6340ed9f7c53e53deca7779f07cd66525ba
React-jsi: 67747b9722f6dab2ffe15b011bcf6b3f2c3f1427
React-jsiexecutor: 80c46bd381fd06e418e0d4f53672dc1d1945c4c3
React-jsinspector: cc614ec18a9ca96fd275100c16d74d62ee11f0ae
JoplinRNShareExtension: cb790ce4c0692367acd1a06c56330c9a440f8b58
RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9
RCTRequired: 3cc065b52aa18db729268b9bd78a2feffb4d0f91
RCTTypeSafety: 3c4fc37d5dea452d2ef17324db5504ec2f05083a
React: 4a00720816c52a213424442954acb7e4b724804a
React-callinvoker: 911fc6570538f3bb5c61edf9dc907c1beb4355bf
React-Core: e134d3a5d7b2a1a731589be776e20dbb14868f27
React-CoreModules: 2f8588b2aa47e7fef27125c8eaaabda963b3ac62
React-cxxreact: 8f1382538cad0cc8b8eafca6d66268828e353bea
React-jsi: 9fe1854d2c0486216acebd5db3c38b4ccb23ca0b
React-jsiexecutor: db2f6e22a534d466fc0e34e622df47d9d20bab2f
React-jsinspector: 8c0517dee5e8c70cd6c3066f20213ff7ce54f176
React-logger: bfddd3418dc1d45b77b822958f3e31422e2c179b
react-native-alarm-notification: 466e4ad56fbd948ecac26e657f292dca8bf483d5
react-native-camera: 35854c4f764a4a6cf61c1c3525888b92f0fe4b31
react-native-document-picker: 0bba80cc56caab1f67dbaa81ff557e3a9b7f2b9f
@@ -520,18 +535,18 @@ SPEC CHECKSUMS:
react-native-sqlite-storage: 418ef4afc5e6df6ce3574c4617e5f0b65cffde55
react-native-version-info: 36490da17d2c6b5cc21321c70e433784dee7ed0b
react-native-webview: 4e96d493f9f90ba4f03b28933f30b2964df07e39
React-perflogger: 25373e382fed75ce768a443822f07098a15ab737
React-RCTActionSheet: af7796ba49ffe4ca92e7277a5d992d37203f7da5
React-RCTAnimation: 6a2e76ab50c6f25b428d81b76a5a45351c4d77aa
React-RCTBlob: 02a2887023e0eed99391b6445b2e23a2a6f9226d
React-RCTImage: ce5bf8e7438f2286d9b646a05d6ab11f38b0323d
React-RCTLinking: ccd20742de14e020cb5f99d5c7e0bf0383aefbd9
React-RCTNetwork: dfb9d089ab0753e5e5f55fc4b1210858f7245647
React-RCTSettings: b14aef2d83699e48b410fb7c3ba5b66cd3291ae2
React-RCTText: 41a2e952dd9adc5caf6fb68ed46b275194d5da5f
React-RCTVibration: 24600e3b1aaa77126989bc58b6747509a1ba14f3
React-runtimeexecutor: a9904c6d0218fb9f8b19d6dd88607225927668f9
ReactCommon: 149906e01aa51142707a10665185db879898e966
React-perflogger: fcac6090a80e3d967791b4c7f1b1a017f9d4a398
React-RCTActionSheet: caf5913d9f9e605f5467206cf9d1caa6d47d7ad6
React-RCTAnimation: 6539e3bf594f6a529cd861985ba6548286ae1ead
React-RCTBlob: 6e2e999d28b15fd03ed533f164ce33e0fcde571a
React-RCTImage: c6bbb10eedb6b840c4474f2108b864173b83de15
React-RCTLinking: 8fda9bb8fdb104e78110a903a9a77754318c7d11
React-RCTNetwork: 2b26daad93830501cf14aab03eac04e304f942d3
React-RCTSettings: 89c0dcee7adb706c749383596f57c1e882a27843
React-RCTText: 71734fce8e6cb854daeb4a5eec182c303ea58473
React-RCTVibration: 6600b5eed7c0fda4a433fa1198d1cb2690151791
React-runtimeexecutor: 33a949a51bec5f8a3c9e8d8092deb259600d761e
ReactCommon: 620442811dc6f707b4bf5e3b27d4f19c12d5a821
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCClipboard: c7abea1baea58adca5c1f29e56dd5261837b4892
RNCPushNotificationIOS: ec7ffe65c7b5097f8d287fd627e1c1674ea69cef
@@ -540,10 +555,10 @@ SPEC CHECKSUMS:
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNSecureRandom: 1f19ad1492f7ed416b8fc79e92216a1f73f13a4c
RNShare: 9cdd23357981cf4dee275eb79239e860dccc0faf
RNShare: edd621a71124961e29a7ba43a84bd1c6f9980d88
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac
Yoga: 2b4a01651f42a32f82e6cef3830a3ba48088237f
PODFILE CHECKSUM: 6aeb91c381b1a03c0c8c78b2f504699bd569b7e3
PODFILE CHECKSUM: 3ccf11f600ddb42a825b2bb9a341a19f5c891f2b
COCOAPODS: 1.10.2

View File

@@ -9,6 +9,6 @@ Pod::Spec.new do |spec|
spec.platform = :ios, "9.0"
spec.source = { :path => "." }
spec.source_files = "Source/RNShareExtension/**/*.{h,m}"
spec.dependency "React", "0.64.2"
spec.dependency "React", "0.66.1"
spec.dependency "JoplinCommonShareExtension"
end

File diff suppressed because it is too large Load Diff

View File

@@ -15,8 +15,8 @@
"postinstall": "jetify && npm run build"
},
"dependencies": {
"@joplin/lib": "~2.5",
"@joplin/renderer": "~2.5",
"@joplin/lib": "~2.6",
"@joplin/renderer": "~2.6",
"@react-native-community/clipboard": "^1.5.0",
"@react-native-community/datetimepicker": "^3.0.3",
"@react-native-community/geolocation": "^2.0.2",
@@ -33,8 +33,8 @@
"md5": "^2.2.1",
"prop-types": "^15.6.0",
"punycode": "^2.1.1",
"react": "17.0.1",
"react-native": "0.64.2",
"react": "17.0.2",
"react-native": "0.66.1",
"react-native-action-button": "^2.8.5",
"react-native-camera": "^3.40.0",
"react-native-dialogbox": "^0.6.10",
@@ -50,7 +50,7 @@
"react-native-quick-actions": "^0.3.13",
"react-native-rsa-native": "^2.0.4",
"react-native-securerandom": "^1.0.0-rc.0",
"react-native-share": "^5.1.5",
"react-native-share": "^7.2.1",
"react-native-side-menu": "^1.1.3",
"react-native-sqlite-storage": "^5.0.0",
"react-native-vector-icons": "^7.1.0",
@@ -73,7 +73,7 @@
"@codemirror/lang-markdown": "^0.18.4",
"@codemirror/state": "^0.18.7",
"@codemirror/view": "^0.18.19",
"@joplin/tools": "~2.5",
"@joplin/tools": "~2.6",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-typescript": "^8.2.1",
"@types/node": "^14.14.6",
@@ -84,7 +84,7 @@
"fs-extra": "^8.1.0",
"gulp": "^4.0.2",
"jetifier": "^1.6.5",
"metro-react-native-babel-preset": "^0.64.0",
"metro-react-native-babel-preset": "^0.66.2",
"nodemon": "^2.0.12",
"rollup": "^2.53.1",
"typescript": "^4.0.5",

View File

@@ -505,9 +505,9 @@ async function initialize(dispatch: Function) {
Setting.setValue('sync.10.path', 'http://api.joplincloud.local:22300');
Setting.setValue('sync.10.userContentPath', 'http://joplinusercontent.local:22300');
Setting.setValue('sync.target', 10);
Setting.setValue('sync.10.username', 'user1@example.com');
Setting.setValue('sync.10.password', 'hunter1hunter2hunter3');
// Setting.setValue('sync.target', 10);
// Setting.setValue('sync.10.username', 'user1@example.com');
// Setting.setValue('sync.10.password', 'hunter1hunter2hunter3');
}
if (Setting.value('db.ftsEnabled') === -1) {
@@ -561,7 +561,7 @@ async function initialize(dispatch: Function) {
// / E2EE SETUP
// ----------------------------------------------------------------
await ShareService.instance().initialize(store);
await ShareService.instance().initialize(store, EncryptionService.instance());
reg.logger().info('Loading folders...');

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"id": "<%= pluginId %>",
"app_min_version": "2.5",
"app_min_version": "2.6",
"version": "1.0.0",
"name": "<%= pluginName %>",
"description": "<%= pluginDescription %>",

View File

@@ -1,6 +1,6 @@
{
"name": "generator-joplin",
"version": "2.5.0",
"version": "2.6.0",
"description": "Scaffolds out a new Joplin plugin",
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
"author": {

View File

@@ -488,7 +488,6 @@ export default class BaseApplication {
// appLogger.debug('Reducer action', this.reducerActionToString(action));
const result = next(action);
const newState = store.getState();
let refreshNotes = false;
let refreshFolders: boolean | string = false;
// let refreshTags = false;
@@ -496,6 +495,7 @@ export default class BaseApplication {
let refreshNotesHash = '';
await reduxSharedMiddleware(store, next, action);
const newState = store.getState();
if (this.hasGui() && ['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
if (!(await reg.syncTarget().syncStarted())) void reg.scheduleSync(30 * 1000, { syncSteps: ['update_remote', 'delete_remote'] });
@@ -637,7 +637,7 @@ export default class BaseApplication {
BaseSyncTarget.dispatch = this.store().dispatch;
DecryptionWorker.instance().dispatch = this.store().dispatch;
ResourceFetcher.instance().dispatch = this.store().dispatch;
ShareService.instance().initialize(this.store());
ShareService.instance().initialize(this.store(), EncryptionService.instance());
}
public deinitRedux() {

View File

@@ -351,7 +351,7 @@ export default class JoplinDatabase extends Database {
// must be set in the synchronizer too.
// Note: v16 and v17 don't do anything. They were used to debug an issue.
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39];
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
@@ -900,10 +900,11 @@ export default class JoplinDatabase extends Database {
queries.push('ALTER TABLE `notes` ADD COLUMN conflict_original_id TEXT NOT NULL DEFAULT ""');
}
// if (targetVersion == 40) {
// queries.push('ALTER TABLE `folders` ADD COLUMN master_key_id TEXT NOT NULL DEFAULT ""');
// queries.push('ALTER TABLE `notes` ADD COLUMN master_key_id TEXT NOT NULL DEFAULT ""');
// }
if (targetVersion == 40) {
queries.push('ALTER TABLE `folders` ADD COLUMN master_key_id TEXT NOT NULL DEFAULT ""');
queries.push('ALTER TABLE `notes` ADD COLUMN master_key_id TEXT NOT NULL DEFAULT ""');
queries.push('ALTER TABLE `resources` ADD COLUMN master_key_id TEXT NOT NULL DEFAULT ""');
}
const updateVersionQuery = { sql: 'UPDATE version SET version = ?', params: [targetVersion] };

View File

@@ -142,7 +142,7 @@ export default class JoplinServerApi {
}
if (sessionId) headers['X-API-AUTH'] = sessionId;
headers['X-API-MIN-VERSION'] = '2.1.4';
headers['X-API-MIN-VERSION'] = '2.6.0'; // Need server 2.6 for new lock support
const fetchOptions: any = {};
fetchOptions.headers = headers;
@@ -253,8 +253,12 @@ export default class JoplinServerApi {
const output = await loadResponseJson();
return output;
} catch (error) {
if (error.code !== 404) {
// Don't print error info for file not found (handled by the
// driver), or lock-acquisition errors because it's handled by
// LockHandler.
if (![404, 'hasExclusiveLock', 'hasSyncLock'].includes(error.code)) {
logger.warn(this.requestToCurl_(url, fetchOptions));
logger.warn('Code:', error.code);
logger.warn(error);
}

View File

@@ -1,6 +1,6 @@
import Logger from './Logger';
import LockHandler, { LockType } from './services/synchronizer/LockHandler';
import Setting from './models/Setting';
import LockHandler, { hasActiveLock, LockClientType, LockType } from './services/synchronizer/LockHandler';
import Setting, { AppType } from './models/Setting';
import shim from './shim';
import MigrationHandler from './services/synchronizer/MigrationHandler';
import eventManager from './eventManager';
@@ -66,6 +66,7 @@ export default class Synchronizer {
private resourceService_: ResourceService = null;
private syncTargetIsLocked_: boolean = false;
private shareService_: ShareService = null;
private lockClientType_: LockClientType = null;
// Debug flags are used to test certain hard-to-test conditions
// such as cancelling in the middle of a loop.
@@ -120,9 +121,21 @@ export default class Synchronizer {
return this.lockHandler_;
}
private lockClientType(): LockClientType {
if (this.lockClientType_) return this.lockClientType_;
if (this.appType_ === AppType.Desktop) this.lockClientType_ = LockClientType.Desktop;
if (this.appType_ === AppType.Mobile) this.lockClientType_ = LockClientType.Mobile;
if (this.appType_ === AppType.Cli) this.lockClientType_ = LockClientType.Cli;
if (!this.lockClientType_) throw new Error(`Invalid client type: ${this.appType_}`);
return this.lockClientType_;
}
migrationHandler() {
if (this.migrationHandler_) return this.migrationHandler_;
this.migrationHandler_ = new MigrationHandler(this.api(), this.db(), this.lockHandler(), this.appType_, this.clientId_);
this.migrationHandler_ = new MigrationHandler(this.api(), this.db(), this.lockHandler(), this.lockClientType(), this.clientId_);
return this.migrationHandler_;
}
@@ -164,6 +177,12 @@ export default class Synchronizer {
return !!report && !!report.errors && !!report.errors.length;
}
private static completionTime(report: any): string {
const duration = report.completedTime - report.startTime;
if (duration > 1000) return `${Math.round(duration / 1000)}s`;
return `${duration}ms`;
}
static reportToLines(report: any) {
const lines = [];
if (report.createLocal) lines.push(_('Created local items: %d.', report.createLocal));
@@ -174,7 +193,7 @@ export default class Synchronizer {
if (report.deleteRemote) lines.push(_('Deleted remote items: %d.', report.deleteRemote));
if (report.fetchingTotal && report.fetchingProcessed) lines.push(_('Fetched items: %d/%d.', report.fetchingProcessed, report.fetchingTotal));
if (report.cancelling && !report.completedTime) lines.push(_('Cancelling...'));
if (report.completedTime) lines.push(_('Completed: %s (%s)', time.formatMsToLocal(report.completedTime), `${Math.round((report.completedTime - report.startTime) / 1000)}s`));
if (report.completedTime) lines.push(_('Completed: %s (%s)', time.formatMsToLocal(report.completedTime), this.completionTime(report)));
if (this.reportHasErrors(report)) lines.push(_('Last error: %s', report.errors[report.errors.length - 1].toString().substr(0, 500)));
return lines;
@@ -298,10 +317,13 @@ export default class Synchronizer {
}
async lockErrorStatus_() {
const hasActiveExclusiveLock = await this.lockHandler().hasActiveLock(LockType.Exclusive);
const locks = await this.lockHandler().locks();
const currentDate = await this.lockHandler().currentDate();
const hasActiveExclusiveLock = await hasActiveLock(locks, currentDate, this.lockHandler().lockTtl, LockType.Exclusive);
if (hasActiveExclusiveLock) return 'hasExclusiveLock';
const hasActiveSyncLock = await this.lockHandler().hasActiveLock(LockType.Sync, this.appType_, this.clientId_);
const hasActiveSyncLock = await hasActiveLock(locks, currentDate, this.lockHandler().lockTtl, LockType.Sync, this.lockClientType(), this.clientId_);
if (!hasActiveSyncLock) return 'syncLockGone';
return '';
@@ -446,10 +468,10 @@ export default class Synchronizer {
const previousE2EE = localInfo.e2ee;
logger.info('Sync target info differs between local and remote - merging infos: ', newInfo.toObject());
await this.lockHandler().acquireLock(LockType.Exclusive, this.appType_, this.clientId_, { clearExistingSyncLocksFromTheSameClient: true });
await this.lockHandler().acquireLock(LockType.Exclusive, this.lockClientType(), this.clientId_, { clearExistingSyncLocksFromTheSameClient: true });
await uploadSyncInfo(this.api(), newInfo);
await saveLocalSyncInfo(newInfo);
await this.lockHandler().releaseLock(LockType.Exclusive, this.appType_, this.clientId_);
await this.lockHandler().releaseLock(LockType.Exclusive, this.lockClientType(), this.clientId_);
// console.info('NEW', newInfo);
@@ -473,7 +495,7 @@ export default class Synchronizer {
throw error;
}
syncLock = await this.lockHandler().acquireLock(LockType.Sync, this.appType_, this.clientId_);
syncLock = await this.lockHandler().acquireLock(LockType.Sync, this.lockClientType(), this.clientId_);
this.lockHandler().startAutoLockRefresh(syncLock, (error: any) => {
logger.warn('Could not refresh lock - cancelling sync. Error was:', error);
@@ -1084,7 +1106,7 @@ export default class Synchronizer {
if (syncLock) {
this.lockHandler().stopAutoLockRefresh(syncLock);
await this.lockHandler().releaseLock(LockType.Sync, this.appType_, this.clientId_);
await this.lockHandler().releaseLock(LockType.Sync, this.lockClientType(), this.clientId_);
}
this.syncTargetIsLocked_ = false;

View File

@@ -198,14 +198,19 @@ export const useNeedMasterPassword = (passwordChecks: PasswordChecks, masterKeys
return false;
};
export const upgradeMasterKey = async (masterKey: MasterKeyEntity, passwordChecks: PasswordChecks, passwords: Record<string, string>): Promise<string> => {
const passwordCheck = passwordChecks[masterKey.id];
if (!passwordCheck) {
export const determineKeyPassword = (masterKeyId: string, masterPasswordKeys: PasswordChecks, masterPassword: string, passwords: Record<string, string>): string => {
if (masterPasswordKeys[masterKeyId]) return masterPassword;
return passwords[masterKeyId];
};
export const upgradeMasterKey = async (masterKey: MasterKeyEntity, password: string): Promise<string> => {
if (!password) {
return _('Please enter your password in the master key list below before upgrading the key.');
}
try {
const password = passwords[masterKey.id];
// Just re-encrypt the master key, but using the new encryption method
// (which would be the default).
const newMasterKey = await EncryptionService.instance().reencryptMasterKey(masterKey, password, password);
await MasterKey.save(newMasterKey);
void reg.waitForSyncFinishedThenSync();

View File

@@ -2,6 +2,7 @@ import { MultiPutItem } from './file-api';
import JoplinError from './JoplinError';
import JoplinServerApi from './JoplinServerApi';
import { trimSlashes } from './path-utils';
import { Lock, LockClientType, LockType } from './services/synchronizer/LockHandler';
// All input paths should be in the format: "path/to/file". This is converted to
// "root:/path/to/file:" when doing the API call.
@@ -40,6 +41,10 @@ export default class FileApiDriverJoplinServer {
return true;
}
public get supportsLocks() {
return true;
}
public requestRepeatCount() {
return 3;
}
@@ -196,6 +201,50 @@ export default class FileApiDriverJoplinServer {
throw new Error('Not supported');
}
// private lockClientTypeToId(clientType:AppType):number {
// if (clientType === AppType.Desktop) return 1;
// if (clientType === AppType.Mobile) return 2;
// if (clientType === AppType.Cli) return 3;
// throw new Error('Invalid client type: ' + clientType);
// }
// private lockTypeToId(lockType:LockType):number {
// if (lockType === LockType.None) return 0; // probably not possible?
// if (lockType === LockType.Sync) return 1;
// if (lockType === LockType.Exclusive) return 2;
// throw new Error('Invalid lock type: ' + lockType);
// }
// private lockClientIdTypeToType(clientType:number):AppType {
// if (clientType === 1) return AppType.Desktop;
// if (clientType === 2) return AppType.Mobile;
// if (clientType === 3) return AppType.Cli;
// throw new Error('Invalid client type: ' + clientType);
// }
// private lockIdToType(lockType:number):LockType {
// if (lockType === 0) return LockType.None; // probably not possible?
// if (lockType === 1) return LockType.Sync;
// if (lockType === 2) return LockType.Exclusive;
// throw new Error('Invalid lock type: ' + lockType);
// }
public async acquireLock(type: LockType, clientType: LockClientType, clientId: string): Promise<Lock> {
return this.api().exec('POST', 'api/locks', null, {
type,
clientType,
clientId: clientId,
});
}
public async releaseLock(type: LockType, clientType: LockClientType, clientId: string) {
await this.api().exec('DELETE', `api/locks/${type}_${clientType}_${clientId}`);
}
public async listLocks() {
return this.api().exec('GET', 'api/locks');
}
public async clearRoot(path: string) {
const response = await this.list(path);
@@ -203,6 +252,8 @@ export default class FileApiDriverJoplinServer {
await this.delete(item.path);
}
await this.api().exec('POST', 'api/debug', null, { action: 'clearKeyValues' });
if (response.has_more) throw new Error('has_more support not implemented');
}
}

View File

@@ -5,6 +5,7 @@ import time from './time';
const { isHidden } = require('./path-utils');
import JoplinError from './JoplinError';
import { Lock, LockClientType, LockType } from './services/synchronizer/LockHandler';
const ArrayUtils = require('./ArrayUtils');
const { sprintf } = require('sprintf-js');
const Mutex = require('async-mutex').Mutex;
@@ -36,7 +37,7 @@ export interface RemoteItem {
export interface PaginatedList {
items: RemoteItem[];
has_more: boolean;
hasMore: boolean;
context: any;
}
@@ -130,6 +131,10 @@ class FileApi {
return !!this.driver().supportsAccurateTimestamp;
}
public get supportsLocks(): boolean {
return !!this.driver().supportsLocks;
}
async fetchRemoteDateOffset_() {
const tempFile = `${this.tempDirName()}/timeCheck${Math.round(Math.random() * 1000000)}.txt`;
const startTime = Date.now();
@@ -349,6 +354,22 @@ class FileApi {
logger.debug(`delta ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.delta(this.fullPath(path), options), this.requestRepeatCount());
}
public async acquireLock(type: LockType, clientType: LockClientType, clientId: string): Promise<Lock> {
if (!this.supportsLocks) throw new Error('Sync target does not support built-in locks');
return tryAndRepeat(() => this.driver_.acquireLock(type, clientType, clientId), this.requestRepeatCount());
}
public async releaseLock(type: LockType, clientType: LockClientType, clientId: string) {
if (!this.supportsLocks) throw new Error('Sync target does not support built-in locks');
return tryAndRepeat(() => this.driver_.releaseLock(type, clientType, clientId), this.requestRepeatCount());
}
public async listLocks() {
if (!this.supportsLocks) throw new Error('Sync target does not support built-in locks');
return tryAndRepeat(() => this.driver_.listLocks(), this.requestRepeatCount());
}
}
function basicDeltaContextFromOptions_(options: any) {

View File

@@ -0,0 +1,122 @@
const { setupDatabaseAndSynchronizer, switchClient, supportDir } = require('./testing/test-utils.js');
const shim = require('./shim').default;
const { enexXmlToHtml } = require('./import-enex-html-gen.js');
const cleanHtml = require('clean-html');
const fileWithPath = (filename) =>
`${supportDir}/../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) => {
options = {
resources: [],
...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',
});
compareOutputToExpected({
testName: 'svg',
});
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,
}],
});
compareOutputToExpected({
testName: 'quoted-attributes',
});
// 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');
// });
// }));
});

View File

@@ -426,14 +426,21 @@ function attributeToLowerCase(node: any) {
return output;
}
function cssValue(context: any, style: string, propName: string): string {
function cssValue(context: any, style: string, propName: string | string[]): string {
if (!style) return null;
const propNames = Array.isArray(propName) ? propName : [propName];
try {
const o = cssParser.parse(`pre {${style}}`);
if (!o.stylesheet.rules.length) return null;
const prop = o.stylesheet.rules[0].declarations.find((d: any) => d.property.toLowerCase() === propName);
return prop && prop.value ? prop.value.trim().toLowerCase() : null;
for (const propName of propNames) {
const prop = o.stylesheet.rules[0].declarations.find((d: any) => d.property.toLowerCase() === propName);
if (prop && prop.value) return prop.value.trim().toLowerCase();
}
return null;
} catch (error) {
displaySaxWarning(context, error.message);
return null;
@@ -507,7 +514,13 @@ function isCodeBlock(context: any, nodeName: string, attributes: any) {
// Yes, this property sometimes appears as -en-codeblock, sometimes as
// --en-codeblock. Would be too easy to import ENEX data otherwise.
// https://github.com/laurent22/joplin/issues/4965
const enCodeBlock = cssValue(context, attributes.style, '-en-codeblock') || cssValue(context, attributes.style, '--en-codeblock');
const enCodeBlock = cssValue(context, attributes.style, [
'-en-codeblock',
'--en-codeblock',
'-evernote-codeblock',
'--evernote-codeblock',
]);
if (enCodeBlock && enCodeBlock.toLowerCase() === 'true') return true;
}
return false;
@@ -518,8 +531,19 @@ function isHighlight(context: any, _nodeName: string, attributes: any) {
// Evernote uses various inconsistent CSS prefixes: so far I've found
// "--en", "-en", "-evernote", so I'm guessing "--evernote" probably
// exists too.
const enHighlight = cssValue(context, attributes.style, '-evernote-highlight') || cssValue(context, attributes.style, '--evernote-highlight');
if (enHighlight && enHighlight.toLowerCase() === 'true') return true;
const enHighlight = cssValue(context, attributes.style, [
'-evernote-highlight',
'--evernote-highlight',
'-en-highlight',
'--en-highlight',
]);
// Value can be any colour or "true". I guess if it's set at all it
// should be highlighted but just in case handle case where it's
// "false".
if (enHighlight && enHighlight.toLowerCase() !== 'false') return true;
}
return false;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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