1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

103 Commits

Author SHA1 Message Date
Laurent Cozic
3bd0656eab Android release v1.2.4 2020-10-08 12:51:48 +01:00
Laurent Cozic
e9af71dd76 Android: Reverted app to singleTop launch mode and fixed potential crash when sharing with app 2020-10-08 11:49:39 +01:00
Laurent Cozic
73b33e8e32 Android: Fixes #3800: Simplify initialisation code to prevent sharing
with app to create multiple instance of app and break settings.

Revert "Mobile: Add startup screen to show progress of db migration"

This reverts commit 569355a318.
2020-10-08 11:35:29 +01:00
Laurent Cozic
c2c7efee91 Desktop: Also make toggle button area wider 2020-10-07 21:03:56 +01:00
Laurent Cozic
0836fca822 Merge branch 'release-1.2' of github.com:laurent22/joplin into release-1.2 2020-10-07 20:59:50 +01:00
Laurent Cozic
566df5039c Desktop: Fixes #3876: Notebooks and tags click area was too narrow 2020-10-07 20:58:43 +01:00
Laurent Cozic
559655bf33 Android release v1.2.3 2020-10-06 13:06:48 +01:00
Laurent Cozic
0eab23fbcf Android: Set app launchMode to singleInstance to try to fix lost settings issue 2020-10-06 13:02:41 +01:00
Laurent Cozic
f334f4f487 All: Improved handling of database migration failures 2020-10-06 12:47:33 +01:00
Laurent Cozic
00057da17d Electron release v1.2.4 2020-09-30 08:16:46 +01:00
Laurent Cozic
0a05464013 Desktop: Regression: Context menu on sidebar did not work anymore 2020-09-30 08:16:20 +01:00
Laurent Cozic
9ebb574059 Merge branch 'release-1.2' of github.com:laurent22/joplin into release-1.2 2020-09-29 14:27:33 +01:00
Laurent Cozic
d29c3c2466 Desktop: Regression: Sidebar toggle button did not work anymore 2020-09-29 14:26:05 +01:00
Laurent Cozic
a71f1c19ec Android release v1.2.2 2020-09-29 12:40:46 +01:00
Laurent Cozic
485921d879 CLI v1.2.2 2020-09-29 12:34:42 +01:00
Laurent Cozic
15de7572c0 Electron release v1.2.3 2020-09-29 12:32:24 +01:00
Laurent Cozic
09f41dd50e Desktop: Make global search field wider when it has focus 2020-09-29 12:31:19 +01:00
Laurent Cozic
7b8ee467a0 Desktop: Improved rendering of All Notes item in sidebar 2020-09-29 11:49:51 +01:00
Laurent Cozic
99a496d684 Desktop: Always label "Click to add tags" 2020-09-29 11:33:22 +01:00
Laurent Cozic
f43ee123d8 Tools: Fixed tests 2020-09-29 10:54:31 +01:00
Laurent Cozic
f42fb1b871 Changed tag label 2020-09-29 10:51:47 +01:00
Laurent Cozic
cf2442c5b2 Desktop: Fixes #3835: Prevent crash in rare case when opening the config screen 2020-09-29 08:40:14 +01:00
Laurent Cozic
e0e4735b03 Desktop: Fixes #3754: Refresh search results when searching by tag and when a tag is changed 2020-09-29 08:11:52 +01:00
Laurent Cozic
8bd58c9608 Merge branch 'release-1.2' of github.com:laurent22/joplin into release-1.2 2020-09-28 19:19:52 +01:00
Laurent Cozic
215a725ded Mobile: Fixes #3834: Fixed search highlights 2020-09-28 19:19:21 +01:00
Naveen M V
12c0a05af0 Desktop: Keep search fuzzy scores between 0 and 2 (#3812) 2020-09-28 18:58:19 +01:00
Caleb John
a7fa119041 Desktop: Extend functionality of codemirror vim (#3823)
add swapLine(Up/Down)
have `o` use the more complex list indent
enable sync initializing from vim (and maybe emacs)
split keymap stuff into it's own file
2020-09-28 18:57:17 +01:00
Laurent Cozic
7fb52b8b0e Desktop: Fix issue with highlighted search terms in CodeMirror viewer 2020-09-28 18:44:21 +01:00
Laurent Cozic
3e86ae4a82 Desktop: Disable fuzzy search for now due to performance issues 2020-09-28 18:41:16 +01:00
Laurent Cozic
947d81d96d Desktop: Optimised sidebar rendering speed 2020-09-24 14:30:20 +01:00
Laurent Cozic
6ca640d2ed Desktop: Fix: Fade out checked items in Rich Text editor too 2020-09-23 17:49:25 +01:00
Laurent Cozic
6aca233b21 CLI v1.2.1 2020-09-23 12:16:58 +01:00
Laurent Cozic
2200be697e Cli: Fixed crash due to missing spellfix extension 2020-09-23 12:14:17 +01:00
Laurent Cozic
25ab3c323b Desktop: Fixes #3801: Fixed editor font size 2020-09-23 11:39:36 +01:00
Laurent Cozic
5bf30a9586 Merge branch 'release-1.2' into dev 2020-09-23 10:24:55 +01:00
Laurent Cozic
b6779a8074 Desktop: Fixes #3810: Only disable relevant toolbar buttons when editor is read-only 2020-09-23 10:21:24 +01:00
Caleb John
59599d318c Desktop: Adjust the codemirror code block colors for the dark theme (#3794) 2020-09-23 09:34:39 +01:00
Arda Kılıçdağı
538600fd6c All: Translation: Update tr_TR.po (#3798) 2020-09-22 21:12:31 -04:00
Ji-Hyeon Gim
63264ba471 All: Translation: Update ko.po (#3778)
This patch includes the translation of missing strings, the improvement of the existing translation

Signed-off-by: Ji-Hyeon Gim <potatogim@potatogim.net>
2020-09-22 21:11:55 -04:00
Laurent Cozic
95e7f3df7d Electron release v1.2.2 2020-09-22 16:39:18 +01:00
Laurent Cozic
366fd2a333 Fixed desktop build 2020-09-22 16:38:47 +01:00
Laurent Cozic
5be99a4a16 Merge branch 'release-1.2' of github.com:laurent22/joplin into release-1.2 2020-09-22 16:37:23 +01:00
Laurent Cozic
d86f6a1fbd Tools: Require setting type flag for new Android releases 2020-09-22 16:36:46 +01:00
Laurent Cozic
7d68208cb4 Android release v1.2.1 2020-09-22 16:17:11 +01:00
Laurent Cozic
e9de9d9128 Electron release v1.2.1 2020-09-22 16:02:51 +01:00
Laurent Cozic
1af16d9f0b Tools: Update package locks 2020-09-22 16:02:22 +01:00
Laurent Cozic
8e11eababa Android: Fixes #3797: Disable beta editor even if it was already enabled 2020-09-22 16:01:00 +01:00
Laurent Cozic
4ec9faadd5 Desktop: Disable auto-update by default 2020-09-22 15:41:25 +01:00
Laurent Cozic
5cf462c885 Tools: Increase release version to 1.2 2020-09-22 15:31:32 +01:00
Laurent Cozic
f7ef0a2b1e Tools: Added script to automatically increase major and minor version numbers on new releases 2020-09-22 15:30:20 +01:00
Laurent Cozic
870f55a6c5 Merge branch 'release-1.2' of github.com:laurent22/joplin into release-1.2 2020-09-22 14:37:34 +01:00
Caleb John
7f7e38b434 Desktop, Mobile: Resolves #3740: Upgrade Mermaid to v8.8.0 (#3745)
Co-authored-by: Laurent <laurent22@users.noreply.github.com>
2020-09-22 13:21:35 +01:00
Caleb John
460a07b1a3 Desktop: Fix missed highlighting when using the global search (#3717) 2020-09-22 13:17:51 +01:00
Caleb John
48c9b86d2b Desktop: Fixes #3791: Add stricter rules for katex blocks (#3795) 2020-09-22 13:16:37 +01:00
Caleb John
7202066c1f Desktop: Fix bug where editor would scroll to focus global search (#3787) 2020-09-22 13:12:22 +01:00
Carlos Eduardo
5226f0019b Desktop: Add frequently used languages to markdown editor (#3786) 2020-09-22 13:11:12 +01:00
Laurent Cozic
26ac745419 Deskop, Cli: Fixes #3780: Fixed link generation when exporting to PDF or HTML 2020-09-22 12:56:56 +01:00
Laurent Cozic
b3f2bbee5b Desktop, Cli: Fixes #3760: Improved handling of special characters when exporting to Markdown 2020-09-22 12:06:19 +01:00
Laurent Cozic
56c6cfc785 Update website 2020-09-21 17:53:14 +01:00
Laurent Cozic
1db4932573 Merge branch 'release-1.2' into dev 2020-09-21 17:52:25 +01:00
Laurent Cozic
a2873ebbc5 Merge branch 'release-1.1' into dev 2020-09-21 17:52:19 +01:00
Laurent Cozic
f652011d59 Desktop: Fixes #3748: Fixed issue when switching from search to "All notes" 2020-09-21 17:50:59 +01:00
Laurent Cozic
27c572b2f5 Desktop: Fixes #3700: Disable editor shortcuts when a dialog, such as GotoAnything, is visible 2020-09-21 17:31:25 +01:00
Laurent Cozic
7a4c97618d Desktop: Improved menu enabled states when not in main screen 2020-09-21 17:09:57 +01:00
Laurent Cozic
3ac4fbeee5 Desktop, Mobile: Fixes #3698: Always use light theme for notes in HTML mode 2020-09-21 16:41:24 +01:00
Laurent Cozic
9e05fa553c Desktop: Fixes #3684: Allow Read Time label to be translated 2020-09-21 16:16:28 +01:00
Laurent Cozic
d4f0d2423d CLI v1.1.8 2020-09-21 13:03:33 +01:00
Laurent Cozic
abdd7e3256 Tools: Improved git changelog 2020-09-21 13:01:46 +01:00
Laurent Cozic
f3ea476f27 Merge branch 'release-1.1' of github.com:laurent22/joplin into release-1.1 2020-09-21 12:40:28 +01:00
Laurent Cozic
aa22af443c Tools: Clean up after spellfix build 2020-09-21 12:35:20 +01:00
Laurent Cozic
ce3bd2a47d Tools: Fixed Cli version handling 2020-09-21 12:16:05 +01:00
Laurent Cozic
a9b26246e6 Merge branch 'dev' into release-1.2 2020-09-21 11:56:32 +01:00
Laurent Cozic
cc1e941dd9 Merge branch 'release-1.1' into dev 2020-09-21 11:55:47 +01:00
Laurent Cozic
9610b7e6bd Electron release v1.1.4 2020-09-21 11:42:26 +01:00
Marc BOUVIER
ad85a12535 All: Translation: Update fr_FR.po (#3776) 2020-09-19 13:35:05 -04:00
Ji-Hyeon Gim
b825346829 All: Translation: Update ko.po (#3771)
Update ko.po translations.

Signed-off-by: Ji-Hyeon Gim <potatogim@potatogim.net>
2020-09-19 13:32:37 -04:00
Ettore Atalan
bd4cbaf93d All: Translation: Update de_DE.po (#3770)
This patch includes the translation of missing strings, the improvement of the existing translation and the replacement of Anglicisms by German words.
2020-09-19 13:31:25 -04:00
Laurent Cozic
9af2a19bdf Merge branch 'dev' into release-1.2 2020-09-19 14:22:02 +01:00
Laurent Cozic
d3fa906a9a Merge branch 'dev' of github.com:laurent22/joplin into dev 2020-09-19 14:20:02 +01:00
Gen Neko
22679641ee All: Translation: Update ja_JP.po (#3761) 2020-09-18 01:56:18 -04:00
Laurent Cozic
0ca7457000 Electron release v1.1.3 2020-09-17 10:19:25 +01:00
Laurent Cozic
c84e49c71c All: Fixes #3696: Increased file extension limit to 20 to prevent issue when using external editors 2020-09-17 10:17:45 +01:00
Laurent Cozic
07ab0e986d Merge branch 'release-1.1' of github.com:laurent22/joplin into release-1.1 2020-09-17 10:00:30 +01:00
Laurent Cozic
17957f5da4 Desktop, Cli: Do not prevent export when one item is still encrypted 2020-09-17 10:00:13 +01:00
Naveen M V
a7b5d43e69 Desktop: Fix: Creating a note after backward redirection places it in a wrong notebook (#3759) 2020-09-17 09:32:52 +01:00
Caleb John
38eda3f151 Desktop: Fixes #3749: Use joplin list handling in emacs mode (#3758) 2020-09-17 09:29:19 +01:00
Laurent
056285deda Desktop: UI update (#3586) 2020-09-15 14:01:07 +01:00
Laurent Cozic
bdedf69439 Tools: Remove console statement 2020-09-15 12:13:29 +01:00
Laurent Cozic
c9451d8675 Electron release v1.1.2 2020-09-15 12:12:16 +01:00
Laurent Cozic
c38834b04c Updated French translation 2020-09-15 12:08:49 +01:00
Laurent Cozic
851eee1500 Fixed and simplified translations 2020-09-15 12:08:25 +01:00
Laurent Cozic
40e24102ce Merge branch 'release-1.1' into dev 2020-09-14 13:06:56 +01:00
Laurent Cozic
7614a795e9 Fixed tests 2020-09-14 13:06:33 +01:00
Laurent Cozic
1273a1dc5f Merge branch 'dev' of github.com:laurent22/joplin into dev 2020-09-14 11:26:41 +01:00
Laurent Cozic
10909fe4fc Merge branch 'release-1.1' into dev 2020-09-14 11:26:27 +01:00
Laurent Cozic
9b3d3026bf Merge branch 'release-1.0' into dev 2020-09-14 11:26:18 +01:00
Laurent Cozic
96076c84f4 CLI v1.0.168 2020-09-14 09:50:09 +01:00
Helmut K. C. Tessarek
2c553db45a Update translations 2020-09-14 02:22:27 -04:00
Laurent Cozic
7d7005596f Desktop: Security: Upgrade packages to fix vulnerabilities 2020-09-13 17:25:46 +01:00
Laurent Cozic
998dd52adc Desktop: Clarifies labels of certain actions, and added shortcut for note list toggle 2020-09-13 17:21:11 +01:00
Patryk Długajczyk
2a1c6d6475 Linux: Fixes #3720: Fix icons path in AppImage build (#3721) 2020-09-12 00:45:44 +01:00
Laurent Cozic
1ba0644142 Merge branch 'release-1.1' of github.com:laurent22/joplin into release-1.1 2020-09-12 00:24:10 +01:00
Laurent Cozic
88ac57d7f3 Tools: Fixed handling of security vulnerabilities in git-changelog 2020-09-12 00:22:17 +01:00
370 changed files with 30215 additions and 22832 deletions

View File

@@ -69,8 +69,12 @@ ElectronClient/commands/focusElement.js
ElectronClient/commands/startExternalEditing.js
ElectronClient/commands/stopExternalEditing.js
ElectronClient/global.d.js
ElectronClient/gui/Button/Button.js
ElectronClient/gui/ConfigScreen/ButtonBar.js
ElectronClient/gui/ConfigScreen/ConfigScreen.js
ElectronClient/gui/ConfigScreen/SideBar.js
ElectronClient/gui/DropboxLoginScreen.js
ElectronClient/gui/ErrorBoundary.js
ElectronClient/gui/Header/commands/focusSearch.js
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
ElectronClient/gui/KeymapConfig/styles/index.js
@@ -81,8 +85,8 @@ ElectronClient/gui/MainScreen/commands/editAlarm.js
ElectronClient/gui/MainScreen/commands/exportPdf.js
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
ElectronClient/gui/MainScreen/commands/moveToFolder.js
ElectronClient/gui/MainScreen/commands/newFolder.js
ElectronClient/gui/MainScreen/commands/newNote.js
ElectronClient/gui/MainScreen/commands/newNotebook.js
ElectronClient/gui/MainScreen/commands/newTodo.js
ElectronClient/gui/MainScreen/commands/print.js
ElectronClient/gui/MainScreen/commands/renameFolder.js
@@ -94,9 +98,11 @@ ElectronClient/gui/MainScreen/commands/showModalMessage.js
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
ElectronClient/gui/MainScreen/commands/toggleEditors.js
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
@@ -113,9 +119,11 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
ElectronClient/gui/NoteEditor/NoteEditor.js
@@ -125,19 +133,42 @@ ElectronClient/gui/NoteEditor/utils/index.js
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
ElectronClient/gui/NoteEditor/utils/types.js
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
ElectronClient/gui/NoteEditor/utils/useFolder.js
ElectronClient/gui/NoteEditor/utils/useFormNote.js
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
ElectronClient/gui/NoteList/NoteList.js
ElectronClient/gui/NoteListControls/commands/focusSearch.js
ElectronClient/gui/NoteListControls/NoteListControls.js
ElectronClient/gui/NoteListItem.js
ElectronClient/gui/NoteTextViewer.js
ElectronClient/gui/NoteToolbar/NoteToolbar.js
ElectronClient/gui/OneDriveLoginScreen.js
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js
ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
ElectronClient/gui/ResizableLayout/ResizableLayout.js
ElectronClient/gui/ResourceScreen.js
ElectronClient/gui/Root_UpgradeSyncTarget.js
ElectronClient/gui/SearchBar/hooks/useSearch.js
ElectronClient/gui/SearchBar/SearchBar.js
ElectronClient/gui/SearchBar/styles/index.js
ElectronClient/gui/ShareNoteDialog.js
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
ElectronClient/gui/SideBar/SideBar.js
ElectronClient/gui/SideBar/styles/index.js
ElectronClient/gui/StatusScreen/StatusScreen.js
ElectronClient/gui/style/StyledInput.js
ElectronClient/gui/style/StyledTextInput.js
ElectronClient/gui/ToggleEditorsButton/styles/index.js
ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
ElectronClient/gui/ToolbarBase.js
ElectronClient/gui/ToolbarButton/styles/index.js
ElectronClient/gui/ToolbarButton/ToolbarButton.js
ReactNativeClient/lib/AsyncActionQueue.js
ReactNativeClient/lib/checkPermissions.js
ReactNativeClient/lib/commands/historyBackward.js
@@ -147,6 +178,7 @@ ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/hooks/useEffectDebugger.js
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js
ReactNativeClient/lib/hooks/usePropsDebugger.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
@@ -154,6 +186,7 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
ReactNativeClient/lib/JoplinServerApi.js
ReactNativeClient/lib/ntpDate.js
ReactNativeClient/lib/services/CommandService.js
ReactNativeClient/lib/services/debug/populateDatabase.js
ReactNativeClient/lib/services/keychain/KeychainService.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
@@ -176,6 +209,16 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
ReactNativeClient/lib/services/UndoRedoService.js
ReactNativeClient/lib/ShareExtension.js
ReactNativeClient/lib/shareHandler.js
ReactNativeClient/lib/theme.js
ReactNativeClient/lib/themes/aritimDark.js
ReactNativeClient/lib/themes/dark.js
ReactNativeClient/lib/themes/dracula.js
ReactNativeClient/lib/themes/light.js
ReactNativeClient/lib/themes/nord.js
ReactNativeClient/lib/themes/oledDark.js
ReactNativeClient/lib/themes/solarizedDark.js
ReactNativeClient/lib/themes/solarizedLight.js
ReactNativeClient/lib/themes/type.js
ReactNativeClient/lib/versionInfo.js
ReactNativeClient/PluginAssetsLoader.js
ReactNativeClient/setUpQuickActions.js

51
.gitignore vendored
View File

@@ -50,8 +50,8 @@ joplin-webclipper-source.zip
Tools/commit_hook.txt
.vscode/*
*.map
ReactNativeClient/lib/sql-extensions/
!ReactNativeClient/lib/sql-extensions/spellfix.dll
ReactNativeClient/lib/sql-extensions/spellfix.so
ReactNativeClient/lib/sql-extensions/spellfix.dylib
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
CliClient/app/LinkSelector.js
@@ -62,8 +62,12 @@ ElectronClient/commands/focusElement.js
ElectronClient/commands/startExternalEditing.js
ElectronClient/commands/stopExternalEditing.js
ElectronClient/global.d.js
ElectronClient/gui/Button/Button.js
ElectronClient/gui/ConfigScreen/ButtonBar.js
ElectronClient/gui/ConfigScreen/ConfigScreen.js
ElectronClient/gui/ConfigScreen/SideBar.js
ElectronClient/gui/DropboxLoginScreen.js
ElectronClient/gui/ErrorBoundary.js
ElectronClient/gui/Header/commands/focusSearch.js
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
ElectronClient/gui/KeymapConfig/styles/index.js
@@ -74,8 +78,8 @@ ElectronClient/gui/MainScreen/commands/editAlarm.js
ElectronClient/gui/MainScreen/commands/exportPdf.js
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
ElectronClient/gui/MainScreen/commands/moveToFolder.js
ElectronClient/gui/MainScreen/commands/newFolder.js
ElectronClient/gui/MainScreen/commands/newNote.js
ElectronClient/gui/MainScreen/commands/newNotebook.js
ElectronClient/gui/MainScreen/commands/newTodo.js
ElectronClient/gui/MainScreen/commands/print.js
ElectronClient/gui/MainScreen/commands/renameFolder.js
@@ -87,9 +91,11 @@ ElectronClient/gui/MainScreen/commands/showModalMessage.js
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
ElectronClient/gui/MainScreen/commands/toggleEditors.js
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
@@ -106,9 +112,11 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
ElectronClient/gui/NoteEditor/NoteEditor.js
@@ -118,19 +126,42 @@ ElectronClient/gui/NoteEditor/utils/index.js
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
ElectronClient/gui/NoteEditor/utils/types.js
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
ElectronClient/gui/NoteEditor/utils/useFolder.js
ElectronClient/gui/NoteEditor/utils/useFormNote.js
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
ElectronClient/gui/NoteList/NoteList.js
ElectronClient/gui/NoteListControls/commands/focusSearch.js
ElectronClient/gui/NoteListControls/NoteListControls.js
ElectronClient/gui/NoteListItem.js
ElectronClient/gui/NoteTextViewer.js
ElectronClient/gui/NoteToolbar/NoteToolbar.js
ElectronClient/gui/OneDriveLoginScreen.js
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js
ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
ElectronClient/gui/ResizableLayout/ResizableLayout.js
ElectronClient/gui/ResourceScreen.js
ElectronClient/gui/Root_UpgradeSyncTarget.js
ElectronClient/gui/SearchBar/hooks/useSearch.js
ElectronClient/gui/SearchBar/SearchBar.js
ElectronClient/gui/SearchBar/styles/index.js
ElectronClient/gui/ShareNoteDialog.js
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
ElectronClient/gui/SideBar/SideBar.js
ElectronClient/gui/SideBar/styles/index.js
ElectronClient/gui/StatusScreen/StatusScreen.js
ElectronClient/gui/style/StyledInput.js
ElectronClient/gui/style/StyledTextInput.js
ElectronClient/gui/ToggleEditorsButton/styles/index.js
ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
ElectronClient/gui/ToolbarBase.js
ElectronClient/gui/ToolbarButton/styles/index.js
ElectronClient/gui/ToolbarButton/ToolbarButton.js
ReactNativeClient/lib/AsyncActionQueue.js
ReactNativeClient/lib/checkPermissions.js
ReactNativeClient/lib/commands/historyBackward.js
@@ -140,6 +171,7 @@ ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/hooks/useEffectDebugger.js
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js
ReactNativeClient/lib/hooks/usePropsDebugger.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
@@ -147,6 +179,7 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
ReactNativeClient/lib/JoplinServerApi.js
ReactNativeClient/lib/ntpDate.js
ReactNativeClient/lib/services/CommandService.js
ReactNativeClient/lib/services/debug/populateDatabase.js
ReactNativeClient/lib/services/keychain/KeychainService.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
@@ -169,6 +202,16 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
ReactNativeClient/lib/services/UndoRedoService.js
ReactNativeClient/lib/ShareExtension.js
ReactNativeClient/lib/shareHandler.js
ReactNativeClient/lib/theme.js
ReactNativeClient/lib/themes/aritimDark.js
ReactNativeClient/lib/themes/dark.js
ReactNativeClient/lib/themes/dracula.js
ReactNativeClient/lib/themes/light.js
ReactNativeClient/lib/themes/nord.js
ReactNativeClient/lib/themes/oledDark.js
ReactNativeClient/lib/themes/solarizedDark.js
ReactNativeClient/lib/themes/solarizedLight.js
ReactNativeClient/lib/themes/type.js
ReactNativeClient/lib/versionInfo.js
ReactNativeClient/PluginAssetsLoader.js
ReactNativeClient/setUpQuickActions.js

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -11,6 +11,7 @@ Note that all the applications share the same library, which, for historical rea
- macOS, Linux: Install rsync - https://nodejs.org/en/
- macOS: Install Cocoapods - `brew install cocoapods`
- Windows: Install Windows Build Tools - `npm install -g windows-build-tools`
- Linux: Install dependencies - `sudo apt install libnss3 libsecret-1-dev`
## Building
@@ -25,6 +26,8 @@ Then you can test the various applications:
cd ElectronClient
npm start
You can also run it under WSL 2. To do so, [follow these instructions](https://www.beekeeperstudio.io/blog/building-electron-windows-ubuntu-wsl2) to setup your environment.
## Testing the Terminal application
cd CliClient

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -38,42 +38,42 @@ locales['tr_TR'] = require('./tr_TR.json');
locales['vi'] = require('./vi.json');
locales['zh_CN'] = require('./zh_CN.json');
locales['zh_TW'] = require('./zh_TW.json');
stats['ar'] = {"percentDone":82};
stats['eu'] = {"percentDone":35};
stats['bs_BA'] = {"percentDone":85};
stats['bg_BG'] = {"percentDone":68};
stats['ca'] = {"percentDone":54};
stats['ar'] = {"percentDone":80};
stats['eu'] = {"percentDone":34};
stats['bs_BA'] = {"percentDone":83};
stats['bg_BG'] = {"percentDone":66};
stats['ca'] = {"percentDone":53};
stats['hr_HR'] = {"percentDone":28};
stats['cs_CZ'] = {"percentDone":84};
stats['da_DK'] = {"percentDone":76};
stats['de_DE'] = {"percentDone":99};
stats['et_EE'] = {"percentDone":68};
stats['cs_CZ'] = {"percentDone":82};
stats['da_DK'] = {"percentDone":74};
stats['de_DE'] = {"percentDone":95};
stats['et_EE'] = {"percentDone":66};
stats['en_GB'] = {"percentDone":100};
stats['en_US'] = {"percentDone":100};
stats['es_ES'] = {"percentDone":91};
stats['eo'] = {"percentDone":39};
stats['fr_FR'] = {"percentDone":97};
stats['gl_ES'] = {"percentDone":44};
stats['id_ID'] = {"percentDone":95};
stats['it_IT'] = {"percentDone":93};
stats['nl_NL'] = {"percentDone":99};
stats['nl_BE'] = {"percentDone":35};
stats['nb_NO'] = {"percentDone":91};
stats['fa'] = {"percentDone":34};
stats['pl_PL'] = {"percentDone":87};
stats['pt_PT'] = {"percentDone":91};
stats['pt_BR'] = {"percentDone":98};
stats['ro'] = {"percentDone":35};
stats['sl_SI'] = {"percentDone":44};
stats['sv'] = {"percentDone":73};
stats['th_TH'] = {"percentDone":54};
stats['vi'] = {"percentDone":88};
stats['tr_TR'] = {"percentDone":99};
stats['el_GR'] = {"percentDone":93};
stats['ru_RU'] = {"percentDone":90};
stats['sr_RS'] = {"percentDone":74};
stats['zh_CN'] = {"percentDone":99};
stats['zh_TW'] = {"percentDone":98};
stats['ja_JP'] = {"percentDone":99};
stats['ko'] = {"percentDone":89};
stats['es_ES'] = {"percentDone":95};
stats['eo'] = {"percentDone":38};
stats['fr_FR'] = {"percentDone":94};
stats['gl_ES'] = {"percentDone":43};
stats['id_ID'] = {"percentDone":93};
stats['it_IT'] = {"percentDone":91};
stats['nl_NL'] = {"percentDone":96};
stats['nl_BE'] = {"percentDone":34};
stats['nb_NO'] = {"percentDone":88};
stats['fa'] = {"percentDone":80};
stats['pl_PL'] = {"percentDone":96};
stats['pt_PT'] = {"percentDone":89};
stats['pt_BR'] = {"percentDone":96};
stats['ro'] = {"percentDone":78};
stats['sl_SI'] = {"percentDone":42};
stats['sv'] = {"percentDone":71};
stats['th_TH'] = {"percentDone":52};
stats['vi'] = {"percentDone":85};
stats['tr_TR'] = {"percentDone":96};
stats['el_GR'] = {"percentDone":96};
stats['ru_RU'] = {"percentDone":95};
stats['sr_RS'] = {"percentDone":72};
stats['zh_CN'] = {"percentDone":96};
stats['zh_TW'] = {"percentDone":95};
stats['ja_JP'] = {"percentDone":96};
stats['ko'] = {"percentDone":86};
module.exports = { locales: locales, stats: stats };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "1.0.167",
"version": "1.2.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -5880,6 +5880,11 @@
"is-fullwidth-code-point": "^2.0.0"
}
},
"slug": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/slug/-/slug-3.3.4.tgz",
"integrity": "sha512-VpHbtRCEWmgaZsrZcTsVl/Dhw98lcrOYDO17DNmJCNpppI6s3qJvnNu2Q3D4L84/2bi6vkW40mjNQI9oGQsflg=="
},
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -6742,11 +6747,6 @@
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
},
"unorm": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
},
"unpack-string": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/unpack-string/-/unpack-string-0.0.2.tgz",
@@ -6849,14 +6849,6 @@
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
},
"uslug": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/uslug/-/uslug-1.0.4.tgz",
"integrity": "sha1-uaIvCRTgqGFAYz2swwLl9PpFBnc=",
"requires": {
"unorm": ">= 1.0.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@@ -28,7 +28,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.1.167",
"version": "1.2.2",
"bin": {
"joplin": "./main.js"
},
@@ -109,7 +109,7 @@
"terminal-kit": "^1.30.0",
"tkwidgets": "^0.5.26",
"url-parse": "^1.4.7",
"uslug": "^1.0.4",
"slug": "^3.3.4",
"uuid": "^3.0.1",
"valid-url": "^1.0.9",
"word-wrap": "^1.2.3",

View File

@@ -173,7 +173,7 @@ describe('services_KeymapService', () => {
keymapService.initialize('darwin');
const customKeymapItems_Darwin = [
{ command: 'newNote', accelerator: 'Option+Shift+Cmd+N' },
{ command: 'synchronize', accelerator: 'F11' },
{ command: 'synchronize', accelerator: 'Ctrl+F11' },
{ command: 'textBold', accelerator: 'Shift+F5' },
{ command: 'showLocalSearch', accelerator: 'Ctrl+Option+S' },
{ command: 'gotoAnything', accelerator: 'Ctrl+Shift+G' },
@@ -194,7 +194,7 @@ describe('services_KeymapService', () => {
keymapService.initialize('win32');
const customKeymapItems_Win32 = [
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
{ command: 'synchronize', accelerator: 'F11' },
{ command: 'synchronize', accelerator: 'Ctrl+F11' },
{ command: 'textBold', accelerator: 'Shift+F5' },
{ command: 'showLocalSearch', accelerator: 'Ctrl+Alt+S' },
{ command: 'gotoAnything', accelerator: 'Ctrl+Shift+G' },

View File

@@ -1,163 +1,163 @@
/* eslint-disable no-unused-vars */
/* eslint prefer-const: 0*/
require('app-module-path').addPath(__dirname);
// require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, createNTestNotes, switchClient, createNTestFolders } = require('test-utils.js');
const SearchEngine = require('lib/services/searchengine/SearchEngine');
const Note = require('lib/models/Note');
const Folder = require('lib/models/Folder');
const Tag = require('lib/models/Tag');
const ItemChange = require('lib/models/ItemChange');
const Setting = require('lib/models/Setting');
const Resource = require('lib/models/Resource.js');
const { shim } = require('lib/shim');
const ResourceService = require('lib/services/ResourceService.js');
// const { time } = require('lib/time-utils.js');
// const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, createNTestNotes, switchClient, createNTestFolders } = require('test-utils.js');
// const SearchEngine = require('lib/services/searchengine/SearchEngine');
// const Note = require('lib/models/Note');
// const Folder = require('lib/models/Folder');
// const Tag = require('lib/models/Tag');
// const ItemChange = require('lib/models/ItemChange');
// const Setting = require('lib/models/Setting');
// const Resource = require('lib/models/Resource.js');
// const { shim } = require('lib/shim');
// const ResourceService = require('lib/services/ResourceService.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
// process.on('unhandledRejection', (reason, p) => {
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
// });
let engine = null;
// let engine = null;
const ids = (array) => array.map(a => a.id);
// const ids = (array) => array.map(a => a.id);
describe('services_SearchFuzzy', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
// describe('services_SearchFuzzy', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
engine = new SearchEngine();
engine.setDb(db());
// engine = new SearchEngine();
// engine.setDb(db());
Setting.setValue('db.fuzzySearchEnabled', 1);
done();
});
// Setting.setValue('db.fuzzySearchEnabled', 1);
// done();
// });
it('should return note almost matching title', asyncTest(async () => {
let rows;
const n1 = await Note.save({ title: 'If It Ain\'t Baroque, Don\'t Fix It' });
const n2 = await Note.save({ title: 'Important note' });
// it('should return note almost matching title', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'If It Ain\'t Baroque, Don\'t Fix It' });
// const n2 = await Note.save({ title: 'Important note' });
await engine.syncTables();
rows = await engine.search('Broke', { fuzzy: false });
expect(rows.length).toBe(0);
// await engine.syncTables();
// rows = await engine.search('Broke', { fuzzy: false });
// expect(rows.length).toBe(0);
rows = await engine.search('Broke', { fuzzy: true });
expect(rows.length).toBe(1);
expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('Broke', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
rows = await engine.search('title:Broke', { fuzzy: true });
expect(rows.length).toBe(1);
expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('title:Broke', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
rows = await engine.search('title:"Broke"', { fuzzy: true });
expect(rows.length).toBe(1);
expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('title:"Broke"', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
rows = await engine.search('Imprtant', { fuzzy: true });
expect(rows.length).toBe(1);
expect(rows[0].id).toBe(n2.id);
}));
// rows = await engine.search('Imprtant', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n2.id);
// }));
it('should order results by min fuzziness', asyncTest(async () => {
let rows;
const n1 = await Note.save({ title: 'I demand you take me to him' });
const n2 = await Note.save({ title: 'He demanded an answer' });
const n3 = await Note.save({ title: 'Don\'t you make demands of me' });
const n4 = await Note.save({ title: 'No drama for me' });
const n5 = await Note.save({ title: 'Just minding my own business' });
// it('should order results by min fuzziness', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'I demand you take me to him' });
// const n2 = await Note.save({ title: 'He demanded an answer' });
// const n3 = await Note.save({ title: 'Don\'t you make demands of me' });
// const n4 = await Note.save({ title: 'No drama for me' });
// const n5 = await Note.save({ title: 'Just minding my own business' });
await engine.syncTables();
rows = await engine.search('demand', { fuzzy: false });
expect(rows.length).toBe(1);
expect(rows[0].id).toBe(n1.id);
// await engine.syncTables();
// rows = await engine.search('demand', { fuzzy: false });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
rows = await engine.search('demand', { fuzzy: true });
expect(rows.length).toBe(3);
expect(rows[0].id).toBe(n1.id);
expect(rows[1].id).toBe(n3.id);
expect(rows[2].id).toBe(n2.id);
}));
// rows = await engine.search('demand', { fuzzy: true });
// expect(rows.length).toBe(3);
// expect(rows[0].id).toBe(n1.id);
// expect(rows[1].id).toBe(n3.id);
// expect(rows[2].id).toBe(n2.id);
// }));
it('should consider any:1', asyncTest(async () => {
let rows;
const n1 = await Note.save({ title: 'cat' });
const n2 = await Note.save({ title: 'cats' });
const n3 = await Note.save({ title: 'cot' });
// it('should consider any:1', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'cat' });
// const n2 = await Note.save({ title: 'cats' });
// const n3 = await Note.save({ title: 'cot' });
const n4 = await Note.save({ title: 'defenestrate' });
const n5 = await Note.save({ title: 'defenstrate' });
const n6 = await Note.save({ title: 'defenestrated' });
// const n4 = await Note.save({ title: 'defenestrate' });
// const n5 = await Note.save({ title: 'defenstrate' });
// const n6 = await Note.save({ title: 'defenestrated' });
const n7 = await Note.save({ title: 'he defenestrated the cat' });
// const n7 = await Note.save({ title: 'he defenestrated the cat' });
await engine.syncTables();
// await engine.syncTables();
rows = await engine.search('defenestrated cat', { fuzzy: true });
expect(rows.length).toBe(1);
// rows = await engine.search('defenestrated cat', { fuzzy: true });
// expect(rows.length).toBe(1);
rows = await engine.search('any:1 defenestrated cat', { fuzzy: true });
expect(rows.length).toBe(7);
}));
// rows = await engine.search('any:1 defenestrated cat', { fuzzy: true });
// expect(rows.length).toBe(7);
// }));
it('should leave phrase searches alone', asyncTest(async () => {
let rows;
const n1 = await Note.save({ title: 'abc def' });
const n2 = await Note.save({ title: 'def ghi' });
const n3 = await Note.save({ title: 'ghi jkl' });
const n4 = await Note.save({ title: 'def abc' });
const n5 = await Note.save({ title: 'mno pqr ghi jkl' });
// it('should leave phrase searches alone', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'abc def' });
// const n2 = await Note.save({ title: 'def ghi' });
// const n3 = await Note.save({ title: 'ghi jkl' });
// const n4 = await Note.save({ title: 'def abc' });
// const n5 = await Note.save({ title: 'mno pqr ghi jkl' });
await engine.syncTables();
// await engine.syncTables();
rows = await engine.search('abc def', { fuzzy: true });
expect(rows.length).toBe(2);
expect(rows.map(r=>r.id)).toContain(n1.id);
expect(rows.map(r=>r.id)).toContain(n4.id);
// rows = await engine.search('abc def', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// expect(rows.map(r=>r.id)).toContain(n4.id);
rows = await engine.search('"abc def"', { fuzzy: true });
expect(rows.length).toBe(1);
expect(rows.map(r=>r.id)).toContain(n1.id);
// rows = await engine.search('"abc def"', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows.map(r=>r.id)).toContain(n1.id);
rows = await engine.search('"ghi jkl"', { fuzzy: true });
expect(rows.length).toBe(2);
expect(rows.map(r=>r.id)).toContain(n3.id);
expect(rows.map(r=>r.id)).toContain(n5.id);
// rows = await engine.search('"ghi jkl"', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n5.id);
rows = await engine.search('"ghi jkl" mno', { fuzzy: true });
expect(rows.length).toBe(1);
expect(rows.map(r=>r.id)).toContain(n5.id);
// rows = await engine.search('"ghi jkl" mno', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows.map(r=>r.id)).toContain(n5.id);
rows = await engine.search('any:1 "ghi jkl" mno', { fuzzy: true });
expect(rows.length).toBe(2);
expect(rows.map(r=>r.id)).toContain(n3.id);
expect(rows.map(r=>r.id)).toContain(n5.id);
}));
// rows = await engine.search('any:1 "ghi jkl" mno', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// }));
it('should leave wild card searches alone', asyncTest(async () => {
let rows;
const n1 = await Note.save({ title: 'abc def' });
const n2 = await Note.save({ title: 'abcc ghi' });
const n3 = await Note.save({ title: 'abccc ghi' });
const n4 = await Note.save({ title: 'abcccc ghi' });
const n5 = await Note.save({ title: 'wxy zzz' });
// it('should leave wild card searches alone', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'abc def' });
// const n2 = await Note.save({ title: 'abcc ghi' });
// const n3 = await Note.save({ title: 'abccc ghi' });
// const n4 = await Note.save({ title: 'abcccc ghi' });
// const n5 = await Note.save({ title: 'wxy zzz' });
await engine.syncTables();
// await engine.syncTables();
rows = await engine.search('abc*', { fuzzy: true });
// rows = await engine.search('abc*', { fuzzy: true });
expect(rows.length).toBe(4);
expect(rows.map(r=>r.id)).toContain(n1.id);
expect(rows.map(r=>r.id)).toContain(n2.id);
expect(rows.map(r=>r.id)).toContain(n3.id);
expect(rows.map(r=>r.id)).toContain(n4.id);
}));
// expect(rows.length).toBe(4);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// expect(rows.map(r=>r.id)).toContain(n2.id);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n4.id);
// }));
});
// });

View File

@@ -26,10 +26,11 @@ describe('timeUtils', function() {
startDate = new Date('3 Aug 2020 07:30:20');
expect(time.goBackInTime(startDate, 1, 'day')).toBe(endDate.getTime().toString());
// Note: this test randomly fails - https://github.com/laurent22/joplin/issues/3722
startDate = new Date('11 Aug 2020');
endDate = new Date('9 Aug 2020'); // week start;
expect(time.goBackInTime(startDate, 0, 'week')).toBe(endDate.getTime().toString());
// startDate = new Date('11 Aug 2020');
// endDate = new Date('9 Aug 2020'); // week start;
// expect(time.goBackInTime(startDate, 0, 'week')).toBe(endDate.getTime().toString());
startDate = new Date('02 Feb 2020');
endDate = new Date('01 Jan 2020');
@@ -50,9 +51,9 @@ describe('timeUtils', function() {
expect(time.goForwardInTime(startDate, 1, 'day')).toBe(endDate.getTime().toString());
startDate = new Date('9 Aug 2020');
endDate = new Date('9 Aug 2020'); // week start;
expect(time.goForwardInTime(startDate, 0, 'week')).toBe(endDate.getTime().toString());
// startDate = new Date('9 Aug 2020');
// endDate = new Date('9 Aug 2020'); // week start;
// expect(time.goForwardInTime(startDate, 0, 'week')).toBe(endDate.getTime().toString());
startDate = new Date('02 Jan 2020');
endDate = new Date('01 Feb 2020');

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "1.0.25",
"version": "1.2.0",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplinapp.org",
"content_security_policy": "script-src 'self'; object-src 'self'",

View File

@@ -34,16 +34,17 @@ const KeymapService = require('lib/services/KeymapService').default;
const TemplateUtils = require('lib/TemplateUtils');
const CssUtils = require('lib/CssUtils');
const resourceEditWatcherReducer = require('lib/services/ResourceEditWatcher/reducer').default;
// const populateDatabase = require('lib/services/debug/populateDatabase').default;
const versionInfo = require('lib/versionInfo').default;
const commands = [
require('./gui/Header/commands/focusSearch'),
require('./gui/NoteListControls/commands/focusSearch'),
require('./gui/MainScreen/commands/editAlarm'),
require('./gui/MainScreen/commands/exportPdf'),
require('./gui/MainScreen/commands/hideModalMessage'),
require('./gui/MainScreen/commands/moveToFolder'),
require('./gui/MainScreen/commands/newNote'),
require('./gui/MainScreen/commands/newNotebook'),
require('./gui/MainScreen/commands/newFolder'),
require('./gui/MainScreen/commands/newTodo'),
require('./gui/MainScreen/commands/print'),
require('./gui/MainScreen/commands/renameFolder'),
@@ -58,6 +59,7 @@ const commands = [
require('./gui/MainScreen/commands/toggleNoteList'),
require('./gui/MainScreen/commands/toggleSidebar'),
require('./gui/MainScreen/commands/toggleVisiblePanes'),
require('./gui/MainScreen/commands/toggleEditors'),
require('./gui/NoteEditor/commands/focusElementNoteBody'),
require('./gui/NoteEditor/commands/focusElementNoteTitle'),
require('./gui/NoteEditor/commands/showLocalSearch'),
@@ -98,6 +100,8 @@ const appDefaultState = Object.assign({}, defaultState, {
watchedNoteFiles: [],
lastEditorScrollPercents: {},
devToolsVisible: false,
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: null,
});
class Application extends BaseApplication {
@@ -279,6 +283,31 @@ class Application extends BaseApplication {
newState.devToolsVisible = action.value;
break;
case 'VISIBLE_DIALOGS_ADD':
newState = Object.assign({}, state);
newState.visibleDialogs[state.name] = true;
break;
case 'VISIBLE_DIALOGS_REMOVE':
newState = Object.assign({}, state);
delete newState.visibleDialogs[state.name];
break;
case 'FOCUS_SET':
newState = Object.assign({}, state);
newState.focusedField = action.field;
break;
case 'FOCUS_CLEAR':
// A field can only clear its own state
if (action.field === state.focusedField) {
newState = Object.assign({}, state);
newState.focusedField = null;
}
break;
}
} catch (error) {
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
@@ -286,10 +315,11 @@ class Application extends BaseApplication {
}
newState = resourceEditWatcherReducer(newState, action);
newState = super.reducer(newState, action);
CommandService.instance().scheduleMapStateToProps(newState);
return super.reducer(newState, action);
return newState;
}
toggleDevTools(visible) {
@@ -375,7 +405,7 @@ class Application extends BaseApplication {
await this.updateMenu(screen);
}
async updateMenu(screen) {
async updateMenu(screen, updateStates = true) {
if (this.lastMenuScreen_ === screen) return;
const cmdService = CommandService.instance();
@@ -519,7 +549,7 @@ class Application extends BaseApplication {
const newNoteItem = cmdService.commandToMenuItem('newNote');
const newTodoItem = cmdService.commandToMenuItem('newTodo');
const newNotebookItem = cmdService.commandToMenuItem('newNotebook');
const newFolderItem = cmdService.commandToMenuItem('newFolder');
const printItem = cmdService.commandToMenuItem('print');
toolsItemsFirst.push(syncStatusItem, {
@@ -650,7 +680,7 @@ class Application extends BaseApplication {
},
shim.isMac() ? noItem : newNoteItem,
shim.isMac() ? noItem : newTodoItem,
shim.isMac() ? noItem : newNotebookItem, {
shim.isMac() ? noItem : newFolderItem, {
type: 'separator',
visible: shim.isMac() ? false : true,
}, {
@@ -699,7 +729,7 @@ class Application extends BaseApplication {
submenu: [
newNoteItem,
newTodoItem,
newNotebookItem, {
newFolderItem, {
label: _('Close Window'),
platforms: ['darwin'],
accelerator: shim.isMac() && keymapService.getAccelerator('closeWindow'),
@@ -738,7 +768,6 @@ class Application extends BaseApplication {
const separator = () => {
return {
type: 'separator',
screens: ['Main'],
};
};
@@ -986,6 +1015,8 @@ class Application extends BaseApplication {
Menu.setApplicationMenu(menu);
this.lastMenuScreen_ = screen;
if (updateStates) await this.updateMenuItemStates();
}
async updateMenuItemStates(state = null) {
@@ -1095,7 +1126,7 @@ class Application extends BaseApplication {
try {
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
} catch (err) {
bridge().showErrorMessageBox(err.message);
reg.logger().error(err.message);
}
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
@@ -1128,7 +1159,7 @@ class Application extends BaseApplication {
CommandService.instance().registerDeclaration(declaration);
}
this.updateMenu('Main');
this.updateMenu('Main', false);
// Since the settings need to be loaded before the store is created, it will never
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
@@ -1253,6 +1284,8 @@ class Application extends BaseApplication {
};
bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated);
// await populateDatabase(reg.db());
}
}

View File

@@ -11,7 +11,7 @@ interface Props {
export const declaration:CommandDeclaration = {
name: 'startExternalEditing',
label: () => _('Edit in external editor'),
iconName: 'fa-share-square',
iconName: 'icon-share',
};
export const runtime = ():CommandRuntime => {
@@ -27,10 +27,14 @@ export const runtime = ():CommandRuntime => {
// await comp.saveNoteAndWait(comp.formNote);
},
isEnabled: (props:any) => {
if (props.routeName !== 'Main') return false;
return !!props.noteId;
},
mapStateToProps: (state:any) => {
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null };
return {
noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null,
routeName: state.route.routeName,
};
},
};
};

View File

@@ -18,10 +18,11 @@ export const runtime = ():CommandRuntime => {
ExternalEditWatcher.instance().stopWatching(props.noteId);
},
isEnabled: (props:any) => {
if (props.routeName !== 'Main') return false;
return !!props.noteId;
},
mapStateToProps: (state:any) => {
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null };
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null, routeName: state.route.routeName };
},
};
};

View File

@@ -0,0 +1,195 @@
import * as React from 'react';
const styled = require('styled-components').default;
const { space } = require('styled-system');
export enum ButtonLevel {
Primary = 'primary',
Secondary = 'secondary',
Tertiary = 'tertiary',
SideBarSecondary = 'sideBarSecondary',
}
interface Props {
title?: string,
iconName?: string,
level?: ButtonLevel,
className?:string,
onClick:Function,
color?: string,
iconAnimation?: string,
tooltip?: string,
disabled?: boolean,
style?:any,
}
const StyledTitle = styled.span`
`;
const StyledButtonBase = styled.button`
display: flex;
align-items: center;
flex-direction: row;
height: ${(props:any) => `${props.theme.toolbarHeight}px`};
min-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
max-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
width: ${(props:any) => props.iconOnly ? `${props.theme.toolbarHeight}px` : 'auto'};
${(props:any) => props.iconOnly ? `min-width: ${props.theme.toolbarHeight}px;` : ''}
${(props:any) => !props.iconOnly ? 'min-width: 100px;' : ''}
${(props:any) => props.iconOnly ? `max-width: ${props.theme.toolbarHeight}px;` : ''}
box-sizing: border-box;
border-radius: 3px;
border-style: solid;
border-width: 1px;
font-size: ${(props:any) => props.theme.fontSize}px;
padding: 0 ${(props:any) => props.iconOnly ? 4 : 8}px;
justify-content: center;
opacity: ${(props:any) => props.disabled ? 0.5 : 1};
user-select: none;
`;
const StyledIcon = styled(styled.span(space))`
font-size: ${(props:any) => props.theme.toolbarIconSize}px;
${(props:any) => props.animation ? `animation: ${props.animation}` : ''};
`;
const StyledButtonPrimary = styled(StyledButtonBase)`
border: none;
background-color: ${(props:any) => props.theme.backgroundColor5};
&:hover {
background-color: ${(props:any) => props.theme.backgroundColorHover5};
}
&:active {
background-color: ${(props:any) => props.theme.backgroundColorActive5};
}
${StyledIcon} {
color: ${(props:any) => props.theme.color5};
}
${StyledTitle} {
color: ${(props:any) => props.theme.color5};
}
`;
const StyledButtonSecondary = styled(StyledButtonBase)`
border: 1px solid ${(props:any) => props.theme.borderColor4};
background-color: ${(props:any) => props.theme.backgroundColor4};
&:hover {
background-color: ${(props:any) => props.theme.backgroundColorHover4};
}
&:active {
background-color: ${(props:any) => props.theme.backgroundColorActive4};
}
${StyledIcon} {
color: ${(props:any) => props.theme.color4};
}
${StyledTitle} {
color: ${(props:any) => props.theme.color4};
}
`;
const StyledButtonTertiary = styled(StyledButtonBase)`
border: 1px solid ${(props:any) => props.theme.color3};
background-color: ${(props:any) => props.theme.backgroundColor3};
&:hover {
background-color: ${(props:any) => props.theme.backgroundColorHoverDim3};
}
&:active {
background-color: ${(props:any) => props.theme.backgroundColorActive3};
}
${StyledIcon} {
color: ${(props:any) => props.theme.color};
}
${StyledTitle} {
color: ${(props:any) => props.theme.color};
opacity: 0.9;
}
`;
const StyledButtonSideBarSecondary = styled(StyledButtonBase)`
background: none;
border-color: ${(props:any) => props.theme.color2};
color: ${(props:any) => props.theme.color2};
&:hover {
color: ${(props:any) => props.theme.colorHover2};
border-color: ${(props:any) => props.theme.colorHover2};
background: none;
${StyledTitle} {
color: ${(props:any) => props.theme.colorHover2};
}
${StyledIcon} {
color: ${(props:any) => props.theme.colorHover2};
}
}
&:active {
color: ${(props:any) => props.theme.colorActive2};
border-color: ${(props:any) => props.theme.colorActive2};
background: none;
${StyledTitle} {
color: ${(props:any) => props.theme.colorActive2};
}
${StyledIcon} {
color: ${(props:any) => props.theme.colorActive2};
}
}
${StyledTitle} {
color: ${(props:any) => props.theme.color2};
}
${StyledIcon} {
color: ${(props:any) => props.theme.color2};
}
`;
function buttonClass(level:ButtonLevel) {
if (level === ButtonLevel.Primary) return StyledButtonPrimary;
if (level === ButtonLevel.Tertiary) return StyledButtonTertiary;
if (level === ButtonLevel.SideBarSecondary) return StyledButtonSideBarSecondary;
return StyledButtonSecondary;
}
export default function Button(props:Props) {
const iconOnly = props.iconName && !props.title;
const StyledButton = buttonClass(props.level);
function renderIcon() {
if (!props.iconName) return null;
return <StyledIcon animation={props.iconAnimation} mr={iconOnly ? '0' : '6px'} color={props.color} className={props.iconName}/>;
}
function renderTitle() {
if (!props.title) return null;
return <StyledTitle color={props.color}>{props.title}</StyledTitle>;
}
function onClick() {
if (props.disabled) return;
props.onClick();
}
return (
<StyledButton style={props.style} disabled={props.disabled} title={props.tooltip} className={props.className} iconOnly={iconOnly} onClick={onClick}>
{renderIcon()}
{renderTitle()}
</StyledButton>
);
}

View File

@@ -40,10 +40,12 @@ class ClipperConfigScreenComponent extends React.Component {
}
render() {
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const containerStyle = Object.assign({}, theme.containerStyle, {
overflowY: 'scroll',
padding: theme.configScreenPadding,
backgroundColor: theme.backgroundColor3,
});
const buttonStyle = Object.assign({}, theme.buttonStyle, { marginRight: 10 });
@@ -106,8 +108,8 @@ class ClipperConfigScreenComponent extends React.Component {
return (
<div>
<div style={containerStyle}>
<div style={{ padding: theme.margin }}>
<p style={theme.textStyle}>{_('Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.')}</p>
<div>
<p style={Object.assign({}, theme.textStyle, { marginTop: 0 })}>{_('Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.')}</p>
<p style={theme.textStyle}>{_('In order to use the web clipper, you need to do the following:')}</p>
<div style={stepBoxStyle}>
@@ -120,8 +122,8 @@ class ClipperConfigScreenComponent extends React.Component {
<p style={theme.h1Style}>{_('Step 2: Install the extension')}</p>
<p style={theme.textStyle}>{_('Download and install the relevant extension for your browser:')}</p>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<ExtensionBadge theme={this.props.theme} type="firefox" url="https://addons.mozilla.org/en-US/firefox/addon/joplin-web-clipper/"/>
<ExtensionBadge style={{ marginLeft: 10 }} theme={this.props.theme} type="chrome" url="https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek"/>
<ExtensionBadge themeId={this.props.themeId} type="firefox" url="https://addons.mozilla.org/en-US/firefox/addon/joplin-web-clipper/"/>
<ExtensionBadge style={{ marginLeft: 10 }} themeId={this.props.themeId} type="chrome" url="https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek"/>
</div>
</div>
@@ -145,7 +147,7 @@ class ClipperConfigScreenComponent extends React.Component {
const mapStateToProps = state => {
return {
theme: state.settings.theme,
themeId: state.settings.theme,
clipperServer: state.clipperServer,
clipperServerAutoStart: state.settings['clipperServer.autoStart'],
apiToken: state.settings['api.token'],

View File

@@ -1,44 +0,0 @@
const React = require('react');
const styleSelector = require('./style/ConfigMenuBar');
const Setting = require('lib/models/Setting');
function ConfigMenuBarButton(props) {
const style = styleSelector(null, props);
const iconStyle = props.selected ? style.buttonIconSelected : style.buttonIcon;
const labelStyle = props.selected ? style.buttonLabelSelected : style.buttonLabel;
return (
<button style={style.button} onClick={props.onClick}>
<i style={iconStyle} className={props.iconName}></i>
<span style={labelStyle}>{props.label}</span>
</button>
);
}
function ConfigMenuBar(props) {
const buttons = [];
const style = styleSelector(null, props);
for (const section of props.sections) {
buttons.push(<ConfigMenuBarButton
selected={props.selection === section.name}
theme={props.theme}
key={section.name}
iconName={Setting.sectionNameToIcon(section.name)}
label={Setting.sectionNameToLabel(section.name)}
onClick={() => { props.onSelectionChange({ section: section }); }}
/>);
}
return (
<div style={style.root} className="config-menu-bar">
<div style={style.barButtons}>
{buttons}
</div>
</div>
);
}
module.exports = ConfigMenuBar;

View File

@@ -0,0 +1,52 @@
import * as React from 'react';
import Button, { ButtonLevel } from '../Button/Button';
const styled = require('styled-components').default;
const { _ } = require('lib/locale.js');
interface Props {
backButtonTitle?: string,
hasChanges?: boolean,
onCancelClick: Function,
onSaveClick?: Function,
onApplyClick?: Function,
}
export const StyledRoot = styled.div`
display: flex;
align-items: center;
padding: 10px;
background-color: ${(props:any) => props.theme.backgroundColor3};
padding-left: ${(props:any) => props.theme.configScreenPadding}px;
border-top-width: 1px;
border-top-style: solid;
border-top-color: ${(props:any) => props.theme.dividerColor};
`;
export default function ButtonBar(props:Props) {
function renderOkButton() {
if (!props.onSaveClick) return null;
return <Button style={{ marginRight: 10 }} level={ButtonLevel.Primary} disabled={!props.hasChanges} onClick={props.onSaveClick} title={_('OK')}/>;
}
function renderApplyButton() {
if (!props.onApplyClick) return null;
return <Button level={ButtonLevel.Primary} disabled={!props.hasChanges} onClick={props.onApplyClick} title={_('Apply')}/>;
}
return (
<StyledRoot>
<Button
onClick={props.onCancelClick}
level={ButtonLevel.Secondary}
iconName="fa fa-chevron-left"
title={props.backButtonTitle ? props.backButtonTitle : _('Back')}
/>
{ (props.onApplyClick || props.onSaveClick) && (
<div style={{ display: 'flex', flexDirection: 'row', marginLeft: 30 }}>
{renderOkButton()}
{renderApplyButton()}
</div>
)}
</StyledRoot>
);
}

View File

@@ -1,48 +1,63 @@
const React = require('react');
import * as React from 'react';
import SideBar from './SideBar';
import ButtonBar from './ButtonBar';
import Button, { ButtonLevel } from '../Button/Button';
const { connect } = require('react-redux');
const Setting = require('lib/models/Setting.js');
const { bridge } = require('electron').remote.require('./bridge');
const { themeStyle } = require('lib/theme');
const pathUtils = require('lib/path-utils.js');
const { _ } = require('lib/locale.js');
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
const shared = require('lib/components/shared/config-shared.js');
const ConfigMenuBar = require('./ConfigMenuBar.min.js');
const { EncryptionConfigScreen } = require('./EncryptionConfigScreen.min');
const { ClipperConfigScreen } = require('./ClipperConfigScreen.min');
const { KeymapConfigScreen } = require('./KeymapConfig/KeymapConfigScreen');
const { bridge } = require('electron').remote.require('./bridge');
const { EncryptionConfigScreen } = require('../EncryptionConfigScreen.min');
const { ClipperConfigScreen } = require('../ClipperConfigScreen.min');
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
class ConfigScreenComponent extends React.Component {
constructor() {
super();
class ConfigScreenComponent extends React.Component<any, any> {
rowStyle_:any = null;
constructor(props:any) {
super(props);
shared.init(this);
this.state.selectedSectionName = 'general';
this.state.screenName = '';
this.checkSyncConfig_ = async () => {
await shared.checkSyncConfig(this, this.state.settings);
};
this.checkNextcloudAppButton_click = async () => {
this.setState({ showNextcloudAppLog: true });
await shared.checkNextcloudApp(this, this.state.settings);
};
this.showLogButton_click = () => {
this.setState({ showNextcloudAppLog: true });
};
this.nextcloudAppHelpLink_click = () => {
bridge().openExternal('https://joplinapp.org/nextcloud_app');
this.state = {
selectedSectionName: 'general',
screenName: '',
changedSettingKeys: [],
};
this.rowStyle_ = {
marginBottom: 10,
};
this.configMenuBar_selectionChange = this.configMenuBar_selectionChange.bind(this);
this.sideBar_selectionChange = this.sideBar_selectionChange.bind(this);
this.checkSyncConfig_ = this.checkSyncConfig_.bind(this);
this.checkNextcloudAppButton_click = this.checkNextcloudAppButton_click.bind(this);
this.showLogButton_click = this.showLogButton_click.bind(this);
this.nextcloudAppHelpLink_click = this.nextcloudAppHelpLink_click.bind(this);
this.onCancelClick = this.onCancelClick.bind(this);
this.onSaveClick = this.onSaveClick.bind(this);
this.onApplyClick = this.onApplyClick.bind(this);
}
async checkSyncConfig_() {
await shared.checkSyncConfig(this, this.state.settings);
}
async checkNextcloudAppButton_click() {
this.setState({ showNextcloudAppLog: true });
await shared.checkNextcloudApp(this, this.state.settings);
}
showLogButton_click() {
this.setState({ showNextcloudAppLog: true });
}
nextcloudAppHelpLink_click() {
bridge().openExternal('https://joplinapp.org/nextcloud_app');
}
UNSAFE_componentWillMount() {
@@ -57,7 +72,7 @@ class ConfigScreenComponent extends React.Component {
}
}
sectionByName(name) {
sectionByName(name:string) {
const sections = shared.settingsSections({ device: 'desktop', settings: this.state.settings });
for (const section of sections) {
if (section.name === name) return section;
@@ -66,15 +81,15 @@ class ConfigScreenComponent extends React.Component {
throw new Error(`Invalid section name: ${name}`);
}
screenFromName(screenName) {
if (screenName === 'encryption') return <EncryptionConfigScreen theme={this.props.theme}/>;
if (screenName === 'server') return <ClipperConfigScreen theme={this.props.theme}/>;
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.theme}/>;
screenFromName(screenName:string) {
if (screenName === 'encryption') return <EncryptionConfigScreen themeId={this.props.themeId}/>;
if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>;
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>;
throw new Error(`Invalid screen name: ${screenName}`);
}
switchSection(name) {
switchSection(name:string) {
const section = this.sectionByName(name);
let screenName = '';
if (section.isScreen) {
@@ -89,11 +104,11 @@ class ConfigScreenComponent extends React.Component {
this.setState({ selectedSectionName: section.name, screenName: screenName });
}
configMenuBar_selectionChange(event) {
sideBar_selectionChange(event:any) {
this.switchSection(event.section.name);
}
keyValueToArray(kv) {
keyValueToArray(kv:any) {
const output = [];
for (const k in kv) {
if (!kv.hasOwnProperty(k)) continue;
@@ -106,11 +121,11 @@ class ConfigScreenComponent extends React.Component {
return output;
}
renderSectionDescription(section) {
renderSectionDescription(section:any) {
const description = Setting.sectionDescription(section.name);
if (!description) return null;
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
return (
<div style={Object.assign({}, theme.textStyle, { marginBottom: 15 })}>
{description}
@@ -118,10 +133,10 @@ class ConfigScreenComponent extends React.Component {
);
}
sectionToComponent(key, section, settings, selected) {
const theme = themeStyle(this.props.theme);
sectionToComponent(key:string, section:any, settings:any, selected:boolean) {
const theme = themeStyle(this.props.themeId);
const createSettingComponents = (advanced) => {
const createSettingComponents = (advanced:boolean) => {
const output = [];
for (let i = 0; i < section.metadatas.length; i++) {
const md = section.metadatas[i];
@@ -135,9 +150,10 @@ class ConfigScreenComponent extends React.Component {
const settingComps = createSettingComponents(false);
const advancedSettingComps = createSettingComponents(true);
const sectionStyle = {
const sectionStyle:any = {
marginTop: 20,
marginBottom: 20,
maxWidth: 640,
};
if (!selected) sectionStyle.display = 'none';
@@ -161,9 +177,12 @@ class ConfigScreenComponent extends React.Component {
settingComps.push(
<div key="check_sync_config_button" style={this.rowStyle_}>
<button disabled={this.state.checkSyncConfigResult === 'checking'} style={theme.buttonStyle} onClick={this.checkSyncConfig_}>
{_('Check synchronisation configuration')}
</button>
<Button
title={_('Check synchronisation configuration')}
level={ButtonLevel.Secondary}
disabled={this.state.checkSyncConfigResult === 'checking'}
onClick={this.checkSyncConfig_}
/>
{statusComp}
</div>
);
@@ -204,9 +223,7 @@ class ConfigScreenComponent extends React.Component {
&nbsp;&nbsp;
{showLogButton}
&nbsp;&nbsp;
<button disabled={this.state.checkNextcloudAppResult === 'checking'} style={theme.buttonStyle} onClick={this.checkNextcloudAppButton_click}>
{_('Check Status')}
</button>
<Button level={ButtonLevel.Secondary} style={{ display: 'inline-block' }} title={_('Check Status')} disabled={this.state.checkNextcloudAppResult === 'checking'} onClick={this.checkNextcloudAppButton_click}/>
&nbsp;&nbsp;
<a style={theme.urlStyle} href="#" onClick={this.nextcloudAppHelpLink_click}>[{_('Help')}]</a>
{statusComp}
@@ -220,8 +237,17 @@ class ConfigScreenComponent extends React.Component {
if (advancedSettingComps.length) {
const iconName = this.state.showAdvancedSettings ? 'fa fa-angle-down' : 'fa fa-angle-right';
const advancedSettingsButtonStyle = Object.assign({}, theme.buttonStyle, { marginBottom: 10 });
advancedSettingsButton = <button onClick={() => shared.advancedSettingsButton_click(this)} style={advancedSettingsButtonStyle}><i style={{ fontSize: 14 }} className={iconName}></i> {_('Show Advanced Settings')}</button>;
// const advancedSettingsButtonStyle = Object.assign({}, theme.buttonStyle, { marginBottom: 10 });
advancedSettingsButton = (
<div style={{ marginBottom: 10 }}>
<Button
level={ButtonLevel.Secondary}
onClick={() => shared.advancedSettingsButton_click(this)}
iconName={iconName}
title={_('Show Advanced Settings')}
/>
</div>
);
advancedSettingsSectionStyle.display = this.state.showAdvancedSettings ? 'block' : 'none';
}
@@ -235,35 +261,39 @@ class ConfigScreenComponent extends React.Component {
);
}
settingToComponent(key, value) {
const theme = themeStyle(this.props.theme);
settingToComponent(key:string, value:any) {
const theme = themeStyle(this.props.themeId);
const output = null;
const output:any = null;
const rowStyle = this.rowStyle_;
const rowStyle = {
marginBottom: theme.mainPadding,
};
const labelStyle = Object.assign({}, theme.textStyle, {
display: 'inline-block',
marginRight: 10,
display: 'block',
color: theme.color,
fontSize: theme.fontSize * 1.083333,
fontWeight: 500,
marginBottom: theme.mainPadding / 4,
});
const subLabel = Object.assign({}, labelStyle, {
display: 'block',
opacity: 0.7,
marginBottom: Math.round(rowStyle.marginBottom * 0.7),
});
const invisibleLabel = Object.assign({}, labelStyle, {
opacity: 0,
marginBottom: labelStyle.marginBottom,
});
const checkboxLabelStyle = Object.assign({}, labelStyle, {
marginLeft: 8,
display: 'inline',
backgroundColor: 'transparent',
});
const controlStyle = {
display: 'inline-block',
color: theme.color,
fontFamily: theme.fontFamily,
backgroundColor: theme.backgroundColor,
};
@@ -275,13 +305,19 @@ class ConfigScreenComponent extends React.Component {
});
const textInputBaseStyle = Object.assign({}, controlStyle, {
fontFamily: theme.fontFamily,
border: '1px solid',
padding: '4px 6px',
borderColor: theme.dividerColor,
borderRadius: 4,
boxSizing: 'border-box',
borderColor: theme.borderColor4,
borderRadius: 3,
paddingLeft: 6,
paddingRight: 6,
paddingTop: 4,
paddingBottom: 4,
});
const updateSettingValue = (key, value) => {
const updateSettingValue = (key:string, value:any) => {
// console.info(key + ' = ' + value);
return shared.updateSettingValue(this, key, value);
};
@@ -306,7 +342,14 @@ class ConfigScreenComponent extends React.Component {
);
}
const selectStyle = Object.assign({}, controlStyle, { height: 22, borderColor: theme.dividerColor });
const selectStyle = Object.assign({}, controlStyle, {
paddingLeft: 6,
paddingRight: 6,
paddingTop: 4,
paddingBottom: 4,
borderColor: theme.borderColor4,
borderRadius: 3,
});
return (
<div key={key} style={rowStyle}>
@@ -316,7 +359,7 @@ class ConfigScreenComponent extends React.Component {
<select
value={value}
style={selectStyle}
onChange={event => {
onChange={(event:any) => {
updateSettingValue(key, event.target.value);
}}
>
@@ -330,35 +373,38 @@ class ConfigScreenComponent extends React.Component {
updateSettingValue(key, !value);
};
const checkboxSize = theme.fontSize * 1.1666666666666;
// Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes.
// There's probably a better way to do this but can't figure it out.
return (
<div key={key + value.toString()} style={rowStyle}>
<div style={controlStyle}>
<div style={{ ...controlStyle, backgroundColor: 'transparent', display: 'flex', alignItems: 'center' }}>
<input
id={`setting_checkbox_${key}`}
type="checkbox"
checked={!!value}
onChange={event => {
onCheckboxClick(event);
onChange={() => {
onCheckboxClick();
}}
style={{ marginLeft: 0, width: checkboxSize, height: checkboxSize }}
/>
<label
onClick={event => {
onCheckboxClick(event);
onClick={() => {
onCheckboxClick();
}}
style={checkboxLabelStyle}
style={{ ...checkboxLabelStyle, marginLeft: 5, marginBottom: 0 }}
htmlFor={`setting_checkbox_${key}`}
>
{md.label()}
</label>
{descriptionComp}
</div>
{descriptionComp}
</div>
);
} else if (md.type === Setting.TYPE_STRING) {
const inputStyle = Object.assign({}, textInputBaseStyle, {
const inputStyle:any = Object.assign({}, textInputBaseStyle, {
width: '50%',
minWidth: '20em',
});
@@ -367,13 +413,13 @@ class ConfigScreenComponent extends React.Component {
if (md.subType === 'file_path_and_args') {
inputStyle.marginBottom = subLabel.marginBottom;
const splitCmd = cmdString => {
const splitCmd = (cmdString:string) => {
const path = pathUtils.extractExecutablePath(cmdString);
const args = cmdString.substr(path.length + 1);
return [pathUtils.unquotePath(path), args];
};
const joinCmd = cmdArray => {
const joinCmd = (cmdArray:string[]) => {
if (!cmdArray[0] && !cmdArray[1]) return '';
let cmdString = pathUtils.quotePath(cmdArray[0]);
if (!cmdString) cmdString = '""';
@@ -381,13 +427,13 @@ class ConfigScreenComponent extends React.Component {
return cmdString;
};
const onPathChange = event => {
const onPathChange = (event:any) => {
const cmd = splitCmd(this.state.settings[key]);
cmd[0] = event.target.value;
updateSettingValue(key, joinCmd(cmd));
};
const onArgsChange = event => {
const onArgsChange = (event:any) => {
const cmd = splitCmd(this.state.settings[key]);
cmd[1] = event.target.value;
updateSettingValue(key, joinCmd(cmd));
@@ -405,53 +451,51 @@ class ConfigScreenComponent extends React.Component {
return (
<div key={key} style={rowStyle}>
<div style={labelStyle}>
<label>{md.label()}</label>
</div>
<div style={{ display: 'flex' }}>
<div style={{ flex: 0, whiteSpace: 'nowrap' }}>
<div style={labelStyle}>
<label>{md.label()}</label>
</div>
</div>
<div style={{ flex: 0 }}>
<div style={subLabel}>Path:</div>
<div style={subLabel}>Arguments:</div>
</div>
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: inputStyle.marginBottom }}>
<div style={{ ...rowStyle, marginBottom: 5 }}>
<div style={subLabel}>Path:</div>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: inputStyle.marginBottom }}>
<input
type={inputType}
style={Object.assign({}, inputStyle, { marginBottom: 0, marginRight: 5 })}
onChange={(event:any) => {
onPathChange(event);
}}
value={cmd[0]}
/>
<Button
level={ButtonLevel.Secondary}
title={_('Browse...')}
onClick={browseButtonClick}
/>
</div>
</div>
<div style={{ ...rowStyle, marginBottom: 5 }}>
<div style={subLabel}>Arguments:</div>
<input
type={inputType}
style={Object.assign({}, inputStyle, { marginBottom: 0 })}
onChange={event => {
onPathChange(event);
style={inputStyle}
onChange={(event:any) => {
onArgsChange(event);
}}
value={cmd[0]}
value={cmd[1]}
/>
<button onClick={browseButtonClick} style={Object.assign({}, theme.buttonStyle, { marginLeft: 5 })}>
{_('Browse...')}
</button>
<div style={{ width: inputStyle.width }}>
{descriptionComp}
</div>
</div>
<input
type={inputType}
style={inputStyle}
onChange={event => {
onArgsChange(event);
}}
value={cmd[1]}
/>
</div>
</div>
<div style={{ display: 'flex' }}>
<div style={{ flex: 0, whiteSpace: 'nowrap' }}>
<div style={invisibleLabel}>
<label>{md.label()}</label>
</div>
</div>
<div style={{ flex: 1 }}>{descriptionComp}</div>
</div>
</div>
);
} else {
const onTextChange = event => {
const onTextChange = (event:any) => {
updateSettingValue(key, event.target.value);
};
@@ -464,23 +508,25 @@ class ConfigScreenComponent extends React.Component {
type={inputType}
style={inputStyle}
value={this.state.settings[key]}
onChange={event => {
onChange={(event:any) => {
onTextChange(event);
}}
/>
{descriptionComp}
<div style={{ width: inputStyle.width }}>
{descriptionComp}
</div>
</div>
);
}
} else if (md.type === Setting.TYPE_INT) {
const onNumChange = event => {
const onNumChange = (event:any) => {
updateSettingValue(key, event.target.value);
};
const label = [md.label()];
if (md.unitLabel) label.push(`(${md.unitLabel()})`);
const inputStyle = Object.assign({}, textInputBaseStyle);
const inputStyle:any = Object.assign({}, textInputBaseStyle);
return (
<div key={key} style={rowStyle}>
@@ -491,7 +537,7 @@ class ConfigScreenComponent extends React.Component {
type="number"
style={inputStyle}
value={this.state.settings[key]}
onChange={event => {
onChange={(event:any) => {
onNumChange(event);
}}
min={md.minimum}
@@ -502,20 +548,12 @@ class ConfigScreenComponent extends React.Component {
</div>
);
} else if (md.type === Setting.TYPE_BUTTON) {
const theme = themeStyle(this.props.theme);
const buttonStyle = Object.assign({}, theme.buttonStyle, {
display: 'inline-block',
marginRight: 10,
});
return (
<div key={key} style={rowStyle}>
<div style={labelStyle}>
<label>{md.label()}</label>
</div>
<button style={buttonStyle} onClick={md.onClick}>
{_('Edit')}
</button>
<Button level={ButtonLevel.Secondary} title={_('Edit')} onClick={md.onClick}/>
{descriptionComp}
</div>
);
@@ -544,46 +582,35 @@ class ConfigScreenComponent extends React.Component {
}
render() {
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const style = Object.assign(
{
backgroundColor: theme.backgroundColor,
},
const style = Object.assign({},
this.props.style,
{
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.backgroundColor3,
}
);
const settings = this.state.settings;
const containerStyle = Object.assign({}, theme.containerStyle, { padding: 10, paddingTop: 0, display: 'flex', flex: 1 });
const containerStyle = {
overflow: 'auto',
padding: theme.configScreenPadding,
paddingTop: 0,
display: 'flex',
flex: 1,
};
const hasChanges = this.hasChanges();
const buttonStyle = Object.assign({}, theme.buttonStyle, {
display: 'inline-block',
marginRight: 10,
});
const buttonStyleApprove = Object.assign({}, buttonStyle, {
opacity: hasChanges ? 1 : theme.disabledOpacity,
});
const settingComps = shared.settingsToComponents2(this, 'desktop', settings, this.state.selectedSectionName);
const buttonBarStyle = {
display: 'flex',
alignItems: 'center',
padding: 10,
borderTopWidth: 1,
borderTopStyle: 'solid',
borderTopColor: theme.dividerColor,
};
// screenComp is a custom config screen, such as the encryption config screen or keymap config screen.
// These screens handle their own loading/saving of settings and have bespoke rendering.
// When screenComp is null, it means we are viewing the regular settings.
const screenComp = this.state.screenName ? <div style={{ overflow: 'scroll', flex: 1 }}>{this.screenFromName(this.state.screenName)}</div> : null;
if (screenComp) containerStyle.display = 'none';
@@ -591,45 +618,35 @@ class ConfigScreenComponent extends React.Component {
const sections = shared.settingsSections({ device: 'desktop', settings });
return (
<div style={style}>
<ConfigMenuBar
<div style={{ display: 'flex', flexDirection: 'row' }}>
<SideBar
selection={this.state.selectedSectionName}
onSelectionChange={this.configMenuBar_selectionChange}
onSelectionChange={this.sideBar_selectionChange}
sections={sections}
theme={this.props.theme}
/>
{screenComp}
<div style={containerStyle}>{settingComps}</div>
<div style={buttonBarStyle}>
<button
onClick={() => {
this.onCancelClick();
}}
style={buttonStyle}
>
<i style={theme.buttonIconStyle} className={'fa fa-chevron-left'}></i>
{hasChanges && !screenComp ? _('Cancel') : _('Back')}
</button>
{ !screenComp && (
<div>
<button disabled={!hasChanges} onClick={() => { this.onSaveClick(); }} style={buttonStyleApprove}>{_('OK')}</button>
<button disabled={!hasChanges} onClick={() => { this.onApplyClick(); }} style={buttonStyleApprove}>{_('Apply')}</button>
</div>
)}
<div style={style}>
{screenComp}
<div style={containerStyle}>{settingComps}</div>
<ButtonBar
hasChanges={hasChanges}
backButtonTitle={hasChanges && !screenComp ? _('Cancel') : _('Back')}
onCancelClick={this.onCancelClick}
onSaveClick={screenComp ? null : this.onSaveClick}
onApplyClick={screenComp ? null : this.onApplyClick}
/>
</div>
</div>
);
}
}
const mapStateToProps = state => {
const mapStateToProps = (state:any) => {
return {
theme: state.settings.theme,
themeId: state.settings.theme,
settings: state.settings,
locale: state.settings.locale,
};
};
const ConfigScreen = connect(mapStateToProps)(ConfigScreenComponent);
export default connect(mapStateToProps)(ConfigScreenComponent);
module.exports = { ConfigScreen };

View File

@@ -0,0 +1,74 @@
import * as React from 'react';
const styled = require('styled-components').default;
const Setting = require('lib/models/Setting');
interface Props {
selection: string,
onSelectionChange: Function,
sections: any[],
}
export const StyledRoot = styled.div`
display: flex;
background-color: ${(props:any) => props.theme.backgroundColor2};
flex-direction: column;
`;
export const StyledListItem = styled.a`
box-sizing: border-box;
display: flex;
flex-direction: row;
padding: ${(props:any) => props.theme.mainPadding}px;
background: ${(props:any) => props.selected ? props.theme.selectedColor2 : 'none'};
transition: 0.1s;
text-decoration: none;
cursor: default;
opacity: ${(props:any) => props.selected ? 1 : 0.8};
&:hover {
background-color: ${(props:any) => props.theme.backgroundColorHover2};
}
`;
export const StyledListItemLabel = styled.span`
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.2)}px;
font-weight: 500;
color: ${(props:any) => props.theme.color2};
white-space: nowrap;
display: flex;
flex: 1;
align-items: center;
user-select: none;
`;
export const StyledListItemIcon = styled.i`
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.4)}px;
color: ${(props:any) => props.theme.color2};
margin-right: ${(props:any) => props.theme.mainPadding / 1.5}px;
`;
export default function SideBar(props:Props) {
const buttons:any[] = [];
function renderButton(section:any) {
const selected = props.selection === section.name;
return (
<StyledListItem key={section.name} selected={selected} onClick={() => { props.onSelectionChange({ section: section }); }}>
<StyledListItemIcon className={Setting.sectionNameToIcon(section.name)} />
<StyledListItemLabel>
{Setting.sectionNameToLabel(section.name)}
</StyledListItemLabel>
</StyledListItem>
);
}
for (const section of props.sections) {
buttons.push(renderButton(section));
}
return (
<StyledRoot>
{buttons}
</StyledRoot>
);
}

View File

@@ -3,7 +3,7 @@ const { _ } = require('lib/locale.js');
const { themeStyle } = require('lib/theme');
function DialogButtonRow(props) {
const theme = themeStyle(props.theme);
const theme = themeStyle(props.themeId);
const okButton_click = () => {
if (props.onClick) props.onClick({ buttonName: 'ok' });

View File

@@ -1,16 +1,24 @@
const React = require('react');
import * as React from 'react';
import ButtonBar from './ConfigScreen/ButtonBar';
const { connect } = require('react-redux');
const { bridge } = require('electron').remote.require('./bridge');
const { Header } = require('./Header/Header.min.js');
const { themeStyle } = require('lib/theme');
const { _ } = require('lib/locale.js');
const Shared = require('lib/components/shared/dropbox-login-shared');
class DropboxLoginScreenComponent extends React.Component {
constructor() {
super();
interface Props {
themeId: string,
}
this.shared_ = new Shared(this, msg => bridge().showInfoMessageBox(msg), msg => bridge().showErrorMessageBox(msg));
class DropboxLoginScreenComponent extends React.Component<any, any> {
shared_:any;
constructor(props:Props) {
super(props);
this.shared_ = new Shared(this, (msg:string) => bridge().showInfoMessageBox(msg), (msg:string) => bridge().showErrorMessageBox(msg));
}
UNSAFE_componentWillMount() {
@@ -19,19 +27,18 @@ class DropboxLoginScreenComponent extends React.Component {
render() {
const style = this.props.style;
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
const containerStyle = Object.assign({}, theme.containerStyle, {
padding: theme.margin,
height: style.height - theme.headerHeight - theme.margin * 2,
padding: theme.configScreenPadding,
height: style.height - theme.margin * 2,
flex: 1,
});
const inputStyle = Object.assign({}, theme.inputStyle, { width: 500 });
return (
<div>
<Header style={headerStyle} />
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={containerStyle}>
<p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p>
<p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p>
@@ -46,17 +53,18 @@ class DropboxLoginScreenComponent extends React.Component {
{_('Submit')}
</button>
</div>
<ButtonBar
onCancelClick={() => this.props.dispatch({ type: 'NAV_BACK' })}
/>
</div>
);
}
}
const mapStateToProps = state => {
const mapStateToProps = (state:any) => {
return {
theme: state.settings.theme,
themeId: state.settings.theme,
};
};
const DropboxLoginScreen = connect(mapStateToProps)(DropboxLoginScreenComponent);
module.exports = { DropboxLoginScreen };
export default connect(mapStateToProps)(DropboxLoginScreenComponent);

View File

@@ -35,7 +35,7 @@ class EncryptionConfigScreenComponent extends React.Component {
}
renderMasterKey(mk) {
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const passwordStyle = {
color: theme.color,
@@ -80,7 +80,7 @@ class EncryptionConfigScreenComponent extends React.Component {
const needUpgradeMasterKeys = EncryptionService.instance().masterKeysThatNeedUpgrading(this.props.masterKeys);
if (!needUpgradeMasterKeys.length) return null;
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const rows = [];
const comp = this;
@@ -114,7 +114,7 @@ class EncryptionConfigScreenComponent extends React.Component {
renderReencryptData() {
if (!shim.isElectron()) return null;
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const buttonLabel = _('Re-encrypt data');
const intro = this.props.shouldReencrypt ? _('The default encryption method has been changed to a more secure one and it is recommended that you apply it to your data.') : _('You may use the tool below to re-encrypt your data, for example if you know that some of your notes are encrypted with an obsolete encryption method.');
@@ -139,13 +139,13 @@ class EncryptionConfigScreenComponent extends React.Component {
}
render() {
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const masterKeys = this.props.masterKeys;
const containerPadding = 10;
const containerStyle = Object.assign({}, theme.containerStyle, {
padding: containerPadding,
padding: theme.configScreenPadding,
overflow: 'auto',
backgroundColor: theme.backgroundColor3,
});
const mkComps = [];
@@ -289,7 +289,7 @@ class EncryptionConfigScreenComponent extends React.Component {
const mapStateToProps = state => {
return {
theme: state.settings.theme,
themeId: state.settings.theme,
masterKeys: state.masterKeys,
passwords: state.settings['encryption.passwordCache'],
encryptionEnabled: state.settings['encryption.enabled'],

View File

@@ -1,329 +0,0 @@
const React = require('react');
const { connect } = require('react-redux');
const { themeStyle } = require('lib/theme');
const { _ } = require('lib/locale.js');
const { bridge } = require('electron').remote.require('./bridge');
const CommandService = require('lib/services/CommandService').default;
const Setting = require('lib/models/Setting.js');
const commands = [
require('./commands/focusSearch'),
];
class HeaderComponent extends React.Component {
constructor() {
super();
this.state = {
searchQuery: '',
showSearchUsageLink: false,
showButtonLabels: true,
};
for (const command of commands) {
CommandService.instance().registerRuntime(command.declaration.name, command.runtime(this));
}
this.scheduleSearchChangeEventIid_ = null;
this.searchOnQuery_ = null;
this.searchElement_ = null;
const triggerOnQuery = query => {
clearTimeout(this.scheduleSearchChangeEventIid_);
if (this.searchOnQuery_) this.searchOnQuery_(query, Setting.value('db.fuzzySearchEnabled'));
this.scheduleSearchChangeEventIid_ = null;
};
this.search_onChange = event => {
this.setState({ searchQuery: event.target.value });
if (this.scheduleSearchChangeEventIid_) clearTimeout(this.scheduleSearchChangeEventIid_);
this.scheduleSearchChangeEventIid_ = setTimeout(() => {
triggerOnQuery(this.state.searchQuery);
}, 500);
};
this.search_onClear = () => {
this.resetSearch();
if (this.searchElement_) this.searchElement_.focus();
};
this.search_onFocus = () => {
if (this.hideSearchUsageLinkIID_) {
clearTimeout(this.hideSearchUsageLinkIID_);
this.hideSearchUsageLinkIID_ = null;
}
this.setState({ showSearchUsageLink: true });
};
this.search_onBlur = () => {
if (this.hideSearchUsageLinkIID_) return;
this.hideSearchUsageLinkIID_ = setTimeout(() => {
this.setState({ showSearchUsageLink: false });
}, 5000);
};
this.search_keyDown = event => {
if (event.keyCode === 27) {
// ESCAPE
this.resetSearch();
}
};
this.resetSearch = () => {
this.setState({ searchQuery: '' });
triggerOnQuery('');
};
this.searchUsageLink_click = () => {
bridge().openExternal('https://joplinapp.org/#searching');
};
}
componentDidUpdate(prevProps) {
if (prevProps.notesParentType !== this.props.notesParentType && this.props.notesParentType !== 'Search' && this.state.searchQuery) {
this.resetSearch();
}
if (this.props.zoomFactor !== prevProps.zoomFactor || this.props.size !== prevProps.size) {
this.determineButtonLabelState();
}
}
componentDidMount() {
this.determineButtonLabelState();
}
componentWillUnmount() {
if (this.hideSearchUsageLinkIID_) {
clearTimeout(this.hideSearchUsageLinkIID_);
this.hideSearchUsageLinkIID_ = null;
}
for (const command of commands) {
CommandService.instance().unregisterRuntime(command.declaration.name);
}
}
determineButtonLabelState() {
const mediaQuery = window.matchMedia(`(max-width: ${780 * this.props.zoomFactor}px)`);
const showButtonLabels = !mediaQuery.matches;
if (this.state.showButtonLabels !== showButtonLabels) {
this.setState({
showButtonLabels: !mediaQuery.matches,
});
}
}
back_click() {
this.props.dispatch({ type: 'NAV_BACK' });
}
makeButton(key, style, options) {
// TODO: "tab" type is not finished
if (options.type === 'tab') {
const buttons = [];
for (let i = 0; i < options.items.length; i++) {
const item = options.items[i];
buttons.push(this.makeButton(key + item.title, style, Object.assign({}, options, {
title: item.title,
type: 'button',
})));
}
return <span style={{ display: 'flex', flexDirection: 'row' }}>{buttons}</span>;
}
const theme = themeStyle(this.props.theme);
let icon = null;
if (options.iconName) {
const iconStyle = {
fontSize: Math.round(style.fontSize * 1.1),
color: theme.iconColor,
};
if (options.title) iconStyle.marginRight = 5;
if ('undefined' != typeof options.iconRotation) {
iconStyle.transition = 'transform 0.15s ease-in-out';
iconStyle.transform = `rotate(${options.iconRotation}deg)`;
}
icon = <i style={iconStyle} className={`fas ${options.iconName}`}></i>;
}
const isEnabled = !('enabled' in options) || options.enabled;
const classes = ['button'];
if (!isEnabled) classes.push('disabled');
const finalStyle = Object.assign({}, style, {
opacity: isEnabled ? 1 : 0.4,
});
const title = options.title ? options.title : '';
if (options.type === 'checkbox' && options.checked) {
finalStyle.backgroundColor = theme.selectedColor;
finalStyle.borderWidth = 1;
finalStyle.borderTopColor = theme.selectedDividerColor;
finalStyle.borderLeftColor = theme.selectedDividerColor;
finalStyle.borderTopStyle = 'solid';
finalStyle.borderLeftStyle = 'solid';
finalStyle.paddingLeft++;
finalStyle.paddingTop++;
finalStyle.paddingBottom--;
finalStyle.paddingRight--;
finalStyle.boxSizing = 'border-box';
}
return (
<a
className={classes.join(' ')}
style={finalStyle}
key={key}
href="#"
title={title}
onClick={() => {
if (isEnabled) options.onClick();
}}
>
{icon}
<span className="title" style={{
display: this.state.showButtonLabels ? 'inline-block' : 'none',
}}>{title}</span>
</a>
);
}
makeSearch(key, style, options, state) {
const theme = themeStyle(this.props.theme);
const inputStyle = {
display: 'flex',
flex: 1,
marginLeft: 10,
paddingLeft: 6,
paddingRight: 6,
paddingTop: 1, // vertical alignment with buttons
paddingBottom: 0, // vertical alignment with buttons
height: style.fontSize * 2,
maxWidth: 300,
color: style.color,
fontSize: style.fontSize,
fontFamily: style.fontFamily,
backgroundColor: style.searchColor,
border: '1px solid',
borderColor: style.dividerColor,
};
const searchButton = {
paddingLeft: 4,
paddingRight: 4,
paddingTop: 2,
paddingBottom: 2,
textDecoration: 'none',
};
const iconStyle = {
display: 'flex',
fontSize: Math.round(style.fontSize) * 1.2,
color: style.color,
};
const containerStyle = {
display: 'flex',
flexDirection: 'row',
flexGrow: 1,
alignItems: 'center',
};
const iconName = state.searchQuery ? 'fa-times' : 'fa-search';
const icon = <i style={iconStyle} className={`fas ${iconName}`}></i>;
if (options.onQuery) this.searchOnQuery_ = options.onQuery;
const usageLink = !this.state.showSearchUsageLink ? null : (
<a onClick={this.searchUsageLink_click} style={theme.urlStyle} href="#">
{_('Usage')}
</a>
);
return (
<div key={key} style={containerStyle}>
<input type="text" style={inputStyle} placeholder={options.title} value={state.searchQuery} onChange={this.search_onChange} ref={elem => (this.searchElement_ = elem)} onFocus={this.search_onFocus} onBlur={this.search_onBlur} onKeyDown={this.search_keyDown} />
<a href="#" style={searchButton} onClick={this.search_onClear}>
{icon}
</a>
{usageLink}
</div>
);
}
render() {
const style = Object.assign({}, this.props.style);
const theme = themeStyle(this.props.theme);
const showBackButton = this.props.showBackButton === undefined || this.props.showBackButton === true;
style.height = theme.headerHeight;
style.display = 'flex';
style.flexDirection = 'row';
style.borderBottom = `1px solid ${theme.dividerColor}`;
style.boxSizing = 'border-box';
const items = [];
const itemStyle = {
height: theme.headerHeight,
display: 'flex',
alignItems: 'center',
paddingTop: 1,
paddingBottom: 1,
paddingLeft: theme.headerButtonHPadding,
paddingRight: theme.headerButtonHPadding,
color: theme.color,
searchColor: theme.backgroundColor,
dividerColor: theme.dividerColor,
textDecoration: 'none',
fontFamily: theme.fontFamily,
fontSize: theme.fontSize,
boxSizing: 'border-box',
cursor: 'default',
whiteSpace: 'nowrap',
userSelect: 'none',
};
if (showBackButton) {
items.push(this.makeButton('back', itemStyle, { title: _('Back'), onClick: () => this.back_click(), iconName: 'fa-chevron-left ' }));
}
if (this.props.items) {
for (let i = 0; i < this.props.items.length; i++) {
const item = this.props.items[i];
if (item.type === 'search') {
items.push(this.makeSearch(`item_${i}_search`, itemStyle, item, this.state));
} else {
items.push(this.makeButton(`item_${i}_${item.title}`, itemStyle, item));
}
}
}
return (
<div className="header" style={style}>
{items}
</div>
);
}
}
const mapStateToProps = state => {
return {
theme: state.settings.theme,
notesParentType: state.notesParentType,
size: state.windowContentSize,
zoomFactor: state.settings.windowContentZoomFactor / 100,
};
};
const Header = connect(mapStateToProps)(HeaderComponent);
module.exports = { Header };

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