1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-06 23:56:13 +02:00
This commit is contained in:
Laurent Cozic
2020-11-01 00:11:09 +00:00
20 changed files with 250 additions and 207 deletions

View File

@ -126,6 +126,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MenuBar.js ElectronClient/gui/MenuBar.js
ElectronClient/gui/menuCommandNames.js
ElectronClient/gui/MultiNoteActions.js ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js

1
.gitignore vendored
View File

@ -120,6 +120,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MenuBar.js ElectronClient/gui/MenuBar.js
ElectronClient/gui/menuCommandNames.js
ElectronClient/gui/MultiNoteActions.js ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js

View File

@ -69,6 +69,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MenuBar.js ElectronClient/gui/MenuBar.js
ElectronClient/gui/menuCommandNames.js
ElectronClient/gui/MultiNoteActions.js ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js

View File

@ -45,10 +45,12 @@ class Command extends BaseCommand {
const startDecryption = async () => { const startDecryption = async () => {
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.')); this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
while (true) { while (true) {
try { try {
const result = await DecryptionWorker.instance().start(); const result = await DecryptionWorker.instance().start();
if (result.error) throw result.error;
const line = []; const line = [];
line.push(_('Decrypted items: %d', result.decryptedItemCount)); line.push(_('Decrypted items: %d', result.decryptedItemCount));
if (result.skippedItemCount) line.push(_('Skipped items: %d (use --retry-failed-items to retry decrypting them)', result.skippedItemCount)); if (result.skippedItemCount) line.push(_('Skipped items: %d (use --retry-failed-items to retry decrypting them)', result.skippedItemCount));

View File

@ -13,8 +13,10 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.3.1\n" "X-Generator: Poedit 2.4.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
#: CliClient/app/command-cp.js:13 #: CliClient/app/command-cp.js:13
msgid "" msgid ""
@ -315,8 +317,9 @@ msgid "Sync to provided target (defaults to sync.target config value)"
msgstr "Synchroniseren naar opgegeven doel (standaard is dit sync.target)" msgstr "Synchroniseren naar opgegeven doel (standaard is dit sync.target)"
#: CliClient/app/command-sync.js:35 #: CliClient/app/command-sync.js:35
#, fuzzy
msgid "Upgrade the sync target to the latest version." msgid "Upgrade the sync target to the latest version."
msgstr "" msgstr "Sync target updaten naar de nieuwste versie."
#: CliClient/app/command-sync.js:81 CliClient/app/command-sync.js:95 #: CliClient/app/command-sync.js:81 CliClient/app/command-sync.js:95
#: ElectronClient/gui/OneDriveLoginScreen.min.js:40 #: ElectronClient/gui/OneDriveLoginScreen.min.js:40
@ -818,6 +821,8 @@ msgstr "Annuleren"
msgid "" msgid ""
"The app is now going to close. Please relaunch it to complete the process." "The app is now going to close. Please relaunch it to complete the process."
msgstr "" msgstr ""
"De applicatie zal nu afsluiten. Gelieve het weer op te starten om het proces "
"te voltooien."
#: ElectronClient/plugins/GotoAnything.min.js:459 #: ElectronClient/plugins/GotoAnything.min.js:459
msgid "" msgid ""
@ -835,15 +840,14 @@ msgid "Goto Anything..."
msgstr "Ga naar Alles..." msgstr "Ga naar Alles..."
#: ElectronClient/plugins/GotoAnything.js:431 #: ElectronClient/plugins/GotoAnything.js:431
#, fuzzy
msgid "" msgid ""
"Type a note title or part of its content to jump to it. Or type # followed " "Type a note title or part of its content to jump to it. Or type # followed "
"by a tag name, or @ followed by a notebook name. Or type : to search for " "by a tag name, or @ followed by a notebook name. Or type : to search for "
"commands." "commands."
msgstr "" msgstr ""
"Typ de titel van een notitie of een deel van de inhoud om er naartoe te " "Typ de titel van een notitie of een deel van de inhoud om er naartoe te "
"springen. Of typ # gevolgd door de naam van een label, of @ gevolgd door de " "springen. Typ # gevolgd door de naam van een label, @ gevolgd door de naam "
"naam van een notitieboek." "van een notitieboek, of : om te zoeken op commando's."
#: ElectronClient/plugins/GotoAnything.js:463 #: ElectronClient/plugins/GotoAnything.js:463
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:28 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:28
@ -884,9 +888,8 @@ msgid "New version: %s"
msgstr "Nieuwe versie: %s" msgstr "Nieuwe versie: %s"
#: ElectronClient/checkForUpdates.js:154 #: ElectronClient/checkForUpdates.js:154
#, fuzzy
msgid "Download" msgid "Download"
msgstr "Gedownload" msgstr "Downloaden"
#: ElectronClient/checkForUpdates.js:154 #: ElectronClient/checkForUpdates.js:154
msgid "Full Release Notes" msgid "Full Release Notes"
@ -1235,13 +1238,15 @@ msgstr "Bezig met creëren van nieuw(e) %s..."
#: ElectronClient/gui/NoteEditor/NoteEditor.js:379 #: ElectronClient/gui/NoteEditor/NoteEditor.js:379
msgid "The following attachments are being watched for changes:" msgid "The following attachments are being watched for changes:"
msgstr "" msgstr "De volgende bijlagen worden gecontroleerd op wijzigingen:"
#: ElectronClient/gui/NoteEditor/NoteEditor.js:382 #: ElectronClient/gui/NoteEditor/NoteEditor.js:382
msgid "" msgid ""
"The attachments will no longer be watched when you switch to a different " "The attachments will no longer be watched when you switch to a different "
"note." "note."
msgstr "" msgstr ""
"De bijlagen worden niet meer gecontroleerd wanneer je naar een andere "
"notitie schakelt."
#: ElectronClient/gui/NoteEditor/NoteEditor.js:387 #: ElectronClient/gui/NoteEditor/NoteEditor.js:387
#: ElectronClient/gui/NoteText.min.js:1656 #: ElectronClient/gui/NoteText.min.js:1656
@ -1358,9 +1363,9 @@ msgstr "Eigenschappen van notitie"
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:63 #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:63
#: ReactNativeClient/lib/services/KeymapService.js:144 #: ReactNativeClient/lib/services/KeymapService.js:144
#, fuzzy, javascript-format #, javascript-format
msgid "Error: %s" msgid "Error: %s"
msgstr "Fout" msgstr "Fout: %s"
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:121 #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:121
#: ElectronClient/gui/Root.min.js:91 ElectronClient/gui/MenuBar.js:391 #: ElectronClient/gui/Root.min.js:91 ElectronClient/gui/MenuBar.js:391
@ -1370,12 +1375,11 @@ msgstr "Importeren"
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:126 #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:126
msgid "Command" msgid "Command"
msgstr "" msgstr "Commando"
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:127 #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:127
#, fuzzy
msgid "Keyboard Shortcut" msgid "Keyboard Shortcut"
msgstr "Toetsenbordmodus" msgstr "Sneltoets"
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:14 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:14
#: ElectronClient/gui/MenuBar.js:178 ElectronClient/app.js:347 #: ElectronClient/gui/MenuBar.js:178 ElectronClient/app.js:347
@ -1398,9 +1402,8 @@ msgid "Website and documentation"
msgstr "Website en documentatie" msgstr "Website en documentatie"
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:24 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:24
#, fuzzy
msgid "Hide Joplin" msgid "Hide Joplin"
msgstr "Over Joplin" msgstr "Joplin verbergen"
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:26 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:26
#: ElectronClient/gui/MenuBar.js:428 #: ElectronClient/gui/MenuBar.js:428
@ -1408,9 +1411,8 @@ msgid "Close Window"
msgstr "Venster afsluiten" msgstr "Venster afsluiten"
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30
#, fuzzy
msgid "Preferences" msgid "Preferences"
msgstr "Voorkeuren..." msgstr "Voorkeuren"
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30
#: ElectronClient/gui/Root.min.js:92 ElectronClient/gui/MenuBar.js:304 #: ElectronClient/gui/Root.min.js:92 ElectronClient/gui/MenuBar.js:304
@ -1420,13 +1422,15 @@ msgstr "Opties"
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49 #: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49
msgid "Press the shortcut" msgid "Press the shortcut"
msgstr "" msgstr "Druk op de sneltoetsen"
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49 #: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49
msgid "" msgid ""
"Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the " "Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the "
"shortcut." "shortcut."
msgstr "" msgstr ""
"Druk op de sneltoetsen en dan op ENTER. Of, druk op backspace om de "
"sneltoets te wissen."
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:50 #: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:50
#: ElectronClient/gui/EncryptionConfigScreen.min.js:96 #: ElectronClient/gui/EncryptionConfigScreen.min.js:96
@ -1436,17 +1440,20 @@ msgstr "Opslaan"
#: ElectronClient/gui/MainScreen/MainScreen.js:418 #: ElectronClient/gui/MainScreen/MainScreen.js:418
#: ElectronClient/gui/MainScreen/MainScreen.min.js:301 #: ElectronClient/gui/MainScreen/MainScreen.min.js:301
#, fuzzy
msgid "" msgid ""
"The sync target needs to be upgraded before Joplin can sync. The operation " "The sync target needs to be upgraded before Joplin can sync. The operation "
"may take a few minutes to complete and the app needs to be restarted. To " "may take a few minutes to complete and the app needs to be restarted. To "
"proceed please click on the link." "proceed please click on the link."
msgstr "" msgstr ""
"De sync target moet worden geüpdatet voordat Joplin kan synchroniseren. Het "
"kan een paar minuten duren en de app moet opnieuw opgestart worden. Om door "
"te gaan, klik op de link."
#: ElectronClient/gui/MainScreen/MainScreen.js:420 #: ElectronClient/gui/MainScreen/MainScreen.js:420
#: ElectronClient/gui/MainScreen/MainScreen.min.js:306 #: ElectronClient/gui/MainScreen/MainScreen.min.js:306
#, fuzzy
msgid "Restart and upgrade" msgid "Restart and upgrade"
msgstr "Hoofdsleutels die moeten worden bijgewerkt" msgstr "Opnieuw opstarten en bijwerken"
#: ElectronClient/gui/MainScreen/MainScreen.js:424 #: ElectronClient/gui/MainScreen/MainScreen.js:424
#: ElectronClient/gui/MainScreen/MainScreen.min.js:313 #: ElectronClient/gui/MainScreen/MainScreen.min.js:313
@ -2268,18 +2275,16 @@ msgstr[0] "Kopieer Deelbare Link"
msgstr[1] "Kopieer Deelbare Links" msgstr[1] "Kopieer Deelbare Links"
#: ElectronClient/commands/toggleExternalEditing.js:18 #: ElectronClient/commands/toggleExternalEditing.js:18
#, fuzzy
msgid "Toggle external editing" msgid "Toggle external editing"
msgstr "Klik om extern bewerken te stoppen" msgstr "Extern bewerken in- of uitschakelen"
#: ElectronClient/commands/toggleExternalEditing.js:37 #: ElectronClient/commands/toggleExternalEditing.js:37
msgid "Stop" msgid "Stop"
msgstr "" msgstr "Stop"
#: ElectronClient/commands/copyDevCommand.js:18 #: ElectronClient/commands/copyDevCommand.js:18
#, fuzzy
msgid "Copy dev mode command to clipboard" msgid "Copy dev mode command to clipboard"
msgstr "Pad kopiëren naar klembord" msgstr "Ontwikkelaarsmodus commando kopiëren naar klembord"
#: ElectronClient/commands/stopExternalEditing.js:18 #: ElectronClient/commands/stopExternalEditing.js:18
msgid "Stop external editing" msgid "Stop external editing"
@ -2291,9 +2296,8 @@ msgid "Error opening note in editor: %s"
msgstr "Fout bij openen van notitie in editor: %s" msgstr "Fout bij openen van notitie in editor: %s"
#: ElectronClient/commands/openProfileDirectory.js:18 #: ElectronClient/commands/openProfileDirectory.js:18
#, fuzzy
msgid "Open profile directory" msgid "Open profile directory"
msgstr "Open sjabloon map" msgstr "Open profiel map"
#: ElectronClient/app.js:345 #: ElectronClient/app.js:345
#, javascript-format #, javascript-format
@ -2339,7 +2343,7 @@ msgstr ""
#: ReactNativeClient/lib/SyncTargetAmazonS3.js:28 #: ReactNativeClient/lib/SyncTargetAmazonS3.js:28
msgid "AWS S3" msgid "AWS S3"
msgstr "" msgstr "AWS S3"
#: ReactNativeClient/lib/SyncTargetDropbox.js:25 #: ReactNativeClient/lib/SyncTargetDropbox.js:25
msgid "Dropbox" msgid "Dropbox"
@ -2483,19 +2487,19 @@ msgstr "WebDAV-wachtwoord"
#: ReactNativeClient/lib/models/Setting.js:215 #: ReactNativeClient/lib/models/Setting.js:215
msgid "AWS S3 bucket" msgid "AWS S3 bucket"
msgstr "" msgstr "AWS S3 bucket"
#: ReactNativeClient/lib/models/Setting.js:226 #: ReactNativeClient/lib/models/Setting.js:226
msgid "AWS S3 URL" msgid "AWS S3 URL"
msgstr "" msgstr "AWS S3 URL"
#: ReactNativeClient/lib/models/Setting.js:237 #: ReactNativeClient/lib/models/Setting.js:237
msgid "AWS key" msgid "AWS key"
msgstr "" msgstr "AWS key"
#: ReactNativeClient/lib/models/Setting.js:247 #: ReactNativeClient/lib/models/Setting.js:247
msgid "AWS secret" msgid "AWS secret"
msgstr "" msgstr "AWS secret"
#: ReactNativeClient/lib/models/Setting.js:259 #: ReactNativeClient/lib/models/Setting.js:259
msgid "Attachment download behaviour" msgid "Attachment download behaviour"
@ -2955,9 +2959,8 @@ msgid "Web Clipper"
msgstr "Webclipper" msgstr "Webclipper"
#: ReactNativeClient/lib/models/Setting.js:1310 #: ReactNativeClient/lib/models/Setting.js:1310
#, fuzzy
msgid "Keyboard Shortcuts" msgid "Keyboard Shortcuts"
msgstr "Toetsenbordmodus" msgstr "Sneltoetsen"
#: ReactNativeClient/lib/models/Setting.js:1317 #: ReactNativeClient/lib/models/Setting.js:1317
msgid "" msgid ""
@ -3074,9 +3077,8 @@ msgid "Save alarm"
msgstr "Alarm opslaan" msgstr "Alarm opslaan"
#: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28 #: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28
#, fuzzy
msgid "Open" msgid "Open"
msgstr "Openen..." msgstr "Openen"
#: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:29 #: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:29
#: ReactNativeClient/lib/components/screens/Note.js:794 #: ReactNativeClient/lib/components/screens/Note.js:794
@ -3114,8 +3116,9 @@ msgstr ""
"Sommige items kunnen niet worden gesynchroniseerd. Klik voor meer informatie." "Sommige items kunnen niet worden gesynchroniseerd. Klik voor meer informatie."
#: ReactNativeClient/lib/components/screen-header.js:453 #: ReactNativeClient/lib/components/screen-header.js:453
#, fuzzy
msgid "The sync target needs to be upgraded. Press this banner to proceed." msgid "The sync target needs to be upgraded. Press this banner to proceed."
msgstr "" msgstr "De sync target moet worden geüpdatet. Druk hier om door te gaan."
#: ReactNativeClient/lib/components/side-menu-content.js:126 #: ReactNativeClient/lib/components/side-menu-content.js:126
#, javascript-format #, javascript-format
@ -3239,8 +3242,9 @@ msgid "Refresh"
msgstr "Verversen" msgstr "Verversen"
#: ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js:42 #: ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js:42
#, fuzzy
msgid "Sync Target Upgrade" msgid "Sync Target Upgrade"
msgstr "" msgstr "Sync target update"
#: ReactNativeClient/lib/components/screens/NoteTagsDialog.js:163 #: ReactNativeClient/lib/components/screens/NoteTagsDialog.js:163
msgid "New tags:" msgid "New tags:"
@ -3484,11 +3488,14 @@ msgid ""
"\n" "\n"
"You may turn off this option at any time in the Configuration screen." "You may turn off this option at any time in the Configuration screen."
msgstr "" msgstr ""
"Om een geolocatie aan de notitie te koppelen, heeft de app uw toestemming "
"nodig om toegang te krijgen tot uw locatie.\n"
"\n"
"U kunt deze optie op elk moment uitschakelen in het 'Configuratie'-scherm."
#: ReactNativeClient/lib/components/screens/Note.js:341 #: ReactNativeClient/lib/components/screens/Note.js:341
#, fuzzy
msgid "Permission needed" msgid "Permission needed"
msgstr "Toestemming om de camera te gebruiken" msgstr "Toestemming vereist"
#: ReactNativeClient/lib/components/screens/Note.js:583 #: ReactNativeClient/lib/components/screens/Note.js:583
#, javascript-format #, javascript-format
@ -3737,23 +3744,23 @@ msgstr "Geef een importformaat op voor %s"
#: ReactNativeClient/lib/services/KeymapService.js:240 #: ReactNativeClient/lib/services/KeymapService.js:240
msgid "command" msgid "command"
msgstr "" msgstr "commando"
#: ReactNativeClient/lib/services/KeymapService.js:240 #: ReactNativeClient/lib/services/KeymapService.js:240
#: ReactNativeClient/lib/services/KeymapService.js:245 #: ReactNativeClient/lib/services/KeymapService.js:245
#, javascript-format #, javascript-format
msgid "\"%s\" is missing the required \"%s\" property." msgid "\"%s\" is missing the required \"%s\" property."
msgstr "" msgstr "\"%s\" mist de vereiste \"%s\" eigenschap."
#: ReactNativeClient/lib/services/KeymapService.js:245 #: ReactNativeClient/lib/services/KeymapService.js:245
#: ReactNativeClient/lib/services/KeymapService.js:252 #: ReactNativeClient/lib/services/KeymapService.js:252
msgid "accelerator" msgid "accelerator"
msgstr "" msgstr "versneller"
#: ReactNativeClient/lib/services/KeymapService.js:252 #: ReactNativeClient/lib/services/KeymapService.js:252
#, fuzzy, javascript-format #, javascript-format
msgid "Invalid %s: %s." msgid "Invalid %s: %s."
msgstr "Ongeldig antwoord: %s" msgstr "Ongeldig %s: %s."
#: ReactNativeClient/lib/services/KeymapService.js:270 #: ReactNativeClient/lib/services/KeymapService.js:270
#, javascript-format #, javascript-format
@ -3761,11 +3768,13 @@ msgid ""
"Accelerator \"%s\" is used for \"%s\" and \"%s\" commands. This may lead to " "Accelerator \"%s\" is used for \"%s\" and \"%s\" commands. This may lead to "
"unexpected behaviour." "unexpected behaviour."
msgstr "" msgstr ""
"Versneller \"%s\" wordt gebruikt voor \"%s\" en \"%s\" commando's. Dit kan "
"tot onverwacht gedrag leiden."
#: ReactNativeClient/lib/services/KeymapService.js:295 #: ReactNativeClient/lib/services/KeymapService.js:295
#, javascript-format #, javascript-format
msgid "Accelerator \"%s\" is not valid." msgid "Accelerator \"%s\" is not valid."
msgstr "" msgstr "Versneller \"%s\" is niet geldig."
#: ReactNativeClient/lib/services/report.js:121 #: ReactNativeClient/lib/services/report.js:121
msgid "Items that cannot be synchronised" msgid "Items that cannot be synchronised"

View File

@ -3,6 +3,7 @@ require('app-module-path').addPath(__dirname);
const { tempFilePath } = require('test-utils.js'); const { tempFilePath } = require('test-utils.js');
const KeymapService = require('lib/services/KeymapService').default; const KeymapService = require('lib/services/KeymapService').default;
const keymapService = KeymapService.instance(); const keymapService = KeymapService.instance();
keymapService.initialize([]);
describe('services_KeymapService', () => { describe('services_KeymapService', () => {
describe('validateAccelerator', () => { describe('validateAccelerator', () => {
@ -31,7 +32,7 @@ describe('services_KeymapService', () => {
}; };
Object.entries(testCases).forEach(([platform, accelerators]) => { Object.entries(testCases).forEach(([platform, accelerators]) => {
keymapService.initialize(platform); keymapService.initialize([], platform);
accelerators.forEach(accelerator => { accelerators.forEach(accelerator => {
expect(() => keymapService.validateAccelerator(accelerator)).not.toThrow(); expect(() => keymapService.validateAccelerator(accelerator)).not.toThrow();
}); });
@ -69,7 +70,7 @@ describe('services_KeymapService', () => {
}; };
Object.entries(testCases).forEach(([platform, accelerators]) => { Object.entries(testCases).forEach(([platform, accelerators]) => {
keymapService.initialize(platform); keymapService.initialize([], platform);
accelerators.forEach(accelerator => { accelerators.forEach(accelerator => {
expect(() => keymapService.validateAccelerator(accelerator)).toThrow(); expect(() => keymapService.validateAccelerator(accelerator)).toThrow();
}); });
@ -81,12 +82,12 @@ describe('services_KeymapService', () => {
beforeEach(() => keymapService.initialize()); beforeEach(() => keymapService.initialize());
it('should allow registering new commands', async () => { it('should allow registering new commands', async () => {
keymapService.initialize('linux'); keymapService.initialize([], 'linux');
keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B'); keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B');
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B'); expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B');
// Check that macOS key conversion is working // Check that macOS key conversion is working
keymapService.initialize('darwin'); keymapService.initialize([], 'darwin');
keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B'); keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B');
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B'); expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B');
keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X'); keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X');
@ -95,7 +96,7 @@ describe('services_KeymapService', () => {
const keymapFilePath = tempFilePath('json'); const keymapFilePath = tempFilePath('json');
await keymapService.saveCustomKeymap(keymapFilePath); await keymapService.saveCustomKeymap(keymapFilePath);
keymapService.initialize('darwin'); keymapService.initialize([], 'darwin');
await keymapService.loadCustomKeymap(keymapFilePath); await keymapService.loadCustomKeymap(keymapFilePath);
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X'); expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X');
@ -106,17 +107,17 @@ describe('services_KeymapService', () => {
beforeEach(() => keymapService.initialize()); beforeEach(() => keymapService.initialize());
it('should return the platform-specific default Accelerator', () => { it('should return the platform-specific default Accelerator', () => {
keymapService.initialize('darwin'); keymapService.initialize([], 'darwin');
expect(keymapService.getAccelerator('newNote')).toEqual('Cmd+N'); expect(keymapService.getAccelerator('newNote')).toEqual('Cmd+N');
expect(keymapService.getAccelerator('synchronize')).toEqual('Cmd+S'); expect(keymapService.getAccelerator('synchronize')).toEqual('Cmd+S');
expect(keymapService.getAccelerator('textSelectAll')).toEqual('Cmd+A'); expect(keymapService.getAccelerator('textSelectAll')).toEqual('Cmd+A');
expect(keymapService.getAccelerator('textBold')).toEqual('Cmd+B'); expect(keymapService.getAccelerator('textBold')).toEqual('Cmd+B');
keymapService.initialize('linux'); keymapService.initialize([], 'linux');
expect(keymapService.getAccelerator('newNote')).toEqual('Ctrl+N'); expect(keymapService.getAccelerator('newNote')).toEqual('Ctrl+N');
expect(keymapService.getAccelerator('synchronize')).toEqual('Ctrl+S'); expect(keymapService.getAccelerator('synchronize')).toEqual('Ctrl+S');
keymapService.initialize('win32'); keymapService.initialize([], 'win32');
expect(keymapService.getAccelerator('textSelectAll')).toEqual('Ctrl+A'); expect(keymapService.getAccelerator('textSelectAll')).toEqual('Ctrl+A');
expect(keymapService.getAccelerator('textBold')).toEqual('Ctrl+B'); expect(keymapService.getAccelerator('textBold')).toEqual('Ctrl+B');
}); });
@ -130,7 +131,7 @@ describe('services_KeymapService', () => {
beforeEach(() => keymapService.initialize()); beforeEach(() => keymapService.initialize());
it('should update the Accelerator', () => { it('should update the Accelerator', () => {
keymapService.initialize('darwin'); keymapService.initialize(['print'], 'darwin');
const testCases_Darwin = [ const testCases_Darwin = [
{ command: 'newNote', accelerator: 'Ctrl+Option+Shift+N' }, { command: 'newNote', accelerator: 'Ctrl+Option+Shift+N' },
{ command: 'synchronize', accelerator: 'F11' }, { command: 'synchronize', accelerator: 'F11' },
@ -147,7 +148,7 @@ describe('services_KeymapService', () => {
expect(keymapService.getAccelerator(command)).toEqual(accelerator); expect(keymapService.getAccelerator(command)).toEqual(accelerator);
}); });
keymapService.initialize('linux'); keymapService.initialize(['print'], 'linux');
const testCases_Linux = [ const testCases_Linux = [
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' }, { command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
{ command: 'synchronize', accelerator: 'F15' }, { command: 'synchronize', accelerator: 'F15' },
@ -167,7 +168,7 @@ describe('services_KeymapService', () => {
}); });
describe('getDefaultAccelerator', () => { describe('getDefaultAccelerator', () => {
beforeEach(() => keymapService.initialize()); beforeEach(() => keymapService.initialize(['print', 'linux']));
it('should return the default accelerator', () => { it('should return the default accelerator', () => {
const testCases = [ const testCases = [
@ -196,7 +197,7 @@ describe('services_KeymapService', () => {
beforeEach(() => keymapService.initialize()); beforeEach(() => keymapService.initialize());
it('should update the keymap', () => { it('should update the keymap', () => {
keymapService.initialize('darwin'); keymapService.initialize([], 'darwin');
const customKeymapItems_Darwin = [ const customKeymapItems_Darwin = [
{ command: 'newNote', accelerator: 'Option+Shift+Cmd+N' }, { command: 'newNote', accelerator: 'Option+Shift+Cmd+N' },
{ command: 'synchronize', accelerator: 'Ctrl+F11' }, { command: 'synchronize', accelerator: 'Ctrl+F11' },
@ -217,7 +218,7 @@ describe('services_KeymapService', () => {
expect(keymapService.getAccelerator(command)).toEqual(accelerator); expect(keymapService.getAccelerator(command)).toEqual(accelerator);
}); });
keymapService.initialize('win32'); keymapService.initialize([], 'win32');
const customKeymapItems_Win32 = [ const customKeymapItems_Win32 = [
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' }, { command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
{ command: 'synchronize', accelerator: 'Ctrl+F11' }, { command: 'synchronize', accelerator: 'Ctrl+F11' },

View File

@ -14,6 +14,7 @@ import Setting from 'lib/models/Setting';
import actionApi from 'lib/services/rest/actionApi.desktop'; import actionApi from 'lib/services/rest/actionApi.desktop';
import BaseApplication from 'lib/BaseApplication'; import BaseApplication from 'lib/BaseApplication';
import { _, setLocale } from 'lib/locale'; import { _, setLocale } from 'lib/locale';
import menuCommandNames from './gui/menuCommandNames';
require('app-module-path').addPath(__dirname); require('app-module-path').addPath(__dirname);
@ -495,14 +496,6 @@ class Application extends BaseApplication {
const filename = Setting.custom_css_files.JOPLIN_APP; const filename = Setting.custom_css_files.JOPLIN_APP;
await CssUtils.injectCustomStyles(`${dir}/${filename}`); await CssUtils.injectCustomStyles(`${dir}/${filename}`);
const keymapService = KeymapService.instance();
try {
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
} catch (err) {
reg.logger().error(err.message);
}
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId })); AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
AlarmService.setLogger(reg.logger()); AlarmService.setLogger(reg.logger());
@ -533,9 +526,21 @@ class Application extends BaseApplication {
CommandService.instance().registerDeclaration(declaration); CommandService.instance().registerDeclaration(declaration);
} }
// Since the settings need to be loaded before the store is created, it will never const keymapService = KeymapService.instance();
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be // We only add the commands that appear in the menu because only
// initialised. So we manually call dispatchUpdateAll() to force an update. // those can have a shortcut associated with them.
keymapService.initialize(menuCommandNames());
try {
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
} catch (error) {
reg.logger().error(error);
}
// 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 initialised. So we
// manually call dispatchUpdateAll() to force an update.
Setting.dispatchUpdateAll(); Setting.dispatchUpdateAll();
await FoldersScreenUtils.refreshFolders(); await FoldersScreenUtils.refreshFolders();

View File

@ -5,7 +5,7 @@ import { _ } from 'lib/locale';
const commandService = CommandService.instance(); const commandService = CommandService.instance();
const getLabel = (commandName: string) => { const getLabel = (commandName: string):string => {
if (commandService.exists(commandName)) return commandService.label(commandName, true); if (commandService.exists(commandName)) return commandService.label(commandName, true);
// Some commands are not registered in CommandService at the moment // Some commands are not registered in CommandService at the moment

View File

@ -1,11 +1,22 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import KeymapService, { KeymapItem } from 'lib/services/KeymapService'; import KeymapService, { KeymapItem } from 'lib/services/KeymapService';
import getLabel from './getLabel';
const keymapService = KeymapService.instance(); const keymapService = KeymapService.instance();
// This custom hook provides a synchronized snapshot of the keymap residing at KeymapService // This custom hook provides a synchronized snapshot of the keymap residing at KeymapService
// All the logic regarding altering and interacting with the keymap is isolated from the components // All the logic regarding altering and interacting with the keymap is isolated from the components
function allKeymapItems() {
const output = keymapService.getKeymapItems().slice();
output.sort((a:KeymapItem, b:KeymapItem) => {
return getLabel(a.command).toLocaleLowerCase() < getLabel(b.command).toLocaleLowerCase() ? -1 : +1;
});
return output;
}
const useKeymap = (): [ const useKeymap = (): [
KeymapItem[], KeymapItem[],
Error, Error,
@ -13,7 +24,7 @@ const useKeymap = (): [
(commandName: string, accelerator: string) => void, (commandName: string, accelerator: string) => void,
(commandName: string) => void (commandName: string) => void
] => { ] => {
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => keymapService.getKeymapItems()); const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => allKeymapItems());
const [keymapError, setKeymapError] = useState<Error>(null); const [keymapError, setKeymapError] = useState<Error>(null);
const [mustSave, setMustSave] = useState(false); const [mustSave, setMustSave] = useState(false);
@ -42,7 +53,7 @@ const useKeymap = (): [
const overrideKeymapItems = (customKeymapItems: KeymapItem[]) => { const overrideKeymapItems = (customKeymapItems: KeymapItem[]) => {
const oldKeymapItems = [...customKeymapItems]; const oldKeymapItems = [...customKeymapItems];
keymapService.initialize(); // Start with a fresh keymap keymapService.resetKeymap(); // Start with a fresh keymap
try { try {
// First, try to update the in-memory keymap of KeymapService // First, try to update the in-memory keymap of KeymapService

View File

@ -14,6 +14,7 @@ import InteropServiceHelper from '../InteropServiceHelper';
import { _ } from 'lib/locale'; import { _ } from 'lib/locale';
import { MenuItem, MenuItemLocation } from 'lib/services/plugins/api/types'; import { MenuItem, MenuItemLocation } from 'lib/services/plugins/api/types';
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext'; import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
import menuCommandNames from './menuCommandNames';
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
@ -86,39 +87,7 @@ interface Props {
pluginMenus: any[], pluginMenus: any[],
} }
const commandNames:string[] = [ const commandNames:string[] = menuCommandNames();
'focusElementSideBar',
'focusElementNoteList',
'focusElementNoteTitle',
'focusElementNoteBody',
'exportPdf',
'newNote',
'newTodo',
'newFolder',
'newSubFolder',
'print',
'synchronize',
'textCopy',
'textCut',
'textPaste',
'textSelectAll',
'textBold',
'textItalic',
'textLink',
'textCode',
'insertDateTime',
'attachFile',
'focusSearch',
'showLocalSearch',
'toggleSideBar',
'toggleNoteList',
'toggleVisiblePanes',
'toggleExternalEditing',
'setTags',
'showNoteContentProperties',
'copyDevCommand',
'openProfileDirectory',
];
function menuItemSetChecked(id:string, checked:boolean) { function menuItemSetChecked(id:string, checked:boolean) {
const menu = Menu.getApplicationMenu(); const menu = Menu.getApplicationMenu();
@ -249,10 +218,8 @@ function useMenu(props:Props) {
menuItemDic.focusElementNoteBody, menuItemDic.focusElementNoteBody,
]; ];
let toolsItems:any[] = [];
const importItems = []; const importItems = [];
const exportItems = []; const exportItems = [];
const toolsItemsFirst = [];
const templateItems:any[] = []; const templateItems:any[] = [];
const ioService = InteropService.instance(); const ioService = InteropService.instance();
const ioModules = ioService.modules(); const ioModules = ioService.modules();
@ -299,16 +266,18 @@ function useMenu(props:Props) {
}, },
}; };
const separator = () => {
return {
type: 'separator',
};
};
const newNoteItem = menuItemDic.newNote; const newNoteItem = menuItemDic.newNote;
const newTodoItem = menuItemDic.newTodo; const newTodoItem = menuItemDic.newTodo;
const newFolderItem = menuItemDic.newFolder; const newFolderItem = menuItemDic.newFolder;
const newSubFolderItem = menuItemDic.newSubFolder; const newSubFolderItem = menuItemDic.newSubFolder;
const printItem = menuItemDic.print; const printItem = menuItemDic.print;
toolsItemsFirst.push(syncStatusItem, {
type: 'separator',
});
templateItems.push({ templateItems.push({
label: _('Create note from template'), label: _('Create note from template'),
click: () => { click: () => {
@ -342,18 +311,22 @@ function useMenu(props:Props) {
}, },
}); });
let toolsItems:any[] = [];
// we need this workaround, because on macOS the menu is different // we need this workaround, because on macOS the menu is different
const toolsItemsWindowsLinux:any[] = toolsItemsFirst.concat([{ const toolsItemsWindowsLinux:any[] = [
label: _('Options'), {
visible: !shim.isMac(), label: _('Options'),
accelerator: !shim.isMac() && keymapService.getAccelerator('config'), accelerator: keymapService.getAccelerator('config'),
click: () => { click: () => {
props.dispatch({ props.dispatch({
type: 'NAV_GO', type: 'NAV_GO',
routeName: 'Config', routeName: 'Config',
}); });
},
}, },
} as any]); separator(),
];
// the following menu items will be available for all OS under Tools // the following menu items will be available for all OS under Tools
const toolsItemsAll = [{ const toolsItemsAll = [{
@ -451,9 +424,7 @@ function useMenu(props:Props) {
menuItemDic.synchronize, menuItemDic.synchronize,
shim.isMac() ? syncStatusItem : noItem, { shim.isMac() ? noItem : printItem, {
type: 'separator',
}, shim.isMac() ? noItem : printItem, {
type: 'separator', type: 'separator',
platforms: ['darwin'], platforms: ['darwin'],
}, },
@ -518,12 +489,6 @@ function useMenu(props:Props) {
}); });
} }
const separator = () => {
return {
type: 'separator',
};
};
const rootMenus:any = { const rootMenus:any = {
edit: { edit: {
id: 'edit', id: 'edit',
@ -586,11 +551,6 @@ function useMenu(props:Props) {
}, },
}, },
separator(), separator(),
{
label: _('Focus'),
submenu: focusItems,
},
separator(),
{ {
label: _('Actual Size'), label: _('Actual Size'),
click: () => { click: () => {
@ -623,6 +583,18 @@ function useMenu(props:Props) {
accelerator: 'CommandOrControl+-', accelerator: 'CommandOrControl+-',
}], }],
}, },
go: {
label: _('&Go'),
submenu: [
menuItemDic.historyBackward,
menuItemDic.historyForward,
separator(),
{
label: _('Focus'),
submenu: focusItems,
},
],
},
note: { note: {
label: _('&Note'), label: _('&Note'),
submenu: [ submenu: [
@ -655,6 +627,8 @@ function useMenu(props:Props) {
click: () => _checkForUpdates(), click: () => _checkForUpdates(),
}, },
separator(), separator(),
syncStatusItem,
separator(),
{ {
id: 'help:toggleDevTools', id: 'help:toggleDevTools',
label: _('Toggle development tools'), label: _('Toggle development tools'),
@ -709,6 +683,7 @@ function useMenu(props:Props) {
const pluginMenuItems = PluginManager.instance().menuItems(); const pluginMenuItems = PluginManager.instance().menuItems();
for (const item of pluginMenuItems) { for (const item of pluginMenuItems) {
const itemParent = rootMenus[item.parent] ? rootMenus[item.parent] : 'tools'; const itemParent = rootMenus[item.parent] ? rootMenus[item.parent] : 'tools';
itemParent.submenu.push(separator());
itemParent.submenu.push(item); itemParent.submenu.push(item);
} }
} }
@ -741,6 +716,7 @@ function useMenu(props:Props) {
rootMenus.file, rootMenus.file,
rootMenus.edit, rootMenus.edit,
rootMenus.view, rootMenus.view,
rootMenus.go,
rootMenus.note, rootMenus.note,
rootMenus.tools, rootMenus.tools,
rootMenus.help, rootMenus.help,
@ -748,46 +724,6 @@ function useMenu(props:Props) {
if (shim.isMac()) template.splice(0, 0, rootMenus.macOsApp); if (shim.isMac()) template.splice(0, 0, rootMenus.macOsApp);
// TODO
// function isEmptyMenu(template:any[]) {
// for (let i = 0; i < template.length; i++) {
// const t = template[i];
// if (t.type !== 'separator') return false;
// }
// return true;
// }
// function removeUnwantedItems(template:any[], screen:string) {
// const platform = shim.platformName();
// let output = [];
// for (let i = 0; i < template.length; i++) {
// const t = Object.assign({}, template[i]);
// if (t.screens && t.screens.indexOf(screen) < 0) continue;
// if (t.platforms && t.platforms.indexOf(platform) < 0) continue;
// if (t.submenu) t.submenu = removeUnwantedItems(t.submenu, screen);
// if (('submenu' in t) && isEmptyMenu(t.submenu)) continue;
// output.push(t);
// }
// // Remove empty separator for now empty sections
// const temp = [];
// let previous = null;
// for (let i = 0; i < output.length; i++) {
// const t = Object.assign({}, output[i]);
// if (t.type === 'separator') {
// if (!previous) continue;
// if (previous.type === 'separator') continue;
// }
// temp.push(t);
// previous = t;
// }
// output = temp;
// return output;
// }
if (props.routeName !== 'Main') { if (props.routeName !== 'Main') {
setMenu(Menu.buildFromTemplate([ setMenu(Menu.buildFromTemplate([
{ {

View File

@ -0,0 +1,37 @@
export default function() {
return [
'attachFile',
'copyDevCommand',
'exportPdf',
'focusElementNoteBody',
'focusElementNoteList',
'focusElementNoteTitle',
'focusElementSideBar',
'focusSearch',
'historyBackward',
'historyForward',
'insertDateTime',
'newFolder',
'newNote',
'newSubFolder',
'newTodo',
'openProfileDirectory',
'print',
'setTags',
'showLocalSearch',
'showNoteContentProperties',
'synchronize',
'textBold',
'textCode',
'textCopy',
'textCut',
'textItalic',
'textLink',
'textPaste',
'textSelectAll',
'toggleExternalEditing',
'toggleNoteList',
'toggleSideBar',
'toggleVisiblePanes',
];
}

View File

@ -1,6 +1,6 @@
{ {
"name": "Joplin", "name": "Joplin",
"version": "1.3.10", "version": "1.3.11",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "Joplin", "name": "Joplin",
"version": "1.3.10", "version": "1.3.11",
"description": "Joplin for Desktop", "description": "Joplin for Desktop",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@ -557,7 +557,7 @@ GotoAnything.manifest = {
menuItems: [ menuItems: [
{ {
name: 'main', name: 'main',
parent: 'tools', parent: 'go',
label: _('Goto Anything...'), label: _('Goto Anything...'),
accelerator: () => KeymapService.instance().getAccelerator('gotoAnything'), accelerator: () => KeymapService.instance().getAccelerator('gotoAnything'),
screens: ['Main'], screens: ['Main'],

View File

@ -211,7 +211,9 @@ class NoteScreenComponent extends BaseScreenComponent {
}; };
this.useBetaEditor = () => { this.useBetaEditor = () => {
return Setting.value('editor.beta') && Platform.OS !== 'android'; // Disable for now
return false;
// return Setting.value('editor.beta') && Platform.OS !== 'android';
}; };
this.takePhoto_onPress = this.takePhoto_onPress.bind(this); this.takePhoto_onPress = this.takePhoto_onPress.bind(this);

View File

@ -508,11 +508,16 @@ class Setting extends BaseModel {
'folders.sortOrder.reverse': { value: false, type: SettingItemType.Bool, public: true, label: () => _('Reverse sort order'), appTypes: ['cli'] }, 'folders.sortOrder.reverse': { value: false, type: SettingItemType.Bool, public: true, label: () => _('Reverse sort order'), appTypes: ['cli'] },
trackLocation: { value: true, type: SettingItemType.Bool, section: 'note', public: true, label: () => _('Save geo-location with notes') }, trackLocation: { value: true, type: SettingItemType.Bool, section: 'note', public: true, label: () => _('Save geo-location with notes') },
// 2020-10-29: For now disable the beta editor due to
// underlying bugs in the TextInput component which we cannot
// fix. Also the editor crashes in Android and in some cases in
// iOS.
// https://discourse.joplinapp.org/t/anyone-using-the-beta-editor-on-ios/11658/9
'editor.beta': { 'editor.beta': {
value: false, value: false,
type: SettingItemType.Bool, type: SettingItemType.Bool,
section: 'note', section: 'note',
public: mobilePlatform === 'ios', public: false, // mobilePlatform === 'ios',
appTypes: ['mobile'], appTypes: ['mobile'],
label: () => 'Opt-in to the editor beta', label: () => 'Opt-in to the editor beta',
description: () => 'This beta adds list continuation, Markdown preview, and Markdown shortcuts. If you find bugs, please report them in the Discourse forum.', description: () => 'This beta adds list continuation, Markdown preview, and Markdown shortcuts. If you find bugs, please report them in the Discourse forum.',

View File

@ -144,8 +144,17 @@ export default class CommandService extends BaseService {
return output; return output;
} }
public commandNames() { public commandNames(publicOnly:boolean = false) {
return Object.keys(this.commands_); if (publicOnly) {
const output = [];
for (const name in this.commands_) {
if (!this.isPublic(name)) continue;
output.push(name);
}
return output;
} else {
return Object.keys(this.commands_);
}
} }
public commandByName(name:string, options:CommandByNameOptions = null):Command { public commandByName(name:string, options:CommandByNameOptions = null):Command {
@ -230,6 +239,10 @@ export default class CommandService extends BaseService {
return stateToWhenClauseContext(this.store_.getState()); return stateToWhenClauseContext(this.store_.getState());
} }
public isPublic(commandName:string) {
return !!this.label(commandName);
}
// When looping on commands and checking their enabled state, the whenClauseContext // When looping on commands and checking their enabled state, the whenClauseContext
// should be specified (created using currentWhenClauseContext) to avoid having // should be specified (created using currentWhenClauseContext) to avoid having
// to re-create it on each call. // to re-create it on each call.

View File

@ -106,8 +106,9 @@ class DecryptionWorker {
if (!('errorHandler' in options)) options.errorHandler = 'log'; if (!('errorHandler' in options)) options.errorHandler = 'log';
if (this.state_ !== 'idle') { if (this.state_ !== 'idle') {
this.logger().debug(`DecryptionWorker: cannot start because state is "${this.state_}"`); const msg = `DecryptionWorker: cannot start because state is "${this.state_}"`;
return; this.logger().debug(msg);
return { error: new Error(msg) };
} }
// Note: the logic below is an optimisation to avoid going through the loop if no master key exists // Note: the logic below is an optimisation to avoid going through the loop if no master key exists
@ -115,7 +116,8 @@ class DecryptionWorker {
// "throw" and "dispatch" logic. // "throw" and "dispatch" logic.
const loadedMasterKeyCount = await this.encryptionService().loadedMasterKeysCount(); const loadedMasterKeyCount = await this.encryptionService().loadedMasterKeysCount();
if (!loadedMasterKeyCount) { if (!loadedMasterKeyCount) {
this.logger().info('DecryptionWorker: cannot start because no master key is currently loaded.'); const msg = 'DecryptionWorker: cannot start because no master key is currently loaded.';
this.logger().info(msg);
const ids = await MasterKey.allIds(); const ids = await MasterKey.allIds();
if (ids.length) { if (ids.length) {
@ -130,7 +132,7 @@ class DecryptionWorker {
}); });
} }
} }
return; return { error: new Error(msg) };
} }
this.logger().info('DecryptionWorker: starting decryption...'); this.logger().info('DecryptionWorker: starting decryption...');

View File

@ -16,7 +16,6 @@ const defaultKeymapItems = {
{ accelerator: 'Cmd+N', command: 'newNote' }, { accelerator: 'Cmd+N', command: 'newNote' },
{ accelerator: 'Cmd+T', command: 'newTodo' }, { accelerator: 'Cmd+T', command: 'newTodo' },
{ accelerator: 'Cmd+S', command: 'synchronize' }, { accelerator: 'Cmd+S', command: 'synchronize' },
{ accelerator: '', command: 'print' },
{ accelerator: 'Cmd+H', command: 'hideApp' }, { accelerator: 'Cmd+H', command: 'hideApp' },
{ accelerator: 'Cmd+Q', command: 'quit' }, { accelerator: 'Cmd+Q', command: 'quit' },
{ accelerator: 'Cmd+,', command: 'config' }, { accelerator: 'Cmd+,', command: 'config' },
@ -51,7 +50,6 @@ const defaultKeymapItems = {
{ accelerator: 'Ctrl+N', command: 'newNote' }, { accelerator: 'Ctrl+N', command: 'newNote' },
{ accelerator: 'Ctrl+T', command: 'newTodo' }, { accelerator: 'Ctrl+T', command: 'newTodo' },
{ accelerator: 'Ctrl+S', command: 'synchronize' }, { accelerator: 'Ctrl+S', command: 'synchronize' },
{ accelerator: null, command: 'print' },
{ accelerator: 'Ctrl+Q', command: 'quit' }, { accelerator: 'Ctrl+Q', command: 'quit' },
{ accelerator: 'Ctrl+Alt+I', command: 'insertTemplate' }, { accelerator: 'Ctrl+Alt+I', command: 'insertTemplate' },
{ accelerator: 'Ctrl+C', command: 'textCopy' }, { accelerator: 'Ctrl+C', command: 'textCopy' },
@ -103,29 +101,42 @@ export default class KeymapService extends BaseService {
super(); super();
this.lastSaveTime_ = Date.now(); this.lastSaveTime_ = Date.now();
// By default, initialize for the current platform
// Manual initialization allows testing for other platforms
this.initialize();
} }
public get lastSaveTime():number { public get lastSaveTime():number {
return this.lastSaveTime_; return this.lastSaveTime_;
} }
public initialize(platform: string = shim.platformName()) { // `additionalDefaultCommandNames` will be added to the default keymap
// **except** if they are already in it. Basically this is a mechanism
// to add all the commands from the command service to the default
// keymap.
public initialize(additionalDefaultCommandNames:string[] = [], platform: string = shim.platformName()) {
this.platform = platform; this.platform = platform;
switch (platform) { switch (platform) {
case 'darwin': case 'darwin':
this.defaultKeymapItems = defaultKeymapItems.darwin; this.defaultKeymapItems = defaultKeymapItems.darwin.slice();
this.modifiersRegExp = modifiersRegExp.darwin; this.modifiersRegExp = modifiersRegExp.darwin;
break; break;
default: default:
this.defaultKeymapItems = defaultKeymapItems.default; this.defaultKeymapItems = defaultKeymapItems.default.slice();
this.modifiersRegExp = modifiersRegExp.default; this.modifiersRegExp = modifiersRegExp.default;
} }
for (const name of additionalDefaultCommandNames) {
if (this.defaultKeymapItems.find((item:KeymapItem) => item.command === name)) continue;
this.defaultKeymapItems.push({
command: name,
accelerator: null,
});
}
this.resetKeymap();
}
// Reset keymap back to its default values
public resetKeymap() {
this.keymap = {}; this.keymap = {};
for (let i = 0; i < this.defaultKeymapItems.length; i++) { for (let i = 0; i < this.defaultKeymapItems.length; i++) {
// Keep the original defaultKeymapItems array untouched // Keep the original defaultKeymapItems array untouched
@ -140,7 +151,9 @@ export default class KeymapService extends BaseService {
if (await shim.fsDriver().exists(customKeymapPath)) { if (await shim.fsDriver().exists(customKeymapPath)) {
this.logger().info(`KeymapService: Loading keymap from file: ${customKeymapPath}`); this.logger().info(`KeymapService: Loading keymap from file: ${customKeymapPath}`);
const customKeymapFile = await shim.fsDriver().readFile(customKeymapPath, 'utf-8'); const customKeymapFile = (await shim.fsDriver().readFile(customKeymapPath, 'utf-8')).trim();
if (!customKeymapFile) return;
// Custom keymaps are supposed to contain an array of keymap items // Custom keymaps are supposed to contain an array of keymap items
this.overrideKeymap(JSON.parse(customKeymapFile)); this.overrideKeymap(JSON.parse(customKeymapFile));
} }
@ -183,8 +196,8 @@ export default class KeymapService extends BaseService {
if (!commandName) throw new Error('Cannot register an accelerator without a command name'); if (!commandName) throw new Error('Cannot register an accelerator without a command name');
const validatedAccelerator = this.convertToPlatform(accelerator); const validatedAccelerator = accelerator ? this.convertToPlatform(accelerator) : null;
this.validateAccelerator(validatedAccelerator); if (validatedAccelerator) this.validateAccelerator(validatedAccelerator);
this.keymap[commandName] = { this.keymap[commandName] = {
command: commandName, command: commandName,
@ -264,7 +277,7 @@ export default class KeymapService extends BaseService {
// Throws whenever there are duplicate Accelerators used in the keymap // Throws whenever there are duplicate Accelerators used in the keymap
this.validateKeymap(); this.validateKeymap();
} catch (err) { } catch (err) {
this.initialize(); // Discard all the changes if there are any issues this.resetKeymap(); // Discard all the changes if there are any issues
throw err; throw err;
} }
} }
@ -339,7 +352,10 @@ export default class KeymapService extends BaseService {
public domToElectronAccelerator(event: KeyboardEvent<HTMLDivElement>) { public domToElectronAccelerator(event: KeyboardEvent<HTMLDivElement>) {
const parts = []; const parts = [];
const { key, ctrlKey, metaKey, altKey, shiftKey } = event;
// We use the "keyCode" and not "key" because the modifier keys
// would change the "key" value. eg "Option+U" would give "º" as a key instead of "U"
const { keyCode, ctrlKey, metaKey, altKey, shiftKey } = event;
// First, the modifiers // First, the modifiers
if (ctrlKey) parts.push('Ctrl'); if (ctrlKey) parts.push('Ctrl');
@ -355,7 +371,7 @@ export default class KeymapService extends BaseService {
} }
// Finally, the key // Finally, the key
const electronKey = KeymapService.domToElectronKey(key); const electronKey = KeymapService.domToElectronKey(String.fromCharCode(keyCode));
if (electronKey) parts.push(electronKey); if (electronKey) parts.push(electronKey);
return parts.join('+'); return parts.join('+');

View File

@ -10,12 +10,13 @@ const { execCommand, githubUsername } = require('./tool-utils.js');
// From https://stackoverflow.com/a/6234804/561309 // From https://stackoverflow.com/a/6234804/561309
function escapeHtml(unsafe) { function escapeHtml(unsafe) {
// We only escape <> as this is enough for Markdown
return unsafe return unsafe
.replace(/&/g, '&amp;') // .replace(/&/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;') .replace(/>/g, '&gt;');
.replace(/"/g, '&quot;') // .replace(/"/g, '&quot;')
.replace(/'/g, '&#039;'); // .replace(/'/g, '&#039;');
} }
async function gitLog(sinceTag) { async function gitLog(sinceTag) {