diff --git a/CliClient/app/app-gui.js b/CliClient/app/app-gui.js index d69238ba9..ed0f0082e 100644 --- a/CliClient/app/app-gui.js +++ b/CliClient/app/app-gui.js @@ -21,6 +21,7 @@ const WindowWidget = require('tkwidgets/WindowWidget.js'); const NoteWidget = require('./gui/NoteWidget.js'); const FolderListWidget = require('./gui/FolderListWidget.js'); +const NoteListWidget = require('./gui/NoteListWidget.js'); class AppGui { @@ -49,7 +50,7 @@ class AppGui { this.inputMode_ = AppGui.INPUT_MODE_NORMAL; - this.currentShortcutKeys_ = ''; + this.currentShortcutKeys_ = []; this.lastShortcutKeyTime_ = 0; } @@ -75,15 +76,7 @@ class AppGui { }; }); - const noteList = new ListWidget(); - noteList.items = []; - noteList.itemRenderer = (note) => { - let label = note.title; - if (note.is_todo) { - label = '[' + (note.todo_completed ? 'X' : ' ') + '] ' + label; - } - return label; - }; + const noteList = new NoteListWidget(); noteList.name = 'noteList'; noteList.vStretch = true; noteList.style = { @@ -329,6 +322,7 @@ class AppGui { this.widget('noteText').text = text; } + // Any key after which a shortcut is not possible. isSpecialKey(name) { return ['ENTER', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'DELETE', 'BACKSPACE', 'ESCAPE', 'TAB', 'SHIFT_TAB'].indexOf(name) >= 0; } @@ -353,26 +347,33 @@ class AppGui { process.exit(); return; } - + const now = (new Date()).getTime(); - if (now - this.lastShortcutKeyTime_ > 1000 || this.isSpecialKey(name)) { - this.currentShortcutKeys_ = name; + if (now - this.lastShortcutKeyTime_ > 800 || this.isSpecialKey(name)) { + this.currentShortcutKeys_ = [name]; } else { - this.currentShortcutKeys_ += name; + // If the previous key was a special key (eg. up, down arrow), this new key + // starts a new shortcut. + if (this.currentShortcutKeys_.length && this.isSpecialKey(this.currentShortcutKeys_[0])) { + this.currentShortcutKeys_ = [name]; + } else { + this.currentShortcutKeys_.push(name); + } } this.lastShortcutKeyTime_ = now; // Don't process shortcut keys if the console is active, except if the shortcut // starts with CTRL (eg. CTRL+J CTRL+Z to maximize the console window). - if (!consoleWidget.hasFocus || this.currentShortcutKeys_.indexOf('CTRL') === 0) { - this.logger().debug('Now: ' + name + ', Keys: ' + this.currentShortcutKeys_); + if (!consoleWidget.hasFocus || (this.currentShortcutKeys_.length && this.currentShortcutKeys_[0].indexOf('CTRL') === 0)) { + this.logger().debug('Now: ' + name + ', Keys: ', this.currentShortcutKeys_); - if (this.currentShortcutKeys_ in this.shortcuts_) { - const cmd = this.shortcuts_[this.currentShortcutKeys_].action; + const shortcutKey = this.currentShortcutKeys_.join(''); + if (shortcutKey in this.shortcuts_) { + const cmd = this.shortcuts_[shortcutKey].action; if (!cmd.isDocOnly) { - this.currentShortcutKeys_ = ''; + this.currentShortcutKeys_ = []; if (typeof cmd === 'function') { cmd(); } else { diff --git a/CliClient/app/app.js b/CliClient/app/app.js index 9244537e6..c3a87f92a 100644 --- a/CliClient/app/app.js +++ b/CliClient/app/app.js @@ -408,7 +408,7 @@ class Application { generalMiddleware() { const middleware = store => next => async (action) => { - this.logger().info('Middleware reducer action', action.type); + this.logger().info('Reducer action', action.type); const result = next(action); const newState = store.getState(); diff --git a/CliClient/app/base-command.js b/CliClient/app/base-command.js index 62c6bca55..c4f27ca0f 100644 --- a/CliClient/app/base-command.js +++ b/CliClient/app/base-command.js @@ -1,4 +1,5 @@ import { _ } from 'lib/locale.js'; +import { reg } from 'lib/registry.js'; class BaseCommand { @@ -75,6 +76,10 @@ class BaseCommand { }; } + logger() { + return reg.logger(); + } + } export { BaseCommand }; \ No newline at end of file diff --git a/CliClient/app/gui/NoteListWidget.js b/CliClient/app/gui/NoteListWidget.js index a8645c8d0..f103e1656 100644 --- a/CliClient/app/gui/NoteListWidget.js +++ b/CliClient/app/gui/NoteListWidget.js @@ -6,20 +6,32 @@ class NoteListWidget extends ListWidget { constructor() { super(); this.selectedNoteId_ = 0; - this.itemRenderer = (item) => { - return item.title; - }; - } - get selectedNoteId() { - return this.selectedNoteId_; + this.updateIndexFromSelectedNoteId_ = false; + + this.itemRenderer = (note) => { + let label = note.title + ' ' + note.id; + if (note.is_todo) { + label = '[' + (note.todo_completed ? 'X' : ' ') + '] ' + label; + } + return label; + }; } set selectedNoteId(v) { if (v === this.selectedNoteId_) return; + this.updateIndexFromSelectedNoteId_ = true; this.selectedNoteId_ = v; - const index = this.itemIndexByKey('id', this.selectedNoteId_); - this.currentIndex = index >= 0 ? index : 0; + } + + render() { + if (this.updateIndexFromSelectedNoteId_) { + const index = this.itemIndexByKey('id', this.selectedNoteId_); + this.currentIndex = index >= 0 ? index : 0; + this.updateIndexFromSelectedNoteId_ = false; + } + + super.render(); } } diff --git a/ReactNativeClient/lib/models/folder.js b/ReactNativeClient/lib/models/folder.js index 8d895a326..60a10497c 100644 --- a/ReactNativeClient/lib/models/folder.js +++ b/ReactNativeClient/lib/models/folder.js @@ -77,6 +77,12 @@ class Folder extends BaseItem { }); } + static batchDelete(ids, options = null) { + for (let i = 0; i < ids.length; i++) { + this.delete(ids[i], options); + } + } + static conflictFolderTitle() { return _('Conflicts'); } diff --git a/ReactNativeClient/lib/models/note.js b/ReactNativeClient/lib/models/note.js index bcae8f535..4ae8f9248 100644 --- a/ReactNativeClient/lib/models/note.js +++ b/ReactNativeClient/lib/models/note.js @@ -345,6 +345,17 @@ class Note extends BaseItem { }); } + static batchDelete(ids, options = null) { + const result = super.batchDelete(ids, options); + for (let i = 0; i < ids.length; i++) { + this.dispatch({ + type: 'NOTES_DELETE', + noteId: ids[i], + }); + } + return result; + } + } Note.updateGeolocationEnabled_ = true; diff --git a/ReactNativeClient/lib/reducer.js b/ReactNativeClient/lib/reducer.js index faa5fbb87..1f4b353f4 100644 --- a/ReactNativeClient/lib/reducer.js +++ b/ReactNativeClient/lib/reducer.js @@ -191,15 +191,25 @@ const reducer = (state = defaultState, action) => { case 'NOTES_DELETE': + var previousIndex = 0; var newNotes = []; for (let i = 0; i < state.notes.length; i++) { let f = state.notes[i]; - if (f.id == action.noteId) continue; + if (f.id == action.noteId) { + previousIndex = i; + continue; + } newNotes.push(f); } newState = Object.assign({}, state); newState.notes = newNotes; + + if (previousIndex >= newNotes.length) { + previousIndex = newNotes.length - 1; + } + + newState.selectedNoteId = previousIndex >= 0 ? newNotes[previousIndex].id : null; break; case 'FOLDERS_UPDATE_ALL':