From 8cfe4b0f82e454b0ec39b393ef65ddee8d5f2823 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Thu, 29 Oct 2020 16:42:47 +0000 Subject: [PATCH 1/9] Mobile: Disable beta editor for now due to bugs that cannot be fixed and crashes. See https://discourse.joplinapp.org/t/11658/9?u=laurent --- ReactNativeClient/lib/components/screens/Note.tsx | 4 +++- ReactNativeClient/lib/models/Setting.ts | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ReactNativeClient/lib/components/screens/Note.tsx b/ReactNativeClient/lib/components/screens/Note.tsx index 9c4f1ae0c2..7cda1f6d1e 100644 --- a/ReactNativeClient/lib/components/screens/Note.tsx +++ b/ReactNativeClient/lib/components/screens/Note.tsx @@ -211,7 +211,9 @@ class NoteScreenComponent extends BaseScreenComponent { }; 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); diff --git a/ReactNativeClient/lib/models/Setting.ts b/ReactNativeClient/lib/models/Setting.ts index 5a95bc2dfc..e05bc09e91 100644 --- a/ReactNativeClient/lib/models/Setting.ts +++ b/ReactNativeClient/lib/models/Setting.ts @@ -508,11 +508,16 @@ class Setting extends BaseModel { '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') }, + // 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': { value: false, type: SettingItemType.Bool, section: 'note', - public: mobilePlatform === 'ios', + public: false, // mobilePlatform === 'ios', appTypes: ['mobile'], 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.', From 86c471afcd99742998d6fd4f846b79be0a89791b Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Thu, 29 Oct 2020 23:37:19 +0000 Subject: [PATCH 2/9] Cli: Fixes #4000: Display proper error message when decryption worker cannot be started --- CliClient/app/command-e2ee.js | 4 +++- ReactNativeClient/lib/services/DecryptionWorker.js | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CliClient/app/command-e2ee.js b/CliClient/app/command-e2ee.js index 6c26150ff3..05beeb0673 100644 --- a/CliClient/app/command-e2ee.js +++ b/CliClient/app/command-e2ee.js @@ -45,10 +45,12 @@ class Command extends BaseCommand { const startDecryption = async () => { this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.')); - while (true) { try { const result = await DecryptionWorker.instance().start(); + + if (result.error) throw result.error; + const line = []; 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)); diff --git a/ReactNativeClient/lib/services/DecryptionWorker.js b/ReactNativeClient/lib/services/DecryptionWorker.js index 2f776f9a4e..ad97f5c592 100644 --- a/ReactNativeClient/lib/services/DecryptionWorker.js +++ b/ReactNativeClient/lib/services/DecryptionWorker.js @@ -106,8 +106,9 @@ class DecryptionWorker { if (!('errorHandler' in options)) options.errorHandler = 'log'; if (this.state_ !== 'idle') { - this.logger().debug(`DecryptionWorker: cannot start because state is "${this.state_}"`); - return; + const msg = `DecryptionWorker: cannot start because state is "${this.state_}"`; + 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 @@ -115,7 +116,8 @@ class DecryptionWorker { // "throw" and "dispatch" logic. const loadedMasterKeyCount = await this.encryptionService().loadedMasterKeysCount(); 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(); if (ids.length) { @@ -130,7 +132,7 @@ class DecryptionWorker { }); } } - return; + return { error: new Error(msg) }; } this.logger().info('DecryptionWorker: starting decryption...'); From 45024149343d93953b00ab75c368064486b29bf9 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 31 Oct 2020 12:25:12 +0000 Subject: [PATCH 3/9] Desktop: Make sure all commands appear in keymap editor --- CliClient/tests/services_KeymapService.js | 27 ++++++------- ElectronClient/app.ts | 17 ++++---- .../gui/KeymapConfig/utils/getLabel.ts | 2 +- .../gui/KeymapConfig/utils/useKeymap.ts | 15 ++++++- .../lib/services/CommandService.ts | 17 +++++++- .../lib/services/KeymapService.ts | 39 ++++++++++++------- 6 files changed, 78 insertions(+), 39 deletions(-) diff --git a/CliClient/tests/services_KeymapService.js b/CliClient/tests/services_KeymapService.js index 2a8020191f..a2c7c13074 100644 --- a/CliClient/tests/services_KeymapService.js +++ b/CliClient/tests/services_KeymapService.js @@ -3,6 +3,7 @@ require('app-module-path').addPath(__dirname); const { tempFilePath } = require('test-utils.js'); const KeymapService = require('lib/services/KeymapService').default; const keymapService = KeymapService.instance(); +keymapService.initialize([]); describe('services_KeymapService', () => { describe('validateAccelerator', () => { @@ -31,7 +32,7 @@ describe('services_KeymapService', () => { }; Object.entries(testCases).forEach(([platform, accelerators]) => { - keymapService.initialize(platform); + keymapService.initialize([], platform); accelerators.forEach(accelerator => { expect(() => keymapService.validateAccelerator(accelerator)).not.toThrow(); }); @@ -69,7 +70,7 @@ describe('services_KeymapService', () => { }; Object.entries(testCases).forEach(([platform, accelerators]) => { - keymapService.initialize(platform); + keymapService.initialize([], platform); accelerators.forEach(accelerator => { expect(() => keymapService.validateAccelerator(accelerator)).toThrow(); }); @@ -81,12 +82,12 @@ describe('services_KeymapService', () => { beforeEach(() => keymapService.initialize()); it('should allow registering new commands', async () => { - keymapService.initialize('linux'); + keymapService.initialize([], 'linux'); keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B'); expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B'); // Check that macOS key conversion is working - keymapService.initialize('darwin'); + keymapService.initialize([], 'darwin'); keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B'); expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B'); keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X'); @@ -95,7 +96,7 @@ describe('services_KeymapService', () => { const keymapFilePath = tempFilePath('json'); await keymapService.saveCustomKeymap(keymapFilePath); - keymapService.initialize('darwin'); + keymapService.initialize([], 'darwin'); await keymapService.loadCustomKeymap(keymapFilePath); expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X'); @@ -106,17 +107,17 @@ describe('services_KeymapService', () => { beforeEach(() => keymapService.initialize()); it('should return the platform-specific default Accelerator', () => { - keymapService.initialize('darwin'); + keymapService.initialize([], 'darwin'); expect(keymapService.getAccelerator('newNote')).toEqual('Cmd+N'); expect(keymapService.getAccelerator('synchronize')).toEqual('Cmd+S'); expect(keymapService.getAccelerator('textSelectAll')).toEqual('Cmd+A'); expect(keymapService.getAccelerator('textBold')).toEqual('Cmd+B'); - keymapService.initialize('linux'); + keymapService.initialize([], 'linux'); expect(keymapService.getAccelerator('newNote')).toEqual('Ctrl+N'); expect(keymapService.getAccelerator('synchronize')).toEqual('Ctrl+S'); - keymapService.initialize('win32'); + keymapService.initialize([], 'win32'); expect(keymapService.getAccelerator('textSelectAll')).toEqual('Ctrl+A'); expect(keymapService.getAccelerator('textBold')).toEqual('Ctrl+B'); }); @@ -130,7 +131,7 @@ describe('services_KeymapService', () => { beforeEach(() => keymapService.initialize()); it('should update the Accelerator', () => { - keymapService.initialize('darwin'); + keymapService.initialize(['print'], 'darwin'); const testCases_Darwin = [ { command: 'newNote', accelerator: 'Ctrl+Option+Shift+N' }, { command: 'synchronize', accelerator: 'F11' }, @@ -147,7 +148,7 @@ describe('services_KeymapService', () => { expect(keymapService.getAccelerator(command)).toEqual(accelerator); }); - keymapService.initialize('linux'); + keymapService.initialize(['print'], 'linux'); const testCases_Linux = [ { command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' }, { command: 'synchronize', accelerator: 'F15' }, @@ -167,7 +168,7 @@ describe('services_KeymapService', () => { }); describe('getDefaultAccelerator', () => { - beforeEach(() => keymapService.initialize()); + beforeEach(() => keymapService.initialize(['print', 'linux'])); it('should return the default accelerator', () => { const testCases = [ @@ -196,7 +197,7 @@ describe('services_KeymapService', () => { beforeEach(() => keymapService.initialize()); it('should update the keymap', () => { - keymapService.initialize('darwin'); + keymapService.initialize([], 'darwin'); const customKeymapItems_Darwin = [ { command: 'newNote', accelerator: 'Option+Shift+Cmd+N' }, { command: 'synchronize', accelerator: 'Ctrl+F11' }, @@ -217,7 +218,7 @@ describe('services_KeymapService', () => { expect(keymapService.getAccelerator(command)).toEqual(accelerator); }); - keymapService.initialize('win32'); + keymapService.initialize([], 'win32'); const customKeymapItems_Win32 = [ { command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' }, { command: 'synchronize', accelerator: 'Ctrl+F11' }, diff --git a/ElectronClient/app.ts b/ElectronClient/app.ts index 0b350300ff..a1077806cd 100644 --- a/ElectronClient/app.ts +++ b/ElectronClient/app.ts @@ -495,14 +495,6 @@ class Application extends BaseApplication { const filename = Setting.custom_css_files.JOPLIN_APP; 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.setLogger(reg.logger()); @@ -533,6 +525,15 @@ class Application extends BaseApplication { CommandService.instance().registerDeclaration(declaration); } + const keymapService = KeymapService.instance(); + keymapService.initialize(CommandService.instance().commandNames(true)); + + 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. diff --git a/ElectronClient/gui/KeymapConfig/utils/getLabel.ts b/ElectronClient/gui/KeymapConfig/utils/getLabel.ts index 268ca0644d..a0a20fe576 100644 --- a/ElectronClient/gui/KeymapConfig/utils/getLabel.ts +++ b/ElectronClient/gui/KeymapConfig/utils/getLabel.ts @@ -5,7 +5,7 @@ import { _ } from 'lib/locale'; const commandService = CommandService.instance(); -const getLabel = (commandName: string) => { +const getLabel = (commandName: string):string => { if (commandService.exists(commandName)) return commandService.label(commandName, true); // Some commands are not registered in CommandService at the moment diff --git a/ElectronClient/gui/KeymapConfig/utils/useKeymap.ts b/ElectronClient/gui/KeymapConfig/utils/useKeymap.ts index 7ce015b22d..62e5f97cce 100644 --- a/ElectronClient/gui/KeymapConfig/utils/useKeymap.ts +++ b/ElectronClient/gui/KeymapConfig/utils/useKeymap.ts @@ -1,11 +1,22 @@ import { useState, useEffect } from 'react'; import KeymapService, { KeymapItem } from 'lib/services/KeymapService'; +import getLabel from './getLabel'; const keymapService = KeymapService.instance(); // 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 +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 = (): [ KeymapItem[], Error, @@ -13,7 +24,7 @@ const useKeymap = (): [ (commandName: string, accelerator: string) => void, (commandName: string) => void ] => { - const [keymapItems, setKeymapItems] = useState(() => keymapService.getKeymapItems()); + const [keymapItems, setKeymapItems] = useState(() => allKeymapItems()); const [keymapError, setKeymapError] = useState(null); const [mustSave, setMustSave] = useState(false); @@ -42,7 +53,7 @@ const useKeymap = (): [ const overrideKeymapItems = (customKeymapItems: KeymapItem[]) => { const oldKeymapItems = [...customKeymapItems]; - keymapService.initialize(); // Start with a fresh keymap + keymapService.resetKeymap(); // Start with a fresh keymap try { // First, try to update the in-memory keymap of KeymapService diff --git a/ReactNativeClient/lib/services/CommandService.ts b/ReactNativeClient/lib/services/CommandService.ts index d220d611c4..16e3944353 100644 --- a/ReactNativeClient/lib/services/CommandService.ts +++ b/ReactNativeClient/lib/services/CommandService.ts @@ -144,8 +144,17 @@ export default class CommandService extends BaseService { return output; } - public commandNames() { - return Object.keys(this.commands_); + public commandNames(publicOnly:boolean = false) { + 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 { @@ -230,6 +239,10 @@ export default class CommandService extends BaseService { return stateToWhenClauseContext(this.store_.getState()); } + public isPublic(commandName:string) { + return !!this.label(commandName); + } + // When looping on commands and checking their enabled state, the whenClauseContext // should be specified (created using currentWhenClauseContext) to avoid having // to re-create it on each call. diff --git a/ReactNativeClient/lib/services/KeymapService.ts b/ReactNativeClient/lib/services/KeymapService.ts index e674b4fe6f..4b65dacdf1 100644 --- a/ReactNativeClient/lib/services/KeymapService.ts +++ b/ReactNativeClient/lib/services/KeymapService.ts @@ -16,7 +16,6 @@ const defaultKeymapItems = { { accelerator: 'Cmd+N', command: 'newNote' }, { accelerator: 'Cmd+T', command: 'newTodo' }, { accelerator: 'Cmd+S', command: 'synchronize' }, - { accelerator: '', command: 'print' }, { accelerator: 'Cmd+H', command: 'hideApp' }, { accelerator: 'Cmd+Q', command: 'quit' }, { accelerator: 'Cmd+,', command: 'config' }, @@ -51,7 +50,6 @@ const defaultKeymapItems = { { accelerator: 'Ctrl+N', command: 'newNote' }, { accelerator: 'Ctrl+T', command: 'newTodo' }, { accelerator: 'Ctrl+S', command: 'synchronize' }, - { accelerator: null, command: 'print' }, { accelerator: 'Ctrl+Q', command: 'quit' }, { accelerator: 'Ctrl+Alt+I', command: 'insertTemplate' }, { accelerator: 'Ctrl+C', command: 'textCopy' }, @@ -103,29 +101,42 @@ export default class KeymapService extends BaseService { super(); this.lastSaveTime_ = Date.now(); - - // By default, initialize for the current platform - // Manual initialization allows testing for other platforms - this.initialize(); } public get lastSaveTime():number { 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; switch (platform) { case 'darwin': - this.defaultKeymapItems = defaultKeymapItems.darwin; + this.defaultKeymapItems = defaultKeymapItems.darwin.slice(); this.modifiersRegExp = modifiersRegExp.darwin; break; default: - this.defaultKeymapItems = defaultKeymapItems.default; + this.defaultKeymapItems = defaultKeymapItems.default.slice(); 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 = {}; for (let i = 0; i < this.defaultKeymapItems.length; i++) { // Keep the original defaultKeymapItems array untouched @@ -140,7 +151,9 @@ export default class KeymapService extends BaseService { if (await shim.fsDriver().exists(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 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'); - const validatedAccelerator = this.convertToPlatform(accelerator); - this.validateAccelerator(validatedAccelerator); + const validatedAccelerator = accelerator ? this.convertToPlatform(accelerator) : null; + if (validatedAccelerator) this.validateAccelerator(validatedAccelerator); this.keymap[commandName] = { command: commandName, @@ -264,7 +277,7 @@ export default class KeymapService extends BaseService { // Throws whenever there are duplicate Accelerators used in the keymap this.validateKeymap(); } 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; } } From 154163bd6c98131175b3038cc2a074bf10bb4ca4 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 31 Oct 2020 12:46:55 +0000 Subject: [PATCH 4/9] Desktop: Fixes #4010: Add history backward and forward commands to keymap and menus --- ElectronClient/gui/MenuBar.tsx | 109 +++++++++--------------- ElectronClient/plugins/GotoAnything.tsx | 2 +- 2 files changed, 40 insertions(+), 71 deletions(-) diff --git a/ElectronClient/gui/MenuBar.tsx b/ElectronClient/gui/MenuBar.tsx index ddcb236201..01c5cc0f66 100644 --- a/ElectronClient/gui/MenuBar.tsx +++ b/ElectronClient/gui/MenuBar.tsx @@ -118,6 +118,8 @@ const commandNames:string[] = [ 'showNoteContentProperties', 'copyDevCommand', 'openProfileDirectory', + 'historyBackward', + 'historyForward', ]; function menuItemSetChecked(id:string, checked:boolean) { @@ -249,10 +251,8 @@ function useMenu(props:Props) { menuItemDic.focusElementNoteBody, ]; - let toolsItems:any[] = []; const importItems = []; const exportItems = []; - const toolsItemsFirst = []; const templateItems:any[] = []; const ioService = InteropService.instance(); const ioModules = ioService.modules(); @@ -299,16 +299,18 @@ function useMenu(props:Props) { }, }; + const separator = () => { + return { + type: 'separator', + }; + }; + const newNoteItem = menuItemDic.newNote; const newTodoItem = menuItemDic.newTodo; const newFolderItem = menuItemDic.newFolder; const newSubFolderItem = menuItemDic.newSubFolder; const printItem = menuItemDic.print; - toolsItemsFirst.push(syncStatusItem, { - type: 'separator', - }); - templateItems.push({ label: _('Create note from template'), click: () => { @@ -342,18 +344,22 @@ function useMenu(props:Props) { }, }); + let toolsItems:any[] = []; + // we need this workaround, because on macOS the menu is different - const toolsItemsWindowsLinux:any[] = toolsItemsFirst.concat([{ - label: _('Options'), - visible: !shim.isMac(), - accelerator: !shim.isMac() && keymapService.getAccelerator('config'), - click: () => { - props.dispatch({ - type: 'NAV_GO', - routeName: 'Config', - }); + const toolsItemsWindowsLinux:any[] = [ + { + label: _('Options'), + accelerator: keymapService.getAccelerator('config'), + click: () => { + props.dispatch({ + type: 'NAV_GO', + routeName: 'Config', + }); + }, }, - } as any]); + separator(), + ]; // the following menu items will be available for all OS under Tools const toolsItemsAll = [{ @@ -451,9 +457,7 @@ function useMenu(props:Props) { menuItemDic.synchronize, - shim.isMac() ? syncStatusItem : noItem, { - type: 'separator', - }, shim.isMac() ? noItem : printItem, { + shim.isMac() ? noItem : printItem, { type: 'separator', platforms: ['darwin'], }, @@ -518,12 +522,6 @@ function useMenu(props:Props) { }); } - const separator = () => { - return { - type: 'separator', - }; - }; - const rootMenus:any = { edit: { id: 'edit', @@ -586,11 +584,6 @@ function useMenu(props:Props) { }, }, separator(), - { - label: _('Focus'), - submenu: focusItems, - }, - separator(), { label: _('Actual Size'), click: () => { @@ -623,6 +616,18 @@ function useMenu(props:Props) { accelerator: 'CommandOrControl+-', }], }, + go: { + label: _('&Go'), + submenu: [ + menuItemDic.historyBackward, + menuItemDic.historyForward, + separator(), + { + label: _('Focus'), + submenu: focusItems, + }, + ], + }, note: { label: _('&Note'), submenu: [ @@ -655,6 +660,8 @@ function useMenu(props:Props) { click: () => _checkForUpdates(), }, separator(), + syncStatusItem, + separator(), { id: 'help:toggleDevTools', label: _('Toggle development tools'), @@ -709,6 +716,7 @@ function useMenu(props:Props) { const pluginMenuItems = PluginManager.instance().menuItems(); for (const item of pluginMenuItems) { const itemParent = rootMenus[item.parent] ? rootMenus[item.parent] : 'tools'; + itemParent.submenu.push(separator()); itemParent.submenu.push(item); } } @@ -741,6 +749,7 @@ function useMenu(props:Props) { rootMenus.file, rootMenus.edit, rootMenus.view, + rootMenus.go, rootMenus.note, rootMenus.tools, rootMenus.help, @@ -748,46 +757,6 @@ function useMenu(props:Props) { 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') { setMenu(Menu.buildFromTemplate([ { diff --git a/ElectronClient/plugins/GotoAnything.tsx b/ElectronClient/plugins/GotoAnything.tsx index 17ec4c78da..01ffd23c3e 100644 --- a/ElectronClient/plugins/GotoAnything.tsx +++ b/ElectronClient/plugins/GotoAnything.tsx @@ -557,7 +557,7 @@ GotoAnything.manifest = { menuItems: [ { name: 'main', - parent: 'tools', + parent: 'go', label: _('Goto Anything...'), accelerator: () => KeymapService.instance().getAccelerator('gotoAnything'), screens: ['Main'], From e4f53a48d2af20f7ad31d336b711c450a64222fd Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 31 Oct 2020 13:01:40 +0000 Subject: [PATCH 5/9] Desktop: Fixed handling of Option key for shortcuts in macOS --- ReactNativeClient/lib/services/KeymapService.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ReactNativeClient/lib/services/KeymapService.ts b/ReactNativeClient/lib/services/KeymapService.ts index 4b65dacdf1..6e51e6d82d 100644 --- a/ReactNativeClient/lib/services/KeymapService.ts +++ b/ReactNativeClient/lib/services/KeymapService.ts @@ -352,7 +352,10 @@ export default class KeymapService extends BaseService { public domToElectronAccelerator(event: KeyboardEvent) { 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 if (ctrlKey) parts.push('Ctrl'); @@ -368,7 +371,7 @@ export default class KeymapService extends BaseService { } // Finally, the key - const electronKey = KeymapService.domToElectronKey(key); + const electronKey = KeymapService.domToElectronKey(String.fromCharCode(keyCode)); if (electronKey) parts.push(electronKey); return parts.join('+'); From d19796f14c46e6e5e6005dfb36ee0979a4fa53bb Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 31 Oct 2020 13:02:14 +0000 Subject: [PATCH 6/9] Electron release v1.3.11 --- ElectronClient/package-lock.json | 2 +- ElectronClient/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ElectronClient/package-lock.json b/ElectronClient/package-lock.json index d5173f143c..10a00103bd 100644 --- a/ElectronClient/package-lock.json +++ b/ElectronClient/package-lock.json @@ -1,6 +1,6 @@ { "name": "Joplin", - "version": "1.3.10", + "version": "1.3.11", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/ElectronClient/package.json b/ElectronClient/package.json index fdb0d793d4..4c225f62e9 100644 --- a/ElectronClient/package.json +++ b/ElectronClient/package.json @@ -1,6 +1,6 @@ { "name": "Joplin", - "version": "1.3.10", + "version": "1.3.11", "description": "Joplin for Desktop", "main": "main.js", "scripts": { From 08c2a7ad648726ac622ef10820f326c24d7fd0bd Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 31 Oct 2020 13:05:46 +0000 Subject: [PATCH 7/9] Tools: Improved HTML escape in git-changelog --- Tools/git-changelog.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Tools/git-changelog.js b/Tools/git-changelog.js index c07f632e1e..9e78a2ef93 100644 --- a/Tools/git-changelog.js +++ b/Tools/git-changelog.js @@ -10,12 +10,13 @@ const { execCommand, githubUsername } = require('./tool-utils.js'); // From https://stackoverflow.com/a/6234804/561309 function escapeHtml(unsafe) { + // We only escape <> as this is enough for Markdown return unsafe - .replace(/&/g, '&') + // .replace(/&/g, '&') .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + .replace(/>/g, '>'); + // .replace(/"/g, '"') + // .replace(/'/g, '''); } async function gitLog(sinceTag) { From 4d205897733b8830a8d80964cc3618b84fbdc92c Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 31 Oct 2020 16:29:17 +0000 Subject: [PATCH 8/9] Desktop: Remove from keymap editor commands that cannot be associated with a shortcut --- .eslintignore | 1 + .gitignore | 1 + .ignore | 1 + ElectronClient/app.ts | 12 ++++++--- ElectronClient/gui/MenuBar.tsx | 37 ++------------------------ ElectronClient/gui/menuCommandNames.ts | 37 ++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 ElectronClient/gui/menuCommandNames.ts diff --git a/.eslintignore b/.eslintignore index 30873f65cf..0416f3f970 100644 --- a/.eslintignore +++ b/.eslintignore @@ -126,6 +126,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js ElectronClient/gui/MainScreen/MainScreen.js ElectronClient/gui/MenuBar.js +ElectronClient/gui/menuCommandNames.js ElectronClient/gui/MultiNoteActions.js ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js diff --git a/.gitignore b/.gitignore index 433a570480..f41f7f70a5 100644 --- a/.gitignore +++ b/.gitignore @@ -120,6 +120,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js ElectronClient/gui/MainScreen/MainScreen.js ElectronClient/gui/MenuBar.js +ElectronClient/gui/menuCommandNames.js ElectronClient/gui/MultiNoteActions.js ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js diff --git a/.ignore b/.ignore index 9170d586ea..48a1ecc655 100644 --- a/.ignore +++ b/.ignore @@ -69,6 +69,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js ElectronClient/gui/MainScreen/MainScreen.js ElectronClient/gui/MenuBar.js +ElectronClient/gui/menuCommandNames.js ElectronClient/gui/MultiNoteActions.js ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js diff --git a/ElectronClient/app.ts b/ElectronClient/app.ts index a1077806cd..562cbdc2c8 100644 --- a/ElectronClient/app.ts +++ b/ElectronClient/app.ts @@ -14,6 +14,7 @@ import Setting from 'lib/models/Setting'; import actionApi from 'lib/services/rest/actionApi.desktop'; import BaseApplication from 'lib/BaseApplication'; import { _, setLocale } from 'lib/locale'; +import menuCommandNames from './gui/menuCommandNames'; require('app-module-path').addPath(__dirname); @@ -526,7 +527,9 @@ class Application extends BaseApplication { } const keymapService = KeymapService.instance(); - keymapService.initialize(CommandService.instance().commandNames(true)); + // We only add the commands that appear in the menu because only + // those can have a shortcut associated with them. + keymapService.initialize(menuCommandNames()); try { await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`); @@ -534,9 +537,10 @@ class Application extends BaseApplication { 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. + // 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(); await FoldersScreenUtils.refreshFolders(); diff --git a/ElectronClient/gui/MenuBar.tsx b/ElectronClient/gui/MenuBar.tsx index 01c5cc0f66..49908d0fb8 100644 --- a/ElectronClient/gui/MenuBar.tsx +++ b/ElectronClient/gui/MenuBar.tsx @@ -14,6 +14,7 @@ import InteropServiceHelper from '../InteropServiceHelper'; import { _ } from 'lib/locale'; import { MenuItem, MenuItemLocation } from 'lib/services/plugins/api/types'; import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext'; +import menuCommandNames from './menuCommandNames'; const { connect } = require('react-redux'); const { reg } = require('lib/registry.js'); @@ -86,41 +87,7 @@ interface Props { pluginMenus: any[], } -const commandNames:string[] = [ - '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', - 'historyBackward', - 'historyForward', -]; +const commandNames:string[] = menuCommandNames(); function menuItemSetChecked(id:string, checked:boolean) { const menu = Menu.getApplicationMenu(); diff --git a/ElectronClient/gui/menuCommandNames.ts b/ElectronClient/gui/menuCommandNames.ts new file mode 100644 index 0000000000..af34d9c96b --- /dev/null +++ b/ElectronClient/gui/menuCommandNames.ts @@ -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', + ]; +} From 44462f4d71fb5f26c9c937e557e77481aaf16d58 Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 31 Oct 2020 17:38:54 +0100 Subject: [PATCH 9/9] All: Translation: Update nl_NL.po (#4012) --- CliClient/locales/nl_NL.po | 101 ++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/CliClient/locales/nl_NL.po b/CliClient/locales/nl_NL.po index 774e56c0bb..a420879364 100644 --- a/CliClient/locales/nl_NL.po +++ b/CliClient/locales/nl_NL.po @@ -13,8 +13,10 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\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" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" #: CliClient/app/command-cp.js:13 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)" #: CliClient/app/command-sync.js:35 +#, fuzzy 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 #: ElectronClient/gui/OneDriveLoginScreen.min.js:40 @@ -818,6 +821,8 @@ msgstr "Annuleren" msgid "" "The app is now going to close. Please relaunch it to complete the process." msgstr "" +"De applicatie zal nu afsluiten. Gelieve het weer op te starten om het proces " +"te voltooien." #: ElectronClient/plugins/GotoAnything.min.js:459 msgid "" @@ -835,15 +840,14 @@ msgid "Goto Anything..." msgstr "Ga naar Alles..." #: ElectronClient/plugins/GotoAnything.js:431 -#, fuzzy msgid "" "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 " "commands." msgstr "" "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 " -"naam van een notitieboek." +"springen. Typ # gevolgd door de naam van een label, @ gevolgd door de naam " +"van een notitieboek, of : om te zoeken op commando's." #: ElectronClient/plugins/GotoAnything.js:463 #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:28 @@ -884,9 +888,8 @@ msgid "New version: %s" msgstr "Nieuwe versie: %s" #: ElectronClient/checkForUpdates.js:154 -#, fuzzy msgid "Download" -msgstr "Gedownload" +msgstr "Downloaden" #: ElectronClient/checkForUpdates.js:154 msgid "Full Release Notes" @@ -1235,13 +1238,15 @@ msgstr "Bezig met creëren van nieuw(e) %s..." #: ElectronClient/gui/NoteEditor/NoteEditor.js:379 msgid "The following attachments are being watched for changes:" -msgstr "" +msgstr "De volgende bijlagen worden gecontroleerd op wijzigingen:" #: ElectronClient/gui/NoteEditor/NoteEditor.js:382 msgid "" "The attachments will no longer be watched when you switch to a different " "note." msgstr "" +"De bijlagen worden niet meer gecontroleerd wanneer je naar een andere " +"notitie schakelt." #: ElectronClient/gui/NoteEditor/NoteEditor.js:387 #: ElectronClient/gui/NoteText.min.js:1656 @@ -1358,9 +1363,9 @@ msgstr "Eigenschappen van notitie" #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:63 #: ReactNativeClient/lib/services/KeymapService.js:144 -#, fuzzy, javascript-format +#, javascript-format msgid "Error: %s" -msgstr "Fout" +msgstr "Fout: %s" #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:121 #: ElectronClient/gui/Root.min.js:91 ElectronClient/gui/MenuBar.js:391 @@ -1370,12 +1375,11 @@ msgstr "Importeren" #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:126 msgid "Command" -msgstr "" +msgstr "Commando" #: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:127 -#, fuzzy msgid "Keyboard Shortcut" -msgstr "Toetsenbordmodus" +msgstr "Sneltoets" #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:14 #: ElectronClient/gui/MenuBar.js:178 ElectronClient/app.js:347 @@ -1398,9 +1402,8 @@ msgid "Website and documentation" msgstr "Website en documentatie" #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:24 -#, fuzzy msgid "Hide Joplin" -msgstr "Over Joplin" +msgstr "Joplin verbergen" #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:26 #: ElectronClient/gui/MenuBar.js:428 @@ -1408,9 +1411,8 @@ msgid "Close Window" msgstr "Venster afsluiten" #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30 -#, fuzzy msgid "Preferences" -msgstr "Voorkeuren..." +msgstr "Voorkeuren" #: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30 #: ElectronClient/gui/Root.min.js:92 ElectronClient/gui/MenuBar.js:304 @@ -1420,13 +1422,15 @@ msgstr "Opties" #: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49 msgid "Press the shortcut" -msgstr "" +msgstr "Druk op de sneltoetsen" #: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49 msgid "" "Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the " "shortcut." 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/EncryptionConfigScreen.min.js:96 @@ -1436,17 +1440,20 @@ msgstr "Opslaan" #: ElectronClient/gui/MainScreen/MainScreen.js:418 #: ElectronClient/gui/MainScreen/MainScreen.min.js:301 +#, fuzzy msgid "" "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 " "proceed please click on the link." 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.min.js:306 -#, fuzzy 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.min.js:313 @@ -2268,18 +2275,16 @@ msgstr[0] "Kopieer Deelbare Link" msgstr[1] "Kopieer Deelbare Links" #: ElectronClient/commands/toggleExternalEditing.js:18 -#, fuzzy msgid "Toggle external editing" -msgstr "Klik om extern bewerken te stoppen" +msgstr "Extern bewerken in- of uitschakelen" #: ElectronClient/commands/toggleExternalEditing.js:37 msgid "Stop" -msgstr "" +msgstr "Stop" #: ElectronClient/commands/copyDevCommand.js:18 -#, fuzzy 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 msgid "Stop external editing" @@ -2291,9 +2296,8 @@ msgid "Error opening note in editor: %s" msgstr "Fout bij openen van notitie in editor: %s" #: ElectronClient/commands/openProfileDirectory.js:18 -#, fuzzy msgid "Open profile directory" -msgstr "Open sjabloon map" +msgstr "Open profiel map" #: ElectronClient/app.js:345 #, javascript-format @@ -2339,7 +2343,7 @@ msgstr "" #: ReactNativeClient/lib/SyncTargetAmazonS3.js:28 msgid "AWS S3" -msgstr "" +msgstr "AWS S3" #: ReactNativeClient/lib/SyncTargetDropbox.js:25 msgid "Dropbox" @@ -2483,19 +2487,19 @@ msgstr "WebDAV-wachtwoord" #: ReactNativeClient/lib/models/Setting.js:215 msgid "AWS S3 bucket" -msgstr "" +msgstr "AWS S3 bucket" #: ReactNativeClient/lib/models/Setting.js:226 msgid "AWS S3 URL" -msgstr "" +msgstr "AWS S3 URL" #: ReactNativeClient/lib/models/Setting.js:237 msgid "AWS key" -msgstr "" +msgstr "AWS key" #: ReactNativeClient/lib/models/Setting.js:247 msgid "AWS secret" -msgstr "" +msgstr "AWS secret" #: ReactNativeClient/lib/models/Setting.js:259 msgid "Attachment download behaviour" @@ -2955,9 +2959,8 @@ msgid "Web Clipper" msgstr "Webclipper" #: ReactNativeClient/lib/models/Setting.js:1310 -#, fuzzy msgid "Keyboard Shortcuts" -msgstr "Toetsenbordmodus" +msgstr "Sneltoetsen" #: ReactNativeClient/lib/models/Setting.js:1317 msgid "" @@ -3074,9 +3077,8 @@ msgid "Save alarm" msgstr "Alarm opslaan" #: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28 -#, fuzzy msgid "Open" -msgstr "Openen..." +msgstr "Openen" #: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:29 #: ReactNativeClient/lib/components/screens/Note.js:794 @@ -3114,8 +3116,9 @@ msgstr "" "Sommige items kunnen niet worden gesynchroniseerd. Klik voor meer informatie." #: ReactNativeClient/lib/components/screen-header.js:453 +#, fuzzy 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 #, javascript-format @@ -3239,8 +3242,9 @@ msgid "Refresh" msgstr "Verversen" #: ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js:42 +#, fuzzy msgid "Sync Target Upgrade" -msgstr "" +msgstr "Sync target update" #: ReactNativeClient/lib/components/screens/NoteTagsDialog.js:163 msgid "New tags:" @@ -3484,11 +3488,14 @@ msgid "" "\n" "You may turn off this option at any time in the Configuration screen." 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 -#, fuzzy msgid "Permission needed" -msgstr "Toestemming om de camera te gebruiken" +msgstr "Toestemming vereist" #: ReactNativeClient/lib/components/screens/Note.js:583 #, javascript-format @@ -3737,23 +3744,23 @@ msgstr "Geef een importformaat op voor %s" #: ReactNativeClient/lib/services/KeymapService.js:240 msgid "command" -msgstr "" +msgstr "commando" #: ReactNativeClient/lib/services/KeymapService.js:240 #: ReactNativeClient/lib/services/KeymapService.js:245 #, javascript-format 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:252 msgid "accelerator" -msgstr "" +msgstr "versneller" #: ReactNativeClient/lib/services/KeymapService.js:252 -#, fuzzy, javascript-format +#, javascript-format msgid "Invalid %s: %s." -msgstr "Ongeldig antwoord: %s" +msgstr "Ongeldig %s: %s." #: ReactNativeClient/lib/services/KeymapService.js:270 #, javascript-format @@ -3761,11 +3768,13 @@ msgid "" "Accelerator \"%s\" is used for \"%s\" and \"%s\" commands. This may lead to " "unexpected behaviour." msgstr "" +"Versneller \"%s\" wordt gebruikt voor \"%s\" en \"%s\" commando's. Dit kan " +"tot onverwacht gedrag leiden." #: ReactNativeClient/lib/services/KeymapService.js:295 #, javascript-format msgid "Accelerator \"%s\" is not valid." -msgstr "" +msgstr "Versneller \"%s\" is niet geldig." #: ReactNativeClient/lib/services/report.js:121 msgid "Items that cannot be synchronised"