You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-24 20:19:10 +02:00
Compare commits
103 Commits
android-v1
...
android-v1
Author | SHA1 | Date | |
---|---|---|---|
|
3bd0656eab | ||
|
e9af71dd76 | ||
|
73b33e8e32 | ||
|
c2c7efee91 | ||
|
0836fca822 | ||
|
566df5039c | ||
|
559655bf33 | ||
|
0eab23fbcf | ||
|
f334f4f487 | ||
|
00057da17d | ||
|
0a05464013 | ||
|
9ebb574059 | ||
|
d29c3c2466 | ||
|
a71f1c19ec | ||
|
485921d879 | ||
|
15de7572c0 | ||
|
09f41dd50e | ||
|
7b8ee467a0 | ||
|
99a496d684 | ||
|
f43ee123d8 | ||
|
f42fb1b871 | ||
|
cf2442c5b2 | ||
|
e0e4735b03 | ||
|
8bd58c9608 | ||
|
215a725ded | ||
|
12c0a05af0 | ||
|
a7fa119041 | ||
|
7fb52b8b0e | ||
|
3e86ae4a82 | ||
|
947d81d96d | ||
|
6ca640d2ed | ||
|
6aca233b21 | ||
|
2200be697e | ||
|
25ab3c323b | ||
|
5bf30a9586 | ||
|
b6779a8074 | ||
|
59599d318c | ||
|
538600fd6c | ||
|
63264ba471 | ||
|
95e7f3df7d | ||
|
366fd2a333 | ||
|
5be99a4a16 | ||
|
d86f6a1fbd | ||
|
7d68208cb4 | ||
|
e9de9d9128 | ||
|
1af16d9f0b | ||
|
8e11eababa | ||
|
4ec9faadd5 | ||
|
5cf462c885 | ||
|
f7ef0a2b1e | ||
|
870f55a6c5 | ||
|
7f7e38b434 | ||
|
460a07b1a3 | ||
|
48c9b86d2b | ||
|
7202066c1f | ||
|
5226f0019b | ||
|
26ac745419 | ||
|
b3f2bbee5b | ||
|
56c6cfc785 | ||
|
1db4932573 | ||
|
a2873ebbc5 | ||
|
f652011d59 | ||
|
27c572b2f5 | ||
|
7a4c97618d | ||
|
3ac4fbeee5 | ||
|
9e05fa553c | ||
|
d4f0d2423d | ||
|
abdd7e3256 | ||
|
f3ea476f27 | ||
|
aa22af443c | ||
|
ce3bd2a47d | ||
|
a9b26246e6 | ||
|
cc1e941dd9 | ||
|
9610b7e6bd | ||
|
ad85a12535 | ||
|
b825346829 | ||
|
bd4cbaf93d | ||
|
9af2a19bdf | ||
|
d3fa906a9a | ||
|
22679641ee | ||
|
0ca7457000 | ||
|
c84e49c71c | ||
|
07ab0e986d | ||
|
17957f5da4 | ||
|
a7b5d43e69 | ||
|
38eda3f151 | ||
|
056285deda | ||
|
bdedf69439 | ||
|
c9451d8675 | ||
|
c38834b04c | ||
|
851eee1500 | ||
|
40e24102ce | ||
|
7614a795e9 | ||
|
1273a1dc5f | ||
|
10909fe4fc | ||
|
9b3d3026bf | ||
|
96076c84f4 | ||
|
2c553db45a | ||
|
7d7005596f | ||
|
998dd52adc | ||
|
2a1c6d6475 | ||
|
1ba0644142 | ||
|
88ac57d7f3 |
@@ -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
51
.gitignore
vendored
@@ -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 |
3
BUILD.md
3
BUILD.md
@@ -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
@@ -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
20
CliClient/package-lock.json
generated
20
CliClient/package-lock.json
generated
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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' },
|
||||
|
@@ -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);
|
||||
// }));
|
||||
|
||||
});
|
||||
// });
|
||||
|
@@ -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');
|
||||
|
@@ -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'",
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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,
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@@ -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 };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
195
ElectronClient/gui/Button/Button.tsx
Normal file
195
ElectronClient/gui/Button/Button.tsx
Normal 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>
|
||||
);
|
||||
}
|
@@ -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'],
|
||||
|
@@ -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;
|
52
ElectronClient/gui/ConfigScreen/ButtonBar.tsx
Normal file
52
ElectronClient/gui/ConfigScreen/ButtonBar.tsx
Normal 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>
|
||||
);
|
||||
}
|
@@ -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 {
|
||||
|
||||
{showLogButton}
|
||||
|
||||
<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}/>
|
||||
|
||||
<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 };
|
74
ElectronClient/gui/ConfigScreen/SideBar.tsx
Normal file
74
ElectronClient/gui/ConfigScreen/SideBar.tsx
Normal 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>
|
||||
);
|
||||
}
|
@@ -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' });
|
||||
|
@@ -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);
|
@@ -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'],
|
||||
|
@@ -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
Reference in New Issue
Block a user