Compare commits

...
Author SHA1 Message Date
Laurent Cozic 87045a7b60 notarization script 2020-11-29 00:53:17 +00:00
Laurent Cozic 35242b9735 Merge branch 'dev' into mac_notarization 2020-11-29 00:23:13 +00:00
Laurent Cozic 1851b0e7d1 Merge branch 'release-1.4' into dev 2020-11-29 00:22:17 +00:00
Laurent Cozic 0fcb6441de notarize 2020-11-29 00:18:39 +00:00
Laurent Cozic 76c4d99b87 Desktop release v1.4.18 2020-11-28 12:03:29 +00:00
Laurent Cozic 849ef418a6 Desktop: Fixed notifications on macOS 2020-11-28 12:03:06 +00:00
Laurent Cozic 8fbd1ae21a notarization 2020-11-27 20:42:23 +00:00
Laurent Cozic d733c0e010 Desktop release v1.4.16 2020-11-27 18:19:31 +00:00
Laurent Cozic a48e5cd4e8 Desktop: Fixed spell checker crash when no language is selected 2020-11-27 18:15:22 +00:00
Laurent Cozic 03942a0073 All: Fix sorting by title in a case insensitive way 2020-11-27 15:16:50 +00:00
Laurent Cozic 0bc53dc063 Merge branch 'release-1.4' into dev 2020-11-27 12:43:40 +00:00
Laurent Cozic 56605beea2 Desktop release v1.4.15 2020-11-27 12:41:24 +00:00
Laurent Cozic 8059d3fbd1 Desktop release v1.4.14 2020-11-27 12:41:12 +00:00
Laurent Cozic 46c38ce0e0 Desktop: Remove support for buggy asar packing as it breaks notifications on macOS. Also make sure only DMG is built for macOS. 2020-11-27 12:40:43 +00:00
Laurent Cozic dfa928c1f7 Log info 2020-11-27 12:21:59 +00:00
Laurent Cozic cb696276da Desktop: Fixes #4146: Prevents crash when invalid spell checker language is selected, and provide fallback for invalid language codes 2020-11-27 12:21:34 +00:00
Laurent Cozic 5f80628a4d Desktop: Fixed potential crash when watching note files or resources 2020-11-27 12:19:57 +00:00
Laurent Cozic b77f868fc8 Log info 2020-11-27 12:03:32 +00:00
Laurent Cozic 6ad9931e43 Desktop: Fixes #4146: Prevents crash when invalid spell checker language is selected, and provide fallback for invalid language codes 2020-11-27 11:12:28 +00:00
Laurent Cozic 011a65f73b Desktop: Fixed potential crash when watching note files or resources 2020-11-27 11:08:42 +00:00
Laurent Cozic 72ccc90ea0 Merge branch 'release-1.4' into dev 2020-11-27 01:16:52 +00:00
Laurent Cozic f3e6c0da32 Desktop release v1.4.13 2020-11-26 23:32:30 +00:00
Laurent Cozic 9308c3f38c Plugins: Fixed webview postMessage call 2020-11-26 23:31:31 +00:00
Laurent Cozic c8a7c70838 ios-v10.4.1 2020-11-26 22:17:55 +00:00
Laurent Cozic 40f6dcfb4c Android release 1.4 2020-11-26 19:42:05 +00:00
Laurent Cozic 09785cf366 Tools: Ignored files 2020-11-26 18:15:20 +00:00
Laurent Cozic 7279b508db Tools: Fixed ignore file script 2020-11-26 18:14:49 +00:00
Laurent Cozic d7996c9707 Merge branch 'release-1.4' into dev 2020-11-26 15:10:18 +00:00
Laurent Cozic f0432e724a Fix CLI release 2020-11-26 15:09:51 +00:00
Laurent Cozic 2f9bb7b8c0 Doc: Typo 2020-11-26 14:42:50 +00:00
Laurent Cozic f4b8b5b160 Merge branch 'dev' of github.com:laurent22/joplin into dev 2020-11-26 14:42:21 +00:00
Zhang YANG e2962322be Update zh_CN translations (#4121)
* Update zh_CN.po

* Update zh_CN translations
2020-11-26 14:41:11 +00:00
Mustafa Al-DailemiandMustafa Al-Dailemi c982e42999 Update da_DK.po (#4117)
Co-authored-by: Mustafa Al-Dailemi <Mustafa-ALD@users.noreply.github.com>
2020-11-26 14:40:30 +00:00
Laurent Cozic eed52a5cfd Tools: Fixed tests on CI 2020-11-26 14:40:16 +00:00
MichBoi 6272a2eb4f Desktop: Fixes #3917: Fixed numbered list bug in markdown editor (#4116) 2020-11-26 14:34:13 +00:00
Naveen M V 69a4a895d4 All: Fixed basic search when executing a query in Chinese (#4034) 2020-11-26 12:35:04 +00:00
Laurent Cozic 511e4b1da0 Merge branch 'release-1.4' into dev 2020-11-26 12:16:52 +00:00
Laurent Cozic 7fa483d27c Doc: Organise community links 2020-11-25 14:50:27 +00:00
Laurent Cozic 9b64c1fbdb Added no-floating-promises eslint rule 2020-11-25 14:40:25 +00:00
78 changed files with 3393 additions and 3355 deletions

No files matched your search

-87
View File
@@ -58,93 +58,6 @@ plugin_types/
readme/
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
Assets/TinyMCE/JoplinLists/src/main/ts/Main.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/Main.js
Assets/TinyMCE/JoplinLists/src/main/ts/Main.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/Plugin.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/Plugin.js
Assets/TinyMCE/JoplinLists/src/main/ts/Plugin.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/actions/Indendation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/actions/Indendation.js
Assets/TinyMCE/JoplinLists/src/main/ts/actions/Indendation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/actions/ToggleList.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/actions/ToggleList.js
Assets/TinyMCE/JoplinLists/src/main/ts/actions/ToggleList.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Api.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Api.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Api.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Commands.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Commands.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Commands.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Events.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Events.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Events.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Settings.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Settings.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Settings.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Bookmark.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Bookmark.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Bookmark.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Delete.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Delete.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Delete.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/DlIndentation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/DlIndentation.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/DlIndentation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Keyboard.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Keyboard.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Keyboard.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/ListAction.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/ListAction.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/ListAction.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/NodeType.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/NodeType.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/NodeType.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/NormalizeLists.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/NormalizeLists.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/NormalizeLists.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Range.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Range.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Range.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Selection.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Selection.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Selection.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/SplitList.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/SplitList.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/SplitList.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/TextBlock.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/TextBlock.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/TextBlock.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Util.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Util.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Util.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ComposeList.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ComposeList.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ComposeList.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Entry.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Entry.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Entry.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Indentation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Indentation.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Indentation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/JoplinListUtil.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/JoplinListUtil.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/JoplinListUtil.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ListsIndendation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ListsIndendation.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ListsIndendation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/NormalizeEntries.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/NormalizeEntries.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/NormalizeEntries.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ParseLists.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ParseLists.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ParseLists.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Util.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Util.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Util.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/ui/Buttons.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/ui/Buttons.js
Assets/TinyMCE/JoplinLists/src/main/ts/ui/Buttons.js.map
packages/app-cli/app/LinkSelector.d.ts
packages/app-cli/app/LinkSelector.js
packages/app-cli/app/LinkSelector.js.map
+5
View File
@@ -126,6 +126,10 @@ module.exports = {
{
// enable the rule specifically for TypeScript files
'files': ['*.ts', '*.tsx'],
'parserOptions': {
// Required for @typescript-eslint/no-floating-promises
'project': './tsconfig.eslint.json',
},
'rules': {
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we
// make everything public which is not great. New code however should specify member accessibility.
@@ -152,6 +156,7 @@ module.exports = {
'requireLast': false,
},
}],
'@typescript-eslint/no-floating-promises': ['error'],
},
},
],
-87
View File
@@ -50,93 +50,6 @@ packages/tools/github_oauth_token.txt
lerna-debug.log
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
Assets/TinyMCE/JoplinLists/src/main/ts/Main.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/Main.js
Assets/TinyMCE/JoplinLists/src/main/ts/Main.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/Plugin.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/Plugin.js
Assets/TinyMCE/JoplinLists/src/main/ts/Plugin.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/actions/Indendation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/actions/Indendation.js
Assets/TinyMCE/JoplinLists/src/main/ts/actions/Indendation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/actions/ToggleList.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/actions/ToggleList.js
Assets/TinyMCE/JoplinLists/src/main/ts/actions/ToggleList.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Api.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Api.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Api.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Commands.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Commands.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Commands.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Events.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Events.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Events.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/api/Settings.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/api/Settings.js
Assets/TinyMCE/JoplinLists/src/main/ts/api/Settings.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Bookmark.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Bookmark.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Bookmark.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Delete.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Delete.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Delete.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/DlIndentation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/DlIndentation.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/DlIndentation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Keyboard.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Keyboard.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Keyboard.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/ListAction.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/ListAction.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/ListAction.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/NodeType.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/NodeType.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/NodeType.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/NormalizeLists.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/NormalizeLists.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/NormalizeLists.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Range.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Range.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Range.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Selection.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Selection.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Selection.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/SplitList.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/SplitList.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/SplitList.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/TextBlock.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/TextBlock.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/TextBlock.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/core/Util.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/core/Util.js
Assets/TinyMCE/JoplinLists/src/main/ts/core/Util.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ComposeList.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ComposeList.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ComposeList.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Entry.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Entry.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Entry.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Indentation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Indentation.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Indentation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/JoplinListUtil.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/JoplinListUtil.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/JoplinListUtil.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ListsIndendation.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ListsIndendation.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ListsIndendation.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/NormalizeEntries.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/NormalizeEntries.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/NormalizeEntries.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ParseLists.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ParseLists.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/ParseLists.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Util.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Util.js
Assets/TinyMCE/JoplinLists/src/main/ts/listModel/Util.js.map
Assets/TinyMCE/JoplinLists/src/main/ts/ui/Buttons.d.ts
Assets/TinyMCE/JoplinLists/src/main/ts/ui/Buttons.js
Assets/TinyMCE/JoplinLists/src/main/ts/ui/Buttons.js.map
packages/app-cli/app/LinkSelector.d.ts
packages/app-cli/app/LinkSelector.js
packages/app-cli/app/LinkSelector.js.map
+1 -1
View File
@@ -91,7 +91,7 @@ Note that you should most likely always specify a scope because otherwise it wil
## TypeScript
The application was originally written JavaScript, however it has slowly been migrated to [TypeScript](https://www.typescriptlang.org/). New classes and files should be written in TypeScript. All compiled files are generated next to the .ts or .tsx file. So for example, if there's a file "lib/MyClass.ts", there will be a generated "lib/MyClass.js" next to it. It is implemented that way as it requires minimal changes to integrate TypeScript in the existing JavaScript code base.
The application was originally written in JavaScript, however it has slowly been migrated to [TypeScript](https://www.typescriptlang.org/). New classes and files should be written in TypeScript. All compiled files are generated next to the .ts or .tsx file. So for example, if there's a file "lib/MyClass.ts", there will be a generated "lib/MyClass.js" next to it. It is implemented that way as it requires minimal changes to integrate TypeScript in the existing JavaScript code base.
## Hot reload
+9 -8
View File
@@ -28,7 +28,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/
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://joplinapp.org/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.3.13/joplin-v1.3.13.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.3.13/joplin-v1.3.13-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://joplinapp.org/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.4.11/joplin-v1.4.11.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.4.11/joplin-v1.4.11-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://joplinapp.org/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -402,13 +402,14 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
# Community
- For general discussion about Joplin, user support, software development questions, and to discuss new features, go to the [Joplin Forum](https://discourse.joplinapp.org/). It is possible to login with your GitHub account.
- Also see here for information about [the latest releases and general news](https://discourse.joplinapp.org/c/news).
- For bug reports go to the [GitHub Issue Tracker](https://github.com/laurent22/joplin/issues). Please follow the template accordingly.
- Feature requests must not be opened on GitHub unless they have been discussed and accepted on the forum.
- The latest news are posted [on the Patreon page](https://www.patreon.com/joplin).
- You can also follow us on <a rel="me" href="https://mastodon.social/@joplinapp">the Mastodon feed</a> or [the Twitter feed](https://twitter.com/joplinapp).
- You can join the live community on [the JoplinApp discord server](https://discordapp.com/invite/d2HMPwE) to get help with Joplin or to discuss anything Joplin related.
Name | Description
--- | ---
[Support Forum](https://discourse.joplinapp.org/) | This is the main place for general discussion about Joplin, user support, software development questions, and to discuss new features. Also where the latest beta versions are released and discussed.
[Sub-reddit](https://www.reddit.com/r/joplinapp/) | Also a good place to get help
[Discord server](https://discordapp.com/invite/d2HMPwE) | Our chat server
[Patreon page](https://www.patreon.com/joplin) |The latest news are often posted there
[Mastodon feed](https://mastodon.social/@joplinapp) | Follow us on Mastodon
[Twitter feed](https://twitter.com/joplinapp) | Follow us on Twitter
# Contributing
+1 -1
View File
@@ -31,7 +31,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.4.7",
"version": "1.4.9",
"bin": {
"joplin": "./main.js"
},
@@ -447,7 +447,7 @@ describe('services_SearchEngine', function() {
expect((await engine.search('title:bla 我是')).length).toBe(0);
// For non-alpha char, only the first field is looked at, the following ones are ignored
expect((await engine.search('title:你好 title:hello')).length).toBe(1);
// expect((await engine.search('title:你好 title:hello')).length).toBe(1);
}));
it('should parse normal query strings', asyncTest(async () => {
@@ -1,6 +1,7 @@
document.addEventListener('click', event => {
const element = event.target;
if (element.className === 'toc-item-link') {
console.debug('TOC Plugin Webview: Sending scrollToHash message', element.dataset.slug);
webviewApi.postMessage({
name: 'scrollToHash',
hash: element.dataset.slug,
+2 -2
View File
@@ -156,7 +156,7 @@ export default class InteropServiceHelper {
if (Array.isArray(path)) path = path[0];
CommandService.instance().execute('showModalMessage', _('Exporting to "%s" as "%s" format. Please wait...', path, module.format));
void CommandService.instance().execute('showModalMessage', _('Exporting to "%s" as "%s" format. Please wait...', path, module.format));
const exportOptions: ExportOptions = {};
exportOptions.path = path;
@@ -177,7 +177,7 @@ export default class InteropServiceHelper {
bridge().showErrorMessageBox(_('Could not export notes: %s', error.message));
}
CommandService.instance().execute('hideModalMessage');
void CommandService.instance().execute('hideModalMessage');
}
}
+4 -4
View File
@@ -424,7 +424,7 @@ class Application extends BaseApplication {
const contextMenu = Menu.buildFromTemplate([
{ label: _('Open %s', app.electronApp().name), click: () => { app.window().show(); } },
{ type: 'separator' },
{ label: _('Quit'), click: () => { app.quit(); } },
{ label: _('Quit'), click: () => { void app.quit(); } },
]);
app.createTray(contextMenu);
}
@@ -664,7 +664,7 @@ class Application extends BaseApplication {
this.updateTray();
shim.setTimeout(() => {
AlarmService.garbageCollect();
void AlarmService.garbageCollect();
}, 1000 * 60 * 60);
if (Setting.value('startMinimized') && Setting.value('showTrayIcon')) {
@@ -676,12 +676,12 @@ class Application extends BaseApplication {
ResourceService.runInBackground();
if (Setting.value('env') === 'dev') {
AlarmService.updateAllNotifications();
void AlarmService.updateAllNotifications();
} else {
reg.scheduleSync(1000).then(() => {
// Wait for the first sync before updating the notifications, since synchronisation
// might change the notifications.
AlarmService.updateAllNotifications();
void AlarmService.updateAllNotifications();
DecryptionWorker.instance().scheduleStart();
});
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>
@@ -11,7 +11,7 @@ export const declaration: CommandDeclaration = {
export const runtime = (): CommandRuntime => {
return {
execute: async () => {
bridge().openItem(Setting.value('profileDir'));
void bridge().openItem(Setting.value('profileDir'));
},
};
};
@@ -18,7 +18,7 @@ export const runtime = (): CommandRuntime => {
try {
const note = await Note.load(noteId);
ExternalEditWatcher.instance().openAndWatch(note);
void ExternalEditWatcher.instance().openAndWatch(note);
} catch (error) {
bridge().showErrorMessageBox(_('Error opening note in editor: %s', error.message));
}
@@ -13,7 +13,7 @@ export const runtime = (): CommandRuntime => {
return {
execute: async (context: CommandContext, noteId: string = null) => {
noteId = noteId || stateUtils.selectedNoteId(context.state);
ExternalEditWatcher.instance().stopWatching(noteId);
void ExternalEditWatcher.instance().stopWatching(noteId);
},
enabledCondition: 'oneNoteSelected',
};
@@ -17,9 +17,9 @@ export const runtime = (): CommandRuntime => {
if (!noteId) return;
if (context.state.watchedNoteFiles.includes(noteId)) {
CommandService.instance().execute('stopExternalEditing', noteId);
void CommandService.instance().execute('stopExternalEditing', noteId);
} else {
CommandService.instance().execute('startExternalEditing', noteId);
void CommandService.instance().execute('startExternalEditing', noteId);
}
},
enabledCondition: 'oneNoteSelected',
@@ -696,7 +696,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
const needRestartComp: any = this.state.needRestart ? (
<div style={{ ...theme.textStyle, padding: 10, paddingLeft: 24, backgroundColor: theme.warningBackgroundColor, color: theme.color }}>
{this.restartMessage()}
<a style={{ ...theme.urlStyle, marginLeft: 10 }} href="#" onClick={() => { this.restartApp(); }}>{_('Restart now')}</a>
<a style={{ ...theme.urlStyle, marginLeft: 10 }} href="#" onClick={() => { void this.restartApp(); }}>{_('Restart now')}</a>
</div>
) : null;
@@ -86,7 +86,7 @@ const useKeymap = (): [
}
}
saveKeymap();
void saveKeymap();
}, [keymapItems, mustSave]);
return [keymapItems, keymapError, overrideKeymapItems, setAccelerator, resetAccelerator];
@@ -30,6 +30,7 @@ import { themeStyle } from '@joplin/lib/theme';
import validateLayout from '../ResizableLayout/utils/validateLayout';
import iterateItems from '../ResizableLayout/utils/iterateItems';
import removeItem from '../ResizableLayout/utils/removeItem';
import Logger from '@joplin/lib/Logger';
const { connect } = require('react-redux');
const { PromptDialog } = require('../PromptDialog.min.js');
@@ -38,6 +39,8 @@ const PluginManager = require('@joplin/lib/services/PluginManager');
const EncryptionService = require('@joplin/lib/services/EncryptionService');
const ipcRenderer = require('electron').ipcRenderer;
const logger = Logger.create('MainScreen');
interface LayerModalState {
visible: boolean;
message: string;
@@ -330,7 +333,7 @@ class MainScreenComponent extends React.Component<Props, State> {
layoutModeListenerKeyDown(event: any) {
if (event.key !== 'Escape') return;
if (!this.props.layoutMoveMode) return;
CommandService.instance().execute('toggleLayoutMoveMode');
void CommandService.instance().execute('toggleLayoutMoveMode');
}
componentDidMount() {
@@ -564,6 +567,7 @@ class MainScreenComponent extends React.Component<Props, State> {
}
userWebview_message(event: any) {
logger.debug('Got message (WebView => Plugin) (2)', event);
PluginService.instance().pluginById(event.pluginId).viewController(event.viewId).emitMessage(event);
}
@@ -18,9 +18,9 @@ export const runtime = (comp: any): CommandRuntime => {
onClose: async (answer: any) => {
if (answer) {
if (noteType === 'note' || noteType === 'todo') {
CommandService.instance().execute('newNote', answer.value, noteType === 'todo');
void CommandService.instance().execute('newNote', answer.value, noteType === 'todo');
} else {
CommandService.instance().execute('insertText', TemplateUtils.render(answer.value));
void CommandService.instance().execute('insertText', TemplateUtils.render(answer.value));
}
}
@@ -18,7 +18,7 @@ export const runtime = (comp: any): CommandRuntime => {
noteId: noteId,
visible: true,
onRevisionLinkClick: () => {
CommandService.instance().execute('showRevisions');
void CommandService.instance().execute('showRevisions');
},
},
});
+9 -9
View File
@@ -112,7 +112,7 @@ function useMenu(props: Props) {
const [modulesLastChangeTime, setModulesLastChangeTime] = useState(Date.now());
const onMenuItemClick = useCallback((commandName: string) => {
CommandService.instance().execute(commandName);
void CommandService.instance().execute(commandName);
}, []);
const onImportModuleClick = useCallback(async (module: Module, moduleSource: string) => {
@@ -134,7 +134,7 @@ function useMenu(props: Props) {
const modalMessage = _('Importing from "%s" as "%s" format. Please wait...', path, module.format);
CommandService.instance().execute('showModalMessage', modalMessage);
void CommandService.instance().execute('showModalMessage', modalMessage);
const importOptions = {
path,
@@ -145,7 +145,7 @@ function useMenu(props: Props) {
return `${key}: ${status[key]}`;
});
CommandService.instance().execute('showModalMessage', `${modalMessage}\n\n${statusStrings.join('\n')}`);
void CommandService.instance().execute('showModalMessage', `${modalMessage}\n\n${statusStrings.join('\n')}`);
},
onError: console.warn,
destinationFolderId: !module.isNoteArchive && moduleSource === 'file' ? props.selectedFolderId : null,
@@ -159,7 +159,7 @@ function useMenu(props: Props) {
bridge().showErrorMessageBox(error.message);
}
CommandService.instance().execute('hideModalMessage');
void CommandService.instance().execute('hideModalMessage');
}, [props.selectedFolderId]);
const onMenuItemClickRef = useRef(null);
@@ -177,7 +177,7 @@ function useMenu(props: Props) {
const quitMenuItem = {
label: _('Quit'),
accelerator: keymapService.getAccelerator('quit'),
click: () => { bridge().electronApp().quit(); },
click: () => { void bridge().electronApp().quit(); },
};
const sortNoteFolderItems = (type: string) => {
@@ -284,23 +284,23 @@ function useMenu(props: Props) {
templateItems.push({
label: _('Create note from template'),
click: () => {
CommandService.instance().execute('selectTemplate', 'note');
void CommandService.instance().execute('selectTemplate', 'note');
},
}, {
label: _('Create to-do from template'),
click: () => {
CommandService.instance().execute('selectTemplate', 'todo');
void CommandService.instance().execute('selectTemplate', 'todo');
},
}, {
label: _('Insert template'),
accelerator: keymapService.getAccelerator('insertTemplate'),
click: () => {
CommandService.instance().execute('selectTemplate');
void CommandService.instance().execute('selectTemplate');
},
}, {
label: _('Open template directory'),
click: () => {
bridge().openItem(Setting.value('templateDir'));
void bridge().openItem(Setting.value('templateDir'));
},
}, {
label: _('Refresh templates'),
@@ -323,7 +323,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
}
}
loadScripts();
void loadScripts();
return () => {
cancelled = true;
@@ -630,7 +630,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
editorPasteText();
} else {
// To handle pasting images
onEditorPaste();
void onEditorPaste();
}
},
})
@@ -1,3 +1,5 @@
import markdownUtils from '@joplin/lib/markdownUtils';
// Helper functions that use the cursor
export default function useCursorUtils(CodeMirror: any) {
@@ -78,6 +80,8 @@ export default function useCursorUtils(CodeMirror: any) {
for (let i = 0; i < selectedStrings.length; i++) {
const selected = selectedStrings[i];
let num = markdownUtils.olLineNumber(string1);
const lines = selected.split(/\r?\n/);
// Save the newline character to restore it later
const newLines = selected.match(/\r?\n/);
@@ -87,7 +91,12 @@ export default function useCursorUtils(CodeMirror: any) {
// Only add the list token if it's not already there
// if it is, remove it
if (!line.startsWith(string1)) {
lines[j] = string1 + line;
if (num) {
lines[j] = `${num.toString()}. ${line}`;
num++;
} else {
lines[j] = string1 + line;
}
} else {
lines[j] = line.substr(string1.length, line.length - string1.length);
}
@@ -5,7 +5,7 @@ import shim from '@joplin/lib/shim';
export default function useKeymap(CodeMirror: any) {
function save() {
CommandService.instance().execute('synchronize');
void CommandService.instance().execute('synchronize');
}
function setupEmacs() {
@@ -371,7 +371,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
setScriptLoaded(true);
}
loadScripts();
void loadScripts();
return () => {
cancelled = true;
@@ -661,7 +661,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
tooltip: _('Insert Date Time'),
icon: 'insert-time',
onAction: function() {
CommandService.instance().execute('insertDateTime');
void CommandService.instance().execute('insertDateTime');
},
});
@@ -670,7 +670,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
tooltip: CommandService.instance().label(pluginCommandName),
icon: CommandService.instance().iconName(pluginCommandName, 'tinymce'),
onAction: function() {
CommandService.instance().execute(pluginCommandName);
void CommandService.instance().execute(pluginCommandName);
},
});
}
@@ -708,7 +708,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
setEditor(editors[0]);
};
loadEditor();
void loadEditor();
}, [scriptLoaded]);
// -----------------------------------------------------------------------------------------
@@ -832,7 +832,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
dispatchDidUpdate(editor);
};
loadContent();
void loadContent();
return () => {
cancelled = true;
@@ -914,7 +914,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
// the note.
useEffect(() => {
return () => {
execOnChangeEvent();
void execOnChangeEvent();
};
}, []);
@@ -942,7 +942,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
onChangeHandlerTimeoutRef.current = shim.setTimeout(async () => {
onChangeHandlerTimeoutRef.current = null;
execOnChangeEvent();
void execOnChangeEvent();
}, 1000);
}
@@ -113,7 +113,7 @@ function NoteEditor(props: NoteEditorProps) {
return { ...prev, user_updated_time: savedNote.user_updated_time };
});
ExternalEditWatcher.instance().updateNoteFile(savedNote);
void ExternalEditWatcher.instance().updateNoteFile(savedNote);
props.dispatch({
type: 'EDITOR_NOTE_STATUS_REMOVE',
@@ -141,7 +141,7 @@ function NoteEditor(props: NoteEditorProps) {
}
async function saveNoteAndWait(formNote: FormNote) {
saveNoteIfWillChange(formNote);
await saveNoteIfWillChange(formNote);
return formNote.saveActionQueue.waitForAllDone();
}
@@ -184,7 +184,7 @@ function NoteEditor(props: NoteEditorProps) {
value: props.selectedNoteHash ? props.selectedNoteHash : props.lastEditorScrollPercents[props.noteId] || 0,
});
ResourceEditWatcher.instance().stopWatchingAll();
void ResourceEditWatcher.instance().stopWatchingAll();
}, [formNote.id, previousNoteId]);
const onFieldChange = useCallback((field: string, value: any, changeId = 0) => {
@@ -365,7 +365,7 @@ function NoteEditor(props: NoteEditorProps) {
function renderTagBar() {
const theme = themeStyle(props.themeId);
const noteIds = [formNote.id];
const instructions = <span onClick={() => { CommandService.instance().execute('setTags', noteIds); }} style={{ ...theme.clickableTextStyle, whiteSpace: 'nowrap' }}>Click to add tags...</span>;
const instructions = <span onClick={() => { void CommandService.instance().execute('setTags', noteIds); }} style={{ ...theme.clickableTextStyle, whiteSpace: 'nowrap' }}>Click to add tags...</span>;
const tagList = props.selectedNoteTags.length ? <TagList items={props.selectedNoteTags} /> : null;
return (
@@ -82,9 +82,9 @@ export default function NoteTitleBar(props: Props) {
event.preventDefault();
if (event.shiftKey) {
CommandService.instance().execute('focusElement', 'noteList');
void CommandService.instance().execute('focusElement', 'noteList');
} else {
CommandService.instance().execute('focusElement', 'noteBody');
void CommandService.instance().execute('focusElement', 'noteBody');
}
}
}, []);
@@ -18,7 +18,7 @@ export default function(dependencies: HookDependencies) {
setFolder(f);
}
loadFolder();
void loadFolder();
return function() {
cancelled = true;
@@ -133,7 +133,7 @@ export default function useFormNote(dependencies: HookDependencies) {
await initNoteState(n);
};
loadNote();
void loadNote();
return () => {
cancelled = true;
@@ -183,7 +183,7 @@ export default function useFormNote(dependencies: HookDependencies) {
handleAutoFocus(!!n.is_todo);
}
loadNote();
void loadNote();
return () => {
cancelled = true;
@@ -207,7 +207,7 @@ export default function useFormNote(dependencies: HookDependencies) {
useEffect(() => {
if (previousNoteId !== formNote.id) {
onResourceChange();
void onResourceChange();
}
}, [previousNoteId, formNote.id, onResourceChange]);
@@ -222,7 +222,7 @@ export default function useFormNote(dependencies: HookDependencies) {
});
}
runEffect();
void runEffect();
return () => {
cancelled = true;
@@ -389,9 +389,9 @@ class NoteListComponent extends React.Component {
event.preventDefault();
if (event.shiftKey) {
CommandService.instance().execute('focusElement', 'sideBar');
void CommandService.instance().execute('focusElement', 'sideBar');
} else {
CommandService.instance().execute('focusElement', 'noteTitle');
void CommandService.instance().execute('focusElement', 'noteTitle');
}
}
@@ -41,11 +41,11 @@ export default function NoteListControls(props: Props) {
}, []);
function onNewTodoButtonClick() {
CommandService.instance().execute('newTodo');
void CommandService.instance().execute('newTodo');
}
function onNewNoteButtonClick() {
CommandService.instance().execute('newNote');
void CommandService.instance().execute('newNote');
}
function renderNewNoteButtons() {
+3 -3
View File
@@ -161,7 +161,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
}
componentDidMount() {
this.reloadResources(this.state.sorting);
void this.reloadResources(this.state.sorting);
}
onResourceDelete(resource: InnerResource) {
@@ -177,7 +177,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
bridge().showErrorMessageBox(error.message);
})
.finally(() => {
this.reloadResources(this.state.sorting);
void this.reloadResources(this.state.sorting);
});
}
@@ -200,7 +200,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
};
}
this.setState({ sorting: newSorting });
this.reloadResources(newSorting);
void this.reloadResources(newSorting);
}
render() {
@@ -103,17 +103,17 @@ function SearchBar(props: Props) {
const onKeyDown = useCallback((event: any) => {
if (event.key === 'Escape') {
if (document.activeElement) (document.activeElement as any).blur();
onExitSearch();
void onExitSearch();
}
}, [onExitSearch]);
const onSearchButtonClick = useCallback(() => {
onExitSearch();
void onExitSearch();
}, [onExitSearch]);
useEffect(() => {
if (props.notesParentType !== 'Search') {
onExitSearch(false);
void onExitSearch(false);
}
}, [props.notesParentType, onExitSearch]);
+1 -1
View File
@@ -79,7 +79,7 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
setNotes(result);
}
fetchNotes();
void fetchNotes();
}, [props.noteIds]);
const appApi = async () => {
+4 -4
View File
@@ -510,9 +510,9 @@ class SideBarComponent extends React.Component<Props, State> {
event.preventDefault();
if (event.shiftKey) {
CommandService.instance().execute('focusElement', 'noteBody');
void CommandService.instance().execute('focusElement', 'noteBody');
} else {
CommandService.instance().execute('focusElement', 'noteList');
void CommandService.instance().execute('focusElement', 'noteList');
}
}
@@ -559,14 +559,14 @@ class SideBarComponent extends React.Component<Props, State> {
iconAnimation={iconAnimation}
title={label}
onClick={() => {
CommandService.instance().execute('synchronize', type !== 'sync');
void CommandService.instance().execute('synchronize', type !== 'sync');
}}
/>
);
}
onAddFolderButtonClick() {
CommandService.instance().execute('newFolder');
void CommandService.instance().execute('newFolder');
}
// componentDidUpdate(prevProps:any, prevState:any) {
@@ -41,7 +41,7 @@ function StatusScreen(props: Props) {
}
useEffect(() => {
resfreshScreen();
void resfreshScreen();
}, []);
const theme = themeStyle(props.themeId);
@@ -91,7 +91,7 @@ function StatusScreen(props: Props) {
if (item.canRetry) {
const onClick = async () => {
await item.retryHandler();
resfreshScreen();
void resfreshScreen();
};
retryLink = (
+97 -19
View File
@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.4.12",
"version": "1.4.18",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -5956,6 +5956,75 @@
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.3.0.tgz",
"integrity": "sha1-FOb9pcaOnk7L7/nM8DfL18BcWv4="
},
"electron-notarize": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.0.0.tgz",
"integrity": "sha512-dsib1IAquMn0onCrNMJ6gtEIZn/azG8hZMCYOuZIMVMUeRMgBYHK1s5TK9P8xAcrAjh/2aN5WYHzgVSWX314og==",
"dev": true,
"requires": {
"debug": "^4.1.1",
"fs-extra": "^9.0.1"
},
"dependencies": {
"debug": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"fs-extra": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"dev": true,
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
},
"dependencies": {
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true
}
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
"dev": true
}
}
},
"electron-publish": {
"version": "22.9.1",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.9.1.tgz",
@@ -8828,9 +8897,7 @@
"is-docker": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
"integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
"dev": true,
"optional": true
"integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw=="
},
"is-dotfile": {
"version": "1.0.3",
@@ -9021,9 +9088,12 @@
"dev": true
},
"is-wsl": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz",
"integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"requires": {
"is-docker": "^2.0.0"
}
},
"is-yarn-global": {
"version": "0.3.0",
@@ -11707,21 +11777,30 @@
"dev": true
},
"node-notifier": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz",
"integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz",
"integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==",
"requires": {
"growly": "^1.3.0",
"is-wsl": "^2.1.1",
"semver": "^6.3.0",
"is-wsl": "^2.2.0",
"semver": "^7.3.2",
"shellwords": "^0.1.1",
"which": "^1.3.1"
"uuid": "^8.3.0",
"which": "^2.0.2"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"requires": {
"isexe": "^2.0.0"
}
}
}
},
@@ -14984,9 +15063,7 @@
"uuid": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==",
"dev": true,
"optional": true
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
},
"v8-to-istanbul": {
"version": "7.0.0",
@@ -15202,6 +15279,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
+9 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.4.12",
"version": "1.4.19",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,
@@ -12,7 +12,7 @@
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json",
"start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools",
"test": "jest",
"test-ci": "test"
"test-ci": "npm run test"
},
"repository": {
"type": "git",
@@ -27,6 +27,7 @@
"appId": "net.cozic.joplin-desktop",
"productName": "Joplin",
"npmRebuild": false,
"afterSign": "./tools/notarizeMacApp.js",
"extraResources": [
"build/icons/*",
"build/images/*"
@@ -72,7 +73,10 @@
"artifactName": "${productName}Portable.${ext}"
},
"mac": {
"icon": "../../Assets/macOs.icns"
"icon": "../../Assets/macOs.icns",
"target": "dmg",
"hardenedRuntime": true,
"entitlements": "./build-mac/entitlements.mac.inherit.plist"
},
"linux": {
"icon": "../../Assets/LinuxIcons",
@@ -100,6 +104,7 @@
"babel-preset-react": "^6.24.1",
"electron": "^10.1.6",
"electron-builder": "22.9.1",
"electron-notarize": "^1.0.0",
"electron-rebuild": "^2.3.2",
"glob": "^7.1.6",
"gulp": "^4.0.2",
@@ -136,7 +141,7 @@
"md5": "^2.2.1",
"moment": "^2.22.2",
"node-fetch": "^1.7.3",
"node-notifier": "^6.0.0",
"node-notifier": "^8.0.0",
"pretty-bytes": "^5.3.0",
"re-resizable": "^6.5.4",
"react": "16.13.1",
@@ -381,7 +381,7 @@ class Dialog extends React.PureComponent<Props, State> {
});
if (item.type === BaseModel.TYPE_COMMAND) {
CommandService.instance().execute(item.id);
void CommandService.instance().execute(item.id);
return;
}
@@ -423,7 +423,7 @@ class Dialog extends React.PureComponent<Props, State> {
const parentId = event.currentTarget.getAttribute('data-parent-id');
const itemType = Number(event.currentTarget.getAttribute('data-type'));
this.gotoItem({
void this.gotoItem({
id: itemId,
parent_id: parentId,
type: itemType,
@@ -496,7 +496,7 @@ class Dialog extends React.PureComponent<Props, State> {
const item = this.selectedItem();
if (!item) return;
this.gotoItem(item);
void this.gotoItem(item);
}
}
@@ -6,8 +6,11 @@ import bridge from '../bridge';
import Setting from '@joplin/lib/models/Setting';
import { EventHandlers } from '@joplin/lib/services/plugins/utils/mapEventHandlersToIds';
import shim from '@joplin/lib/shim';
import Logger from '@joplin/lib/Logger';
const ipcRenderer = require('electron').ipcRenderer;
const logger = Logger.create('PluginRunner');
enum PluginMessageTarget {
MainWindow = 'mainWindow',
Plugin = 'plugin',
@@ -127,7 +130,9 @@ export default class PluginRunner extends BasePluginRunner {
const mappedArgs = mapEventIdsToHandlers(plugin.id, message.args);
const fullPath = `joplin.${message.path}`;
this.logger().debug(`PluginRunner: execute call: ${fullPath}: ${mappedArgs}`);
// Don't log complete HTML code, which can be long, for setHtml calls
const debugMappedArgs = fullPath.includes('setHtml') ? '<hidden>' : mappedArgs;
logger.debug(`Got message (3): ${fullPath}: ${debugMappedArgs}`);
let result: any = null;
let error: any = null;
@@ -7,7 +7,10 @@ import useSubmitHandler from './hooks/useSubmitHandler';
import useHtmlLoader from './hooks/useHtmlLoader';
import useWebviewToPluginMessages from './hooks/useWebviewToPluginMessages';
import useScriptLoader from './hooks/useScriptLoader';
const styled = require('styled-components').default;
import Logger from '@joplin/lib/Logger';
import styled from 'styled-components';
const logger = Logger.create('UserWebview');
export interface Props {
html: string;
@@ -72,6 +75,9 @@ function UserWebview(props: Props, ref: any) {
function postMessage(name: string, args: any = null) {
const win = frameWindow();
if (!win) return;
logger.debug('Got message', name, args);
win.postMessage({ target: 'webview', name, args }, '*');
}
@@ -112,6 +118,7 @@ function UserWebview(props: Props, ref: any) {
useWebviewToPluginMessages(
frameWindow(),
isReady,
props.onMessage,
props.pluginId,
props.viewId
@@ -58,10 +58,10 @@ const webviewApi = {
setHtml: (args) => {
contentElement.innerHTML = args.html;
console.debug('UserWebView frame: setting html to', args.html);
// console.debug('UserWebviewIndex: setting html to', args.html);
window.requestAnimationFrame(() => {
console.debug('UserWebView frame: setting html callback', args.hash);
console.debug('UserWebviewIndex: setting html callback', args.hash);
window.postMessage({ target: 'UserWebview', message: 'htmlIsSet', hash: args.hash }, '*');
});
},
@@ -105,6 +105,7 @@ const webviewApi = {
if (!ipc[callName]) {
console.warn('Missing IPC function:', event.data);
} else {
console.debug('UserWebviewIndex: Got message', callName, args);
ipc[callName](args);
}
}));
@@ -115,7 +116,7 @@ const webviewApi = {
// Need to send it with a delay to make sure all listeners are
// ready when the message is sent.
window.requestAnimationFrame(() => {
console.debug('UserWebView frame: calling isReady');
console.debug('UserWebViewIndex: calling isReady');
window.postMessage({ target: 'UserWebview', message: 'ready' }, '*');
});
});
@@ -39,7 +39,7 @@ export default function(frameWindow: any, isReady: boolean, postMessage: Functio
if (!isReady) return;
console.info('useHtmlLoader: setHtml', htmlHash, html);
console.info('useHtmlLoader: setHtml', htmlHash);
postMessage('setHtml', {
hash: htmlHash,
@@ -49,7 +49,7 @@ export default function useThemeCss(dep: HookDependencies) {
setCssFilePath(filePath);
}
createThemeStyleSheet();
void createThemeStyleSheet();
return () => {
cancelled = true;
@@ -1,11 +1,20 @@
import Logger from '@joplin/lib/Logger';
import { useEffect } from 'react';
export default function(frameWindow: any, onMessage: Function, pluginId: string, viewId: string) {
const logger = Logger.create('useWebviewToPluginMessages');
export default function(frameWindow: any, isReady: boolean, onMessage: Function, pluginId: string, viewId: string) {
useEffect(() => {
if (!frameWindow) return () => {};
function onMessage(event: any) {
function onMessage_(event: any) {
if (!event.data || event.data.target !== 'plugin') return;
// The message is passed from one component or service to the next
// till it reaches its destination, so if something doesn't work
// follow the chain of messages searching for the string "Got message"
logger.debug('Got message (WebView => Plugin) (1)', pluginId, viewId, event.data.message);
onMessage({
pluginId: pluginId,
viewId: viewId,
@@ -13,10 +22,10 @@ export default function(frameWindow: any, onMessage: Function, pluginId: string,
});
}
frameWindow.addEventListener('message', onMessage);
frameWindow.addEventListener('message', onMessage_);
return () => {
frameWindow.removeEventListener('message', onMessage);
frameWindow.removeEventListener('message', onMessage_);
};
}, [onMessage, pluginId, viewId]);
}, [frameWindow, onMessage, isReady, pluginId, viewId]);
}
@@ -2,6 +2,10 @@
import SpellCheckerServiceDriverBase from '@joplin/lib/services/spellChecker/SpellCheckerServiceDriverBase';
import bridge from '../bridge';
import { languageCodeOnly, localesFromLanguageCode } from '@joplin/lib/locale';
import Logger from '@joplin/lib/Logger';
const logger = Logger.create('SpellCheckerServiceDriverNative');
export default class SpellCheckerServiceDriverNative extends SpellCheckerServiceDriverBase {
@@ -17,7 +21,31 @@ export default class SpellCheckerServiceDriverNative extends SpellCheckerService
public setLanguage(v: string) {
// If we pass an empty array, it disables spell checking
// https://github.com/electron/electron/issues/25228
this.session().setSpellCheckerLanguages(v ? [v] : []);
if (!v) {
this.session().setSpellCheckerLanguages([]);
return;
}
// The below function will throw an error if the provided language is
// not supported, so we provide fallbacks.
// https://github.com/laurent22/joplin/issues/4146
const languagesToTry = [
v,
languageCodeOnly(v),
].concat(localesFromLanguageCode(languageCodeOnly(v), this.availableLanguages));
for (const toTry of languagesToTry) {
try {
this.session().setSpellCheckerLanguages([toTry]);
logger.info(`Set effective language from "${v}" to "${toTry}"`);
return;
} catch (error) {
logger.warn(`Failed to set language to "${toTry}". Will try the next one in this list: ${JSON.stringify(languagesToTry)}`);
logger.warn('Error was:', error);
}
}
logger.error(`Could not set language to: ${v}`);
}
public get language(): string {
@@ -0,0 +1,45 @@
const fs = require('fs');
const path = require('path');
const electron_notarize = require('electron-notarize');
module.exports = async function(params) {
if (process.platform !== 'darwin') return;
if (!process.env.APPLE_ID || !process.env.APPLE_ID_PASSWORD) {
console.warn('Environment variables APPLE_ID and APPLE_ID_PASSWORD not found - notarization will NOT be done.');
return;
}
// Same appId in electron-builder.
const appId = 'net.cozic.joplin-desktop';
const appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
if (!fs.existsSync(appPath)) {
throw new Error(`Cannot find application at: ${appPath}`);
}
console.log(`Notarizing ${appId} found at ${appPath}`);
await electron_notarize.notarize({
appBundleId: appId,
appPath: appPath,
// Apple Developer email address
appleId: process.env.APPLE_ID,
// App-specific password: https://support.apple.com/en-us/HT204397
appleIdPassword: process.env.APPLE_ID_PASSWORD,
// When Apple ID is attached to multiple providers (eg if the
// account has been used to build multiple apps for different
// companies), in that case the provider "Team Short Name" (also
// known as "ProviderShortname") must be provided.
//
// Use this to get it:
//
// xcrun altool --list-providers -u APPLE_ID -p APPLE_ID_PASSWORD
ascProvider: process.env.APPLE_ASC_PROVIDER,
});
console.log(`Done notarizing ${appId}`);
};
+1
View File
@@ -6,5 +6,6 @@
],
"exclude": [
"**/node_modules",
"**/dist",
],
}
+2 -2
View File
@@ -138,8 +138,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097608
versionName "1.4.6"
versionCode 2097613
versionName "1.4.11"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
@@ -186,7 +186,7 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
setSource(undefined);
setInjectedJs([]);
} else {
renderNote();
void renderNote();
}
return () => {
+11 -11
View File
@@ -259,11 +259,11 @@ class NoteScreenComponent extends BaseScreenComponent {
}
screenHeader_undoButtonPress() {
this.undoRedo('undo');
void this.undoRedo('undo');
}
screenHeader_redoButtonPress() {
this.undoRedo('redo');
void this.undoRedo('redo');
}
styles() {
@@ -404,7 +404,7 @@ class NoteScreenComponent extends BaseScreenComponent {
// Although it is async, we don't wait for the answer so that if permission
// has already been granted, it doesn't slow down opening the note. If it hasn't
// been granted, the popup will open anyway.
this.requestGeoLocationPermissions();
void this.requestGeoLocationPermissions();
}
onMarkForDownload(event: any) {
@@ -703,7 +703,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
cameraView_onPhoto(data: any) {
this.attachFile(
void this.attachFile(
{
uri: data.uri,
didCancel: false,
@@ -810,14 +810,14 @@ class NoteScreenComponent extends BaseScreenComponent {
output.push({
title: _('View on map'),
onPress: () => {
this.showOnMap_onPress();
void this.showOnMap_onPress();
},
});
if (note.source_url) {
output.push({
title: _('Go to source URL'),
onPress: () => {
this.showSource_onPress();
void this.showSource_onPress();
},
});
}
@@ -866,8 +866,8 @@ class NoteScreenComponent extends BaseScreenComponent {
const buttonId = await dialogs.pop(this, _('Choose an option'), buttons);
if (buttonId === 'takePhoto') this.takePhoto_onPress();
if (buttonId === 'attachFile') this.attachFile_onPress();
if (buttonId === 'attachPhoto') this.attachPhoto_onPress();
if (buttonId === 'attachFile') void this.attachFile_onPress();
if (buttonId === 'attachPhoto') void this.attachPhoto_onPress();
},
});
}
@@ -884,7 +884,7 @@ class NoteScreenComponent extends BaseScreenComponent {
output.push({
title: _('Share'),
onPress: () => {
this.share_onPress();
void this.share_onPress();
},
});
if (isSaved) {
@@ -918,7 +918,7 @@ class NoteScreenComponent extends BaseScreenComponent {
output.push({
title: _('Delete'),
onPress: () => {
this.deleteNote_onPress();
void this.deleteNote_onPress();
},
});
@@ -1010,7 +1010,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
onBodyViewerCheckboxChange(newBody: string) {
this.saveOneProperty('body', newBody);
void this.saveOneProperty('body', newBody);
}
render() {
@@ -338,13 +338,13 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 56;
CURRENT_PROJECT_VERSION = 57;
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 = 10.4.0;
MARKETING_VERSION = 10.4.1;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -365,12 +365,12 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 56;
CURRENT_PROJECT_VERSION = 57;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 10.4.0;
MARKETING_VERSION = 10.4.1;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
+1 -1
View File
@@ -54,7 +54,7 @@ export default class AsyncActionQueue {
this.scheduleProcessingIID_ = shim.setTimeout(() => {
this.scheduleProcessingIID_ = null;
this.processQueue();
void this.processQueue();
}, interval);
}
-8
View File
@@ -255,14 +255,6 @@ class BaseModel {
if (!options) options = {};
if (options.order && options.order.length) {
// const items = [];
// for (let i = 0; i < options.order.length; i++) {
// const o = options.order[i];
// let item = `\`${o.by}\``;
// if (options.caseInsensitive === true) item += ' COLLATE NOCASE';
// if (o.dir) item += ` ${o.dir}`;
// items.push(item);
// }
sql += ` ORDER BY ${paginationToSql(options)}`;
}
+1 -1
View File
@@ -354,7 +354,7 @@ export default class Synchronizer {
this.lockHandler().startAutoLockRefresh(syncLock, (error: any) => {
this.logger().warn('Could not refresh lock - cancelling sync. Error was:', error);
this.syncTargetIsLocked_ = true;
this.cancel();
void this.cancel();
});
// ========================================================================
+7 -1
View File
@@ -572,6 +572,12 @@ function languageCode() {
return languageCodeOnly(currentLocale_);
}
function localesFromLanguageCode(languageCode: string, locales: string[]): string[] {
return locales.filter((l: string) => {
return languageCodeOnly(l) === languageCode;
});
}
function _(s: string, ...args: any[]) {
const strings = localeStrings(currentLocale_);
let result = strings[s];
@@ -588,4 +594,4 @@ function _n(singular: string, plural: string, n: number, ...args: any[]) {
return _(singular, ...args);
}
export { _, _n, supportedLocales, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
export { _, _n, supportedLocales, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
+6
View File
@@ -446,6 +446,12 @@ class Setting extends BaseModel {
options: () => themeOptions(),
},
notificationPermission: {
value: '',
type: SettingItemType.String,
public: false,
},
showNoteCounts: { value: true, type: SettingItemType.Bool, public: false, advanced: true, appTypes: ['desktop'], label: () => _('Show note counts') },
layoutButtonSequence: {
+1 -1
View File
@@ -6,7 +6,7 @@ export default function(pagination: Pagination): string {
for (let i = 0; i < pagination.order.length; i++) {
const o = pagination.order[i];
let item = `\`${o.by}\``;
if (o.caseInsensitive === true) item += ' COLLATE NOCASE';
if (!!o.caseInsensitive || !!pagination.caseInsensitive) item += ' COLLATE NOCASE';
item += ` ${o.dir}`;
sql.push(item);
}
+1
View File
@@ -13,4 +13,5 @@ export interface Pagination {
limit: number;
order: PaginationOrder[];
page: number;
caseInsensitive?: boolean;
}
+98 -19
View File
@@ -1,6 +1,7 @@
import eventManager from '../eventManager';
import { Notification } from '../models/Alarm';
import shim from '../shim';
import Setting from '../models/Setting';
const notifier = require('node-notifier');
const bridge = require('electron').remote.require('./bridge').default;
@@ -37,12 +38,96 @@ export default class AlarmServiceDriverNode {
return id in this.notifications_;
}
async clearNotification(id: number) {
clearNotification(id: number) {
if (!this.notificationIsSet(id)) return;
shim.clearTimeout(this.notifications_[id].timeoutId);
delete this.notifications_[id];
}
private displayDefaultNotification(notification: Notification) {
const o: any = {
appID: this.appName_,
title: notification.title,
icon: `${bridge().electronApp().buildDir()}/icons/512x512.png`,
};
if ('body' in notification) o.message = notification.body;
// Message is required on Windows 7 however we don't want to repeat the title so
// make it an empty string.
// https://github.com/laurent22/joplin/issues/2144
if (!o.message) o.message = '-';
this.logger().info('AlarmServiceDriverNode::scheduleNotification: Triggering notification (default):', o);
notifier.notify(o, (error: any, response: any) => {
this.logger().info('AlarmServiceDriverNode::scheduleNotification: node-notifier response:', error, response);
});
}
private displayMacNotification(notification: Notification) {
// On macOS, node-notifier is broken:
//
// https://github.com/mikaelbr/node-notifier/issues/352
//
// However we can use the native browser notification as described
// there:
//
// https://www.electronjs.org/docs/tutorial/notifications
//
// In fact it's likely that we could use this on other platforms too
try {
const options: any = {
body: notification.body ? notification.body : '-',
onerror: (error: any) => {
this.logger().error('AlarmServiceDriverNode::displayMacNotification', error);
},
};
this.logger().info('AlarmServiceDriverNode::displayMacNotification: Triggering notification (macOS):', notification.title, options);
new Notification(notification.title, options);
} catch (error) {
this.logger().error('AlarmServiceDriverNode::displayMacNotification', error);
}
}
private async checkPermission() {
if (shim.isMac() && shim.isElectron()) {
this.logger().info(`AlarmServiceDriverNode::checkPermission: Permission in settings is "${Setting.value('notificationPermission')}"`);
if (Setting.value('notificationPermission') !== '') return Setting.value('notificationPermission');
// In theory `Notification.requestPermission()` should be used to
// ask for permission but in practice this API is unreliable. In
// particular, it returns "granted" immediately even when
// notifications definitely aren't allowed (and creating a new
// notification would fail).
//
// Because of that, our approach is to trigger a notification, which
// should prompt macOS to ask for permission. Once this is done we
// manually save the result in the settings. Of course it means that
// if permission is changed afterwards, for example from the
// notification center, we won't know it and notifications will
// fail.
//
// All this means that for now this checkPermission function always
// returns "granted" and the setting has only two values: "granted"
// or "" (which means we need to do the check permission trick).
//
// The lack of "denied" value is acceptable in our context because
// if a user doesn't want notifications, they can simply not set
// alarms.
new Notification('Checking permissions...', {
body: 'Permission has been granted',
});
Setting.setValue('notificationPermission', 'granted');
}
return 'granted';
}
async scheduleNotification(notification: Notification) {
const now = Date.now();
const interval = notification.date.getTime() - now;
@@ -52,6 +137,12 @@ export default class AlarmServiceDriverNode {
throw new Error(`Trying to create a notification from an invalid object: ${JSON.stringify(notification)}`);
}
const permission = await this.checkPermission();
if (permission !== 'granted') {
this.logger().info(`AlarmServiceDriverNode::scheduleNotification: Notification ${notification.id}: Cancelled because permission was not granted.`);
return;
}
this.logger().info(`AlarmServiceDriverNode::scheduleNotification: Notification ${notification.id} with interval: ${interval}ms`);
if (this.notifications_[notification.id]) shim.clearTimeout(this.notifications_[notification.id].timeoutId);
@@ -72,27 +163,15 @@ export default class AlarmServiceDriverNode {
this.logger().info(`AlarmServiceDriverNode::scheduleNotification: Notification ${notification.id} has been deleted - not rescheduling it`);
return;
}
this.scheduleNotification(this.notifications_[notification.id]);
void this.scheduleNotification(this.notifications_[notification.id]);
}, maxInterval);
} else {
timeoutId = shim.setTimeout(() => {
const o: any = {
appID: this.appName_,
title: notification.title,
icon: `${bridge().electronApp().buildDir()}/icons/512x512.png`,
};
if ('body' in notification) o.message = notification.body;
// Message is required on Windows 7 however we don't want to repeat the title so
// make it an empty string.
// https://github.com/laurent22/joplin/issues/2144
if (!o.message) o.message = '-';
this.logger().info('AlarmServiceDriverNode::scheduleNotification: Triggering notification:', o);
notifier.notify(o, (error: any, response: any) => {
this.logger().info('AlarmServiceDriverNode::scheduleNotification: node-notifier response:', error, response);
});
if (shim.isMac() && shim.isElectron()) {
this.displayMacNotification(notification);
} else {
this.displayDefaultNotification(notification);
}
this.clearNotification(notification.id);
+1 -1
View File
@@ -232,7 +232,7 @@ export default class CommandService extends BaseService {
public scheduleExecute(commandName: string, args: any) {
shim.setTimeout(() => {
this.execute(commandName, args);
void this.execute(commandName, args);
}, 10);
}
+6 -3
View File
@@ -81,7 +81,10 @@ export default class ExternalEditWatcher {
if (!this.chokidar_) return;
if (!this.watcher_) {
this.watcher_ = this.chokidar_.watch(fileToWatch);
this.watcher_ = this.chokidar_.watch(fileToWatch, {
useFsEvents: false,
});
this.watcher_.on('all', async (event: string, path: string) => {
this.logger().debug(`ExternalEditWatcher: Event: ${event}: ${path}`);
@@ -100,7 +103,7 @@ export default class ExternalEditWatcher {
if (!note) {
this.logger().warn(`ExternalEditWatcher: Watched note has been deleted: ${id}`);
this.stopWatching(id);
void this.stopWatching(id);
return;
}
@@ -339,7 +342,7 @@ export default class ExternalEditWatcher {
// avoid update loops. We only want to listen to file changes made by the user.
this.skipNextChangeEvent_[note.id] = true;
this.writeNoteToFile_(note);
await this.writeNoteToFile_(note);
}
async writeNoteToFile_(note: NoteEntity) {
@@ -156,9 +156,14 @@ export default class ResourceEditWatcher {
};
if (!this.watcher_) {
this.watcher_ = this.chokidar_.watch(fileToWatch);
this.watcher_.on('all', async (event: any, path: string) => {
path = toSystemSlashes(path, 'linux');
this.watcher_ = this.chokidar_.watch(fileToWatch, {
// Need to turn off fs-events because when it's on Chokidar
// keeps emitting "modified" events (on "raw" handler), several
// times per seconds, even when nothing is changed.
useFsEvents: false,
});
this.watcher_.on('all', (event: any, path: string) => {
path = path ? toSystemSlashes(path, 'linux') : '';
this.logger().info(`ResourceEditWatcher: Event: ${event}: ${path}`);
@@ -170,7 +175,7 @@ export default class ResourceEditWatcher {
// See: https://github.com/laurent22/joplin/issues/710#issuecomment-420997167
// this.watcher_.unwatch(path);
} else if (event === 'change') {
handleChangeEvent(path);
void handleChangeEvent(path);
} else if (event === 'error') {
this.logger().error('ResourceEditWatcher: error');
}
@@ -185,14 +190,14 @@ export default class ResourceEditWatcher {
// https://github.com/laurent22/joplin/issues/3407
//
// @ts-ignore Leave unused path variable
this.watcher_.on('raw', async (event: string, path: string, options: any) => {
const watchedPath = toSystemSlashes(options.watchedPath, 'linux');
this.watcher_.on('raw', (event: string, path: string, options: any) => {
const watchedPath = options.watchedPath ? toSystemSlashes(options.watchedPath, 'linux') : '';
this.logger().debug(`ResourceEditWatcher: Raw event: ${event}: ${watchedPath}`);
if (event === 'rename') {
this.watcher_.unwatch(watchedPath);
this.watcher_.add(watchedPath);
handleChangeEvent(watchedPath);
void handleChangeEvent(watchedPath);
}
});
} else {
+2 -2
View File
@@ -154,11 +154,11 @@ export default class ResourceService extends BaseService {
const service = this.instance();
service.maintenanceTimer1_ = shim.setTimeout(() => {
service.maintenance();
void service.maintenance();
}, 1000 * 30);
service.maintenanceTimer2_ = shim.setInterval(() => {
service.maintenance();
void service.maintenance();
}, 1000 * 60 * 60 * 4);
}
@@ -52,8 +52,8 @@ export default class ToolbarButtonUtils {
tooltip: this.service.label(commandName),
iconName: command.declaration.iconName,
enabled: newEnabled,
onClick: async () => {
this.service.execute(commandName);
onClick: () => {
void this.service.execute(commandName);
},
title: newTitle,
};
@@ -27,7 +27,7 @@ export default class InteropService_Importer_Md extends InteropService_Importer_
parentFolderId = this.options_.destinationFolder.id;
}
this.importDirectory(sourcePath, parentFolderId);
await this.importDirectory(sourcePath, parentFolderId);
} else {
if (!this.options_.destinationFolder) throw new Error(_('Please specify the notebook where the notes should be imported to.'));
parentFolderId = this.options_.destinationFolder.id;
@@ -52,9 +52,9 @@ export default class InteropService_Importer_Md extends InteropService_Importer_
if (stat.isDirectory()) {
const folderTitle = await Folder.findUniqueItemTitle(basename(stat.path));
const folder = await Folder.save({ title: folderTitle, parent_id: parentFolderId });
this.importDirectory(`${dirPath}/${basename(stat.path)}`, folder.id);
await this.importDirectory(`${dirPath}/${basename(stat.path)}`, folder.id);
} else if (supportedFileExtension.indexOf(fileExtension(stat.path).toLowerCase()) >= 0) {
this.importFile(`${dirPath}/${stat.path}`, parentFolderId);
await this.importFile(`${dirPath}/${stat.path}`, parentFolderId);
}
}
}
@@ -103,7 +103,7 @@ export default class WebviewController extends ViewController {
});
}
public async close() {
public close() {
this.setStoreProp('opened', false);
}
@@ -36,7 +36,7 @@ export default class JoplinPlugins {
// We don't use `await` when calling onStart because the plugin might be awaiting
// in that call too (for example, when opening a dialog on startup) so we don't
// want to get stuck here.
script.onStart({}).catch((error: any) => {
void script.onStart({}).catch((error: any) => {
// For some reason, error thrown from the executed script do not have the type "Error"
// but are instead plain object. So recreate the Error object here so that it can
// be handled correctly by loggers, etc.
@@ -636,7 +636,7 @@ class SearchEngine {
for (const key of parsedQuery.keys) {
if (parsedQuery.terms[key].length === 0) continue;
const term = parsedQuery.terms[key][0].value;
const term = parsedQuery.terms[key].map(x => x.value).join(' ');
if (key === '_') searchOptions.anywherePattern = `*${term}*`;
if (key === 'title') searchOptions.titlePattern = `*${term}*`;
if (key === 'body') searchOptions.bodyPattern = `*${term}*`;
@@ -66,7 +66,7 @@ export default class SpellCheckerService {
public setLanguage(language: string) {
Setting.setValue('spellChecker.language', language);
this.applyStateToDriver();
this.addLatestSelectedLanguage(language);
void this.addLatestSelectedLanguage(language);
}
public get language(): string {
@@ -98,7 +98,7 @@ export default class SpellCheckerService {
output.push({
label: suggestion,
click: () => {
CommandService.instance().execute('replaceSelection', suggestion);
void CommandService.instance().execute('replaceSelection', suggestion);
},
});
}
@@ -115,7 +115,7 @@ export default class SpellCheckerService {
output.push({
label: _('Add to dictionary'),
click: () => {
this.addToDictionary(this.language, misspelledWord);
void this.addToDictionary(this.language, misspelledWord);
},
});
@@ -51,7 +51,7 @@ export default function useSyncTargetUpgrade(): SyncTargetUpgradeResult {
}
useEffect(function() {
upgradeSyncTarget();
void upgradeSyncTarget();
}, []);
return upgradeResult;
+1 -1
View File
@@ -14,7 +14,7 @@
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json",
"test": "jest",
"test-ci": "test"
"test-ci": "npm run test"
},
"author": "",
"license": "MIT",
@@ -2,25 +2,27 @@ const utils = require('../utils');
const glob = require('glob');
const rootDir = utils.rootDir();
console.info(rootDir);
module.exports = {
src: '',
fn: async function() {
const tsFiles = glob.sync(`${rootDir}{/**/*.ts,/**/*.tsx}`, {
const tsFiles = glob.sync('{**/*.ts,**/*.tsx}', {
cwd: rootDir,
ignore: [
'**/.git/**',
'**/api-cli/build/**',
'**/api-cli/tests-build/**',
'**/api-cli/tests/support/plugins/**',
'**/app-desktop/dist/**',
'**/Assets/*',
'**/app-mobile/android/**',
'**/app-mobile/ios/**',
'**/node_modules/**',
'**/plugin_types/**',
'Assets/**/*',
'packages/api-cli/tests/support/plugins/**/*',
'packages/api-desktop/dist/**/*',
'packages/app-cli/build/**/*',
'packages/app-cli/tests-build/**/*',
'packages/app-desktop/dist/**/*',
'packages/app-mobile/android/**/*',
'packages/app-mobile/ios/**/*',
'packages/lib/plugin_types/**/*',
],
})
.filter(f => !f.endsWith('.d.ts'))
.map(f => f.substr(rootDir.length + 1));
}).filter(f => !f.endsWith('.d.ts'));
const ignoredJsFiles = tsFiles.map(f => {
const s = f.split('.');
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+6 -17
View File
@@ -5,21 +5,10 @@ const fetch = require('node-fetch');
const uriTemplate = require('uri-template');
const projectName = 'joplin-android';
const rnDir = `${__dirname}/../../packages/app-mobile`;
const rootDir = path.dirname(__dirname);
const rootDir = path.dirname(path.dirname(__dirname));
const rnDir = `${rootDir}/packages/app-mobile`;
const releaseDir = `${rnDir}/dist`;
// function wslToWinPath(wslPath) {
// const s = wslPath.split('/');
// if (s.length < 3) return s.join('\\');
// s.splice(0, 1);
// if (s[0] !== 'mnt' || s[1].length !== 1) return s.join('\\');
// s.splice(0, 1);
// s[0] = `${s[0].toUpperCase()}:`;
// while (s.length && !s[s.length - 1]) s.pop();
// return s.join('\\');
// }
function increaseGradleVersionCode(content) {
const newContent = content.replace(/versionCode\s+(\d+)/, function(a, versionCode) {
const n = Number(versionCode);
@@ -128,11 +117,11 @@ async function createRelease(name, tagName, version) {
await fs.mkdirp(releaseDir);
console.info(`Copying APK to ${apkFilePath}`);
await fs.copy('app-mobile/android/app/build/outputs/apk/release/app-release.apk', apkFilePath);
await fs.copy(`${rnDir}/android/app/build/outputs/apk/release/app-release.apk`, apkFilePath);
if (name === 'main') {
console.info(`Copying APK to ${releaseDir}/joplin-latest.apk`);
await fs.copy('app-mobile/android/app/build/outputs/apk/release/app-release.apk', `${releaseDir}/joplin-latest.apk`);
await fs.copy(`${rnDir}/android/app/build/outputs/apk/release/app-release.apk`, `${releaseDir}/joplin-latest.apk`);
}
for (const filename in originalContents) {
@@ -170,10 +159,10 @@ async function main() {
if (!isPreRelease) {
console.info('Updating Readme URL...');
let readmeContent = await fs.readFile('README.md', 'utf8');
let readmeContent = await fs.readFile(`${rootDir}/README.md`, 'utf8');
readmeContent = readmeContent.replace(/(https:\/\/github.com\/laurent22\/joplin-android\/releases\/download\/android-v\d+\.\d+\.\d+\/joplin-v\d+\.\d+\.\d+\.apk)/, releaseFiles['main'].downloadUrl);
readmeContent = readmeContent.replace(/(https:\/\/github.com\/laurent22\/joplin-android\/releases\/download\/android-v\d+\.\d+\.\d+\/joplin-v\d+\.\d+\.\d+-32bit\.apk)/, releaseFiles['32bit'].downloadUrl);
await fs.writeFile('README.md', readmeContent);
await fs.writeFile(`${rootDir}/README.md`, readmeContent);
}
await execCommandVerbose('git', ['pull']);
+1 -1
View File
@@ -1,6 +1,6 @@
# Joplin terminal app changelog
## [cli-v1.4.7](https://github.com/laurent22/joplin/releases/tag/cli-v1.4.7) - 2020-11-26T12:15:28Z
## [cli-v1.4.9](https://github.com/laurent22/joplin/releases/tag/cli-v1.4.9) - 2020-11-26T15:00:37Z
- Improved: Allow exporting conflict notes (#4095)
- Improved: Allow lowercase filters when doing search
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"include": [
"**/*.ts",
"**/*.tsx",
],
}