You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-08 00:14:28 +02:00
Compare commits
39 Commits
android-1.
...
mac_notari
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87045a7b60 | ||
|
|
35242b9735 | ||
|
|
1851b0e7d1 | ||
|
|
0fcb6441de | ||
|
|
76c4d99b87 | ||
|
|
849ef418a6 | ||
|
|
8fbd1ae21a | ||
|
|
d733c0e010 | ||
|
|
a48e5cd4e8 | ||
|
|
03942a0073 | ||
|
|
0bc53dc063 | ||
|
|
56605beea2 | ||
|
|
8059d3fbd1 | ||
|
|
46c38ce0e0 | ||
|
|
dfa928c1f7 | ||
|
|
cb696276da | ||
|
|
5f80628a4d | ||
|
|
b77f868fc8 | ||
|
|
6ad9931e43 | ||
|
|
011a65f73b | ||
|
|
72ccc90ea0 | ||
|
|
f3e6c0da32 | ||
|
|
9308c3f38c | ||
|
|
c8a7c70838 | ||
|
|
40f6dcfb4c | ||
|
|
09785cf366 | ||
|
|
7279b508db | ||
|
|
d7996c9707 | ||
|
|
f0432e724a | ||
|
|
2f9bb7b8c0 | ||
|
|
f4b8b5b160 | ||
|
|
e2962322be | ||
|
|
c982e42999 | ||
|
|
eed52a5cfd | ||
|
|
6272a2eb4f | ||
|
|
69a4a895d4 | ||
|
|
511e4b1da0 | ||
|
|
7fa483d27c | ||
|
|
9b64c1fbdb |
@@ -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
|
||||
|
||||
@@ -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
.gitignore
vendored
87
.gitignore
vendored
@@ -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
|
||||
|
||||
2
BUILD.md
2
BUILD.md
@@ -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
|
||||
|
||||
|
||||
17
README.md
17
README.md
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
|
||||
setNotes(result);
|
||||
}
|
||||
|
||||
fetchNotes();
|
||||
void fetchNotes();
|
||||
}, [props.noteIds]);
|
||||
|
||||
const appApi = async () => {
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
116
packages/app-desktop/package-lock.json
generated
116
packages/app-desktop/package-lock.json
generated
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
45
packages/app-desktop/tools/notarizeMacApp.js
Normal file
45
packages/app-desktop/tools/notarizeMacApp.js
Normal file
@@ -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}`);
|
||||
};
|
||||
@@ -6,5 +6,6 @@
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules",
|
||||
"**/dist",
|
||||
],
|
||||
}
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class AsyncActionQueue {
|
||||
|
||||
this.scheduleProcessingIID_ = shim.setTimeout(() => {
|
||||
this.scheduleProcessingIID_ = null;
|
||||
this.processQueue();
|
||||
void this.processQueue();
|
||||
}, interval);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -13,4 +13,5 @@ export interface Pagination {
|
||||
limit: number;
|
||||
order: PaginationOrder[];
|
||||
page: number;
|
||||
caseInsensitive?: boolean;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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,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
tsconfig.eslint.json
Normal file
7
tsconfig.eslint.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user