1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-09-02 20:46:21 +02:00

Compare commits

...

64 Commits

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

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

View File

@@ -69,8 +69,12 @@ ElectronClient/commands/focusElement.js
ElectronClient/commands/startExternalEditing.js
ElectronClient/commands/stopExternalEditing.js
ElectronClient/global.d.js
ElectronClient/gui/Button/Button.js
ElectronClient/gui/ConfigScreen/ButtonBar.js
ElectronClient/gui/ConfigScreen/ConfigScreen.js
ElectronClient/gui/ConfigScreen/SideBar.js
ElectronClient/gui/DropboxLoginScreen.js
ElectronClient/gui/ErrorBoundary.js
ElectronClient/gui/Header/commands/focusSearch.js
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
ElectronClient/gui/KeymapConfig/styles/index.js
@@ -81,8 +85,8 @@ ElectronClient/gui/MainScreen/commands/editAlarm.js
ElectronClient/gui/MainScreen/commands/exportPdf.js
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
ElectronClient/gui/MainScreen/commands/moveToFolder.js
ElectronClient/gui/MainScreen/commands/newFolder.js
ElectronClient/gui/MainScreen/commands/newNote.js
ElectronClient/gui/MainScreen/commands/newNotebook.js
ElectronClient/gui/MainScreen/commands/newTodo.js
ElectronClient/gui/MainScreen/commands/print.js
ElectronClient/gui/MainScreen/commands/renameFolder.js
@@ -94,9 +98,11 @@ ElectronClient/gui/MainScreen/commands/showModalMessage.js
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
ElectronClient/gui/MainScreen/commands/toggleEditors.js
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
@@ -116,6 +122,7 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.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 +132,40 @@ 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/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/ToolbarButton/styles/index.js
ElectronClient/gui/ToolbarButton/ToolbarButton.js
ReactNativeClient/lib/AsyncActionQueue.js
ReactNativeClient/lib/checkPermissions.js
ReactNativeClient/lib/commands/historyBackward.js
@@ -176,6 +204,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

46
.gitignore vendored
View File

@@ -50,8 +50,8 @@ joplin-webclipper-source.zip
Tools/commit_hook.txt
.vscode/*
*.map
ReactNativeClient/lib/sql-extensions/
!ReactNativeClient/lib/sql-extensions/spellfix.dll
ReactNativeClient/lib/sql-extensions/spellfix.so
ReactNativeClient/lib/sql-extensions/spellfix.dylib
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
CliClient/app/LinkSelector.js
@@ -62,8 +62,12 @@ ElectronClient/commands/focusElement.js
ElectronClient/commands/startExternalEditing.js
ElectronClient/commands/stopExternalEditing.js
ElectronClient/global.d.js
ElectronClient/gui/Button/Button.js
ElectronClient/gui/ConfigScreen/ButtonBar.js
ElectronClient/gui/ConfigScreen/ConfigScreen.js
ElectronClient/gui/ConfigScreen/SideBar.js
ElectronClient/gui/DropboxLoginScreen.js
ElectronClient/gui/ErrorBoundary.js
ElectronClient/gui/Header/commands/focusSearch.js
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
ElectronClient/gui/KeymapConfig/styles/index.js
@@ -74,8 +78,8 @@ ElectronClient/gui/MainScreen/commands/editAlarm.js
ElectronClient/gui/MainScreen/commands/exportPdf.js
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
ElectronClient/gui/MainScreen/commands/moveToFolder.js
ElectronClient/gui/MainScreen/commands/newFolder.js
ElectronClient/gui/MainScreen/commands/newNote.js
ElectronClient/gui/MainScreen/commands/newNotebook.js
ElectronClient/gui/MainScreen/commands/newTodo.js
ElectronClient/gui/MainScreen/commands/print.js
ElectronClient/gui/MainScreen/commands/renameFolder.js
@@ -87,9 +91,11 @@ ElectronClient/gui/MainScreen/commands/showModalMessage.js
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
ElectronClient/gui/MainScreen/commands/toggleEditors.js
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
@@ -109,6 +115,7 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.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 +125,40 @@ 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/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/ToolbarButton/styles/index.js
ElectronClient/gui/ToolbarButton/ToolbarButton.js
ReactNativeClient/lib/AsyncActionQueue.js
ReactNativeClient/lib/checkPermissions.js
ReactNativeClient/lib/commands/historyBackward.js
@@ -169,6 +197,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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,13 +37,13 @@ const resourceEditWatcherReducer = require('lib/services/ResourceEditWatcher/red
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 +58,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 +99,7 @@ const appDefaultState = Object.assign({}, defaultState, {
watchedNoteFiles: [],
lastEditorScrollPercents: {},
devToolsVisible: false,
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
});
class Application extends BaseApplication {
@@ -279,6 +281,16 @@ 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;
}
} catch (error) {
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
@@ -286,10 +298,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 +388,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 +532,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 +663,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 +712,7 @@ class Application extends BaseApplication {
submenu: [
newNoteItem,
newTodoItem,
newNotebookItem, {
newFolderItem, {
label: _('Close Window'),
platforms: ['darwin'],
accelerator: shim.isMac() && keymapService.getAccelerator('closeWindow'),
@@ -738,7 +751,6 @@ class Application extends BaseApplication {
const separator = () => {
return {
type: 'separator',
screens: ['Main'],
};
};
@@ -986,6 +998,8 @@ class Application extends BaseApplication {
Menu.setApplicationMenu(menu);
this.lastMenuScreen_ = screen;
if (updateStates) await this.updateMenuItemStates();
}
async updateMenuItemStates(state = null) {
@@ -1128,7 +1142,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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ class HelpButtonComponent extends React.Component {
}
render() {
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const style = Object.assign({}, this.props.style, { color: theme.color, textDecoration: 'none' });
const helpIconStyle = { flex: 0, width: 16, height: 16, marginLeft: 10 };
const extraProps = {};
@@ -29,7 +29,7 @@ class HelpButtonComponent extends React.Component {
const mapStateToProps = state => {
return {
theme: state.settings.theme,
themeId: state.settings.theme,
};
};

View File

@@ -4,7 +4,7 @@ const { themeStyle } = require('lib/theme');
class IconButton extends React.Component {
render() {
const style = this.props.style;
const theme = themeStyle(this.props.theme);
const theme = themeStyle(this.props.themeId);
const iconStyle = {
color: theme.color,
fontSize: theme.fontSize * 1.4,

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