You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-30 20:39:46 +02:00
Compare commits
81 Commits
server_nat
...
server-v2.
Author | SHA1 | Date | |
---|---|---|---|
|
1d035fcb37 | ||
|
4bfb4db5f1 | ||
|
4fc3bcbfd7 | ||
|
01826d9eb1 | ||
|
73137cfb27 | ||
|
b58ea0f202 | ||
|
d7e08770bd | ||
|
f495db1391 | ||
|
e4d5e9cefb | ||
|
72f336a5af | ||
|
dfa5f8b357 | ||
|
3cdbd6dd15 | ||
|
c6dec0a045 | ||
|
4879edc59a | ||
|
005f720f7b | ||
|
725c79d1ec | ||
|
0de6e9ed11 | ||
|
7c3785e89d | ||
|
5c2a0ed033 | ||
|
fc64c8264c | ||
|
08f420ce06 | ||
|
cc23a8b70b | ||
|
9ffa29f658 | ||
|
7431da9f3a | ||
|
4deeed0d5c | ||
|
f8d9601ff7 | ||
|
89179c2776 | ||
|
d76646a609 | ||
|
485c0d0314 | ||
|
6b31609338 | ||
|
200ba858dd | ||
|
7b3ad32103 | ||
|
3745cd7cb0 | ||
|
920f2d9655 | ||
|
f800ca0269 | ||
|
33be306d01 | ||
|
3782255c27 | ||
|
0ab235273b | ||
|
75256613cc | ||
|
b328094033 | ||
|
4f0f1af5d1 | ||
|
021ce14348 | ||
|
c4017e52dc | ||
|
ec2c1741a2 | ||
|
3e5ad0a374 | ||
|
05e390d48b | ||
|
31ce0f46e0 | ||
|
d19551b984 | ||
|
dacd697f80 | ||
|
70d5c7a648 | ||
|
42caab6bde | ||
|
01b63ad263 | ||
|
ae4013d2f7 | ||
|
298e85f115 | ||
|
9e1cb9db2c | ||
|
373c041aa6 | ||
|
af19865865 | ||
|
a0d23046bf | ||
|
7ad73df170 | ||
|
ce5c5d6042 | ||
|
8c6d78e01c | ||
|
a65c424233 | ||
|
e6d3396f42 | ||
|
190550fe8e | ||
|
030b18d7c7 | ||
|
f1bfcfde81 | ||
|
47a31c4ef1 | ||
|
630a400181 | ||
|
725abbc167 | ||
|
84ec845499 | ||
|
5892a0678b | ||
|
cb26ab414d | ||
|
74fcd474af | ||
|
c7406f397c | ||
|
d22a29ee3d | ||
|
bcd568a496 | ||
|
66e79ccb23 | ||
|
08ee2b200f | ||
|
57b8aa1789 | ||
|
b5d792c606 | ||
|
370441333f |
@@ -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
|
||||
|
32
.github/scripts/run_ci.sh
vendored
32
.github/scripts/run_ci.sh
vendored
@@ -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"
|
||||
|
8
.github/workflows/github-actions-main.yml
vendored
8
.github/workflows/github-actions-main.yml
vendored
@@ -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
33
.gitignore
vendored
@@ -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
|
||||
|
@@ -188,6 +188,11 @@ h2 {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3em;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.front-page h1 {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
88
README.md
88
README.md
@@ -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&mtm_kwd=joplinapp&mtm_source=joplinapp-github&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&mtm_kwd=joplinapp&mtm_source=joplinapp-github&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 -->
|
||||
| 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
|
||||
|
@@ -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&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development&currency_code=EUR&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>
|
||||
|
@@ -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&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development&currency_code=EUR&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>
|
||||
|
@@ -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&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development&currency_code=EUR&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>
|
||||
|
@@ -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&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development&currency_code=EUR&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>
|
||||
|
@@ -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>
|
||||
|
@@ -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 "JoplinProfile" 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 "JoplinProfile" 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&mtm_kwd=joplinapp&mtm_source=joplinapp-github&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&mtm_kwd=joplinapp&mtm_source=joplinapp-github&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 -->
|
||||
|
@@ -568,10 +568,10 @@
|
||||
<br />
|
||||
|
||||
<div class="text-center sponsors-org">
|
||||
<a class="sponsor-org-item" href="https://usrigging.com/"><img title="U.S. Ringing Supply" src="/images/sponsors/RingingSupply.svg"></a>
|
||||
<a class="sponsor-org-item" href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&mtm_kwd=joplinapp&mtm_source=joplinapp-webseite&mtm_medium=banner"><img title="Hosting.de" src="/images/sponsors/HostingDe.png"></a>
|
||||
<a class="sponsor-org-item" href="https://tranio.com/italy/"><img title="Tranio" src="/images/sponsors/Tranio.png"></a>
|
||||
<a class="sponsor-org-item" href="https://seirei.ne.jp"><img title="Serei Network" src="/images/sponsors/SeireiNetwork.png"></a>
|
||||
<a class="sponsor-org-item" href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&mtm_kwd=joplinapp&mtm_source=joplinapp-webseite&mtm_medium=banner"><img title="Hosting.de" src="/images/sponsors/HostingDe.png"></a>
|
||||
<a class="sponsor-org-item" href="https://usrigging.com/"><img title="U.S. Ringing Supply" src="/images/sponsors/RingingSupply.svg"></a>
|
||||
</div>
|
||||
|
||||
<div class="text-center sponsors-github">
|
||||
|
@@ -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>
|
||||
|
20
gulpfile.js
20
gulpfile.js
@@ -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'));
|
||||
|
4
packages/app-cli/package-lock.json
generated
4
packages/app-cli/package-lock.json
generated
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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>'
|
||||
);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
@@ -0,0 +1,3 @@
|
||||
<en-note>
|
||||
<h1 style="box-sizing:inherit;font-family:"Guardian TextSans Web", "Helvetica Neue", 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>
|
@@ -0,0 +1,3 @@
|
||||
<en-note>
|
||||
<h1 style="box-sizing:inherit;font-family:"Guardian TextSans Web", "Helvetica Neue", 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>
|
@@ -1 +1,3 @@
|
||||
<span style="background-color: rgb(255, 250, 165);-evernote-highlight:true;">I'll highlight some text.</span>
|
||||
<span style="background-color: rgb(255, 250, 165);-evernote-highlight:true;">I'll highlight some text.</span>
|
||||
<br/>
|
||||
<span style="--en-highlight:yellow;background-color: #ffef9e;">this text is yellow</span>
|
@@ -1 +1,2 @@
|
||||
==I'll highlight some text.==
|
||||
==I'll highlight some text.==
|
||||
==this text is yellow==
|
@@ -0,0 +1 @@
|
||||
<a data-from-md href='#'>test</a>
|
@@ -0,0 +1 @@
|
||||
<a>test</a>
|
@@ -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({
|
||||
|
@@ -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));
|
||||
|
||||
|
@@ -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'",
|
||||
|
@@ -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',
|
||||
|
@@ -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;
|
||||
|
@@ -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>
|
||||
|
@@ -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(
|
||||
|
@@ -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,
|
||||
];
|
||||
|
@@ -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]);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
@@ -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);
|
||||
},
|
||||
};
|
||||
};
|
@@ -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);
|
||||
},
|
||||
};
|
||||
};
|
@@ -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() {
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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');
|
||||
}
|
||||
|
@@ -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 };
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
};
|
@@ -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);
|
||||
|
@@ -19,6 +19,7 @@ export interface MarkupToHtmlOptions {
|
||||
contentMaxWidth?: number;
|
||||
plugins?: Record<string, any>;
|
||||
bodyOnly?: boolean;
|
||||
mapsToLine?: boolean;
|
||||
}
|
||||
|
||||
export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
// ----------------------------------------------------------------
|
||||
|
@@ -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),
|
||||
|
@@ -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) {
|
||||
|
@@ -29,9 +29,13 @@ export default function() {
|
||||
'textLink',
|
||||
'textPaste',
|
||||
'textSelectAll',
|
||||
'textBulletedList',
|
||||
'toggleExternalEditing',
|
||||
'toggleLayoutMoveMode',
|
||||
'toggleNoteList',
|
||||
'toggleNotesSortOrderField',
|
||||
'toggleNotesSortOrderReverse',
|
||||
'togglePerFolderSortOrder',
|
||||
'toggleSideBar',
|
||||
'toggleVisiblePanes',
|
||||
'editor.deleteLine',
|
||||
|
@@ -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
|
||||
|
92
packages/app-desktop/gui/utils/SyncScrollMap.ts
Normal file
92
packages/app-desktop/gui/utils/SyncScrollMap.ts
Normal 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;
|
||||
}
|
||||
}
|
@@ -162,7 +162,6 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
4
packages/app-desktop/package-lock.json
generated
4
packages/app-desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.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",
|
||||
|
@@ -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",
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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_);
|
||||
|
@@ -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));
|
||||
|
@@ -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();
|
||||
});
|
||||
});
|
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
});
|
||||
});
|
@@ -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 });
|
||||
}
|
||||
}
|
||||
};
|
1
packages/app-mobile/.gitignore
vendored
1
packages/app-mobile/.gitignore
vendored
@@ -28,6 +28,7 @@ build/
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
|
||||
# node.js
|
||||
#
|
||||
|
@@ -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'
|
||||
}
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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' }
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)";
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
10100
packages/app-mobile/package-lock.json
generated
10100
packages/app-mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
@@ -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...');
|
||||
|
||||
|
2
packages/fork-htmlparser2/package-lock.json
generated
2
packages/fork-htmlparser2/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-htmlparser2",
|
||||
"version": "4.1.36",
|
||||
"version": "4.1.38",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
@@ -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"
|
||||
|
2
packages/fork-sax/package-lock.json
generated
2
packages/fork-sax/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-sax",
|
||||
"version": "1.2.40",
|
||||
"version": "1.2.42",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "@joplin/fork-sax",
|
||||
"description": "An evented streaming XML parser in JavaScript",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"version": "1.2.40",
|
||||
"version": "1.2.42",
|
||||
"main": "lib/sax.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -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 %>",
|
||||
|
@@ -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": {
|
||||
|
@@ -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() {
|
||||
|
@@ -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] };
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
122
packages/lib/import-enex-html-gen.test.js
Normal file
122
packages/lib/import-enex-html-gen.test.js
Normal 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');
|
||||
// });
|
||||
// }));
|
||||
|
||||
});
|
@@ -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
Reference in New Issue
Block a user