diff options
author | Eloy Robillard <eloy.robillard@gmail.com> | 2024-02-15 04:02:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-15 03:02:36 +0000 |
commit | 7a4096240ce4faf70a785d047945388baa0daab3 (patch) | |
tree | 841da38533391a095fdb932c528c4b5c3366ba1b | |
parent | 043ac79203abbc9c7f3aa971e2de8ddedb3c0e90 (diff) |
Show all duplicate notes on click (#636)
* Show all duplicate notes on click
* Change 'notesView' to 'viewNotes'
* Replace api.noteView with api.viewNotes
* Update "viewNote" action to "viewNotes" in options util
* Update options-util test with "viewNotes" action
* Replace uses of "viewNote" with "viewNotes"
* Replace _viewNote with _viewNotes
* Rename /*ViewNote*/ methods to /*ViewNotes*/
-rw-r--r-- | ext/data/schemas/options-schema.json | 2 | ||||
-rw-r--r-- | ext/display-templates.html | 4 | ||||
-rw-r--r-- | ext/js/background/backend.js | 13 | ||||
-rw-r--r-- | ext/js/comm/anki-connect.js | 8 | ||||
-rw-r--r-- | ext/js/comm/api.js | 12 | ||||
-rw-r--r-- | ext/js/data/options-util.js | 17 | ||||
-rw-r--r-- | ext/js/display/display-anki.js | 46 | ||||
-rw-r--r-- | ext/js/pages/settings/anki-controller.js | 2 | ||||
-rw-r--r-- | ext/js/pages/settings/keyboard-shortcuts-controller.js | 2 | ||||
-rw-r--r-- | ext/settings.html | 2 | ||||
-rw-r--r-- | test/options-util.test.js | 4 | ||||
-rw-r--r-- | types/ext/api.d.ts | 4 |
12 files changed, 70 insertions, 46 deletions
diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index 24f3a6b0..ea7caf0f 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -1187,7 +1187,7 @@ {"action": "addNoteTermKanji", "argument": "", "key": "KeyE", "modifiers": ["alt"], "scopes": ["popup", "search"], "enabled": true}, {"action": "addNoteTermKana", "argument": "", "key": "KeyR", "modifiers": ["alt"], "scopes": ["popup", "search"], "enabled": true}, {"action": "playAudio", "argument": "", "key": "KeyP", "modifiers": ["alt"], "scopes": ["popup", "search"], "enabled": true}, - {"action": "viewNote", "argument": "", "key": "KeyV", "modifiers": ["alt"], "scopes": ["popup", "search"], "enabled": true}, + {"action": "viewNotes", "argument": "", "key": "KeyV", "modifiers": ["alt"], "scopes": ["popup", "search"], "enabled": true}, {"action": "copyHostSelection", "argument": "", "key": "KeyC", "modifiers": ["ctrl"], "scopes": ["popup"], "enabled": true} ] } diff --git a/ext/display-templates.html b/ext/display-templates.html index a50cea3b..caf5920c 100644 --- a/ext/display-templates.html +++ b/ext/display-templates.html @@ -8,7 +8,7 @@ <button type="button" class="action-button action-button-collapsible" data-action="view-tags" hidden disabled> <span class="action-icon icon" data-icon="tag"></span> </button> - <button type="button" class="action-button" data-action="view-note" hidden disabled title="View added note" data-hotkey='["viewNote","title","View added note ({0})"]' data-menu-position="left below h-cover v-cover"> + <button type="button" class="action-button" data-action="view-note" hidden disabled title="View added note" data-hotkey='["viewNotes","title","View added note ({0})"]' data-menu-position="left below h-cover v-cover"> <span class="action-icon icon color-icon" data-icon="view-note"></span> <span class="action-button-badge icon" hidden></span> </button> @@ -111,7 +111,7 @@ <div class="entry-current-indicator" title="Current entry"><span class="entry-current-indicator-inner"></span></div> <div class="entry-header"> <div class="actions"> - <button type="button" class="action-button" data-action="view-note" hidden disabled title="View added note" data-hotkey='["viewNote","title","View added note ({0})"]'> + <button type="button" class="action-button" data-action="view-note" hidden disabled title="View added note" data-hotkey='["viewNotes","title","View added note ({0})"]'> <span class="action-icon icon color-icon" data-icon="view-note"></span> </button> <button type="button" class="action-button" data-action="add-note" hidden disabled data-mode="kanji" title="Add kanji" data-hotkey='["addNoteKanji","title","Add kanji ({0})"]'> diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 8b5e8383..090ba7b3 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -154,7 +154,7 @@ export class Backend { ['addAnkiNote', this._onApiAddAnkiNote.bind(this)], ['getAnkiNoteInfo', this._onApiGetAnkiNoteInfo.bind(this)], ['injectAnkiNoteMedia', this._onApiInjectAnkiNoteMedia.bind(this)], - ['noteView', this._onApiNoteView.bind(this)], + ['viewNotes', this._onApiViewNotes.bind(this)], ['suspendAnkiCardsForNote', this._onApiSuspendAnkiCardsForNote.bind(this)], ['commandExec', this._onApiCommandExec.bind(this)], ['getTermAudioInfoList', this._onApiGetTermAudioInfoList.bind(this)], @@ -580,11 +580,11 @@ export class Backend { ); } - /** @type {import('api').ApiHandler<'noteView'>} */ - async _onApiNoteView({noteId, mode, allowFallback}) { - if (mode === 'edit') { + /** @type {import('api').ApiHandler<'viewNotes'>} */ + async _onApiViewNotes({noteIds, mode, allowFallback}) { + if (noteIds.length === 1 && mode === 'edit') { try { - await this._anki.guiEditNote(noteId); + await this._anki.guiEditNote(noteIds[0]); return 'edit'; } catch (e) { if (!(e instanceof Error && this._anki.isErrorUnsupportedAction(e))) { @@ -594,8 +594,7 @@ export class Backend { } } } - // Fallback - await this._anki.guiBrowseNote(noteId); + await this._anki.guiBrowseNotes(noteIds); return 'browse'; } diff --git a/ext/js/comm/anki-connect.js b/ext/js/comm/anki-connect.js index 7cb2d071..0bf38bda 100644 --- a/ext/js/comm/anki-connect.js +++ b/ext/js/comm/anki-connect.js @@ -201,6 +201,14 @@ export class AnkiConnect { } /** + * @param {import('anki').NoteId[]} noteIds + * @returns {Promise<import('anki').CardId[]>} + */ + async guiBrowseNotes(noteIds) { + return await this.guiBrowse(`nid:${noteIds.join(',')}`); + } + + /** * Opens the note editor GUI. * @param {import('anki').NoteId} noteId The ID of the note. * @returns {Promise<void>} Nothing is returned. diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index 2e1e8826..b4fdbeb5 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -118,13 +118,13 @@ export class API { } /** - * @param {import('api').ApiParam<'noteView', 'noteId'>} noteId - * @param {import('api').ApiParam<'noteView', 'mode'>} mode - * @param {import('api').ApiParam<'noteView', 'allowFallback'>} allowFallback - * @returns {Promise<import('api').ApiReturn<'noteView'>>} + * @param {import('api').ApiParam<'viewNotes', 'noteIds'>} noteIds + * @param {import('api').ApiParam<'viewNotes', 'mode'>} mode + * @param {import('api').ApiParam<'viewNotes', 'allowFallback'>} allowFallback + * @returns {Promise<import('api').ApiReturn<'viewNotes'>>} */ - noteView(noteId, mode, allowFallback) { - return this._invoke('noteView', {noteId, mode, allowFallback}); + viewNotes(noteIds, mode, allowFallback) { + return this._invoke('viewNotes', {noteIds, mode, allowFallback}); } /** diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index 312c6efc..1644df2f 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -521,7 +521,8 @@ export class OptionsUtil { this._updateVersion21, this._updateVersion22, this._updateVersion23, - this._updateVersion24 + this._updateVersion24, + this._updateVersion25 ]; /* eslint-enable @typescript-eslint/unbound-method */ if (typeof targetVersion === 'number' && targetVersion < result.length) { @@ -1140,6 +1141,20 @@ export class OptionsUtil { } /** + * - Change 'viewNote' action to 'viewNotes'. + * @type {import('options-util').UpdateFunction} + */ + async _updateVersion25(options) { + for (const profile of options.profiles) { + for (const hotkey of profile.options.inputs.hotkeys) { + if (hotkey.action === 'viewNote') { + hotkey.action = 'viewNotes'; + } + } + } + } + + /** * @param {string} url * @returns {Promise<chrome.tabs.Tab>} */ diff --git a/ext/js/display/display-anki.js b/ext/js/display/display-anki.js index 665521dd..4766f1ae 100644 --- a/ext/js/display/display-anki.js +++ b/ext/js/display/display-anki.js @@ -99,11 +99,11 @@ export class DisplayAnki { /** @type {(event: MouseEvent) => void} */ this._onNoteAddBind = this._onNoteAdd.bind(this); /** @type {(event: MouseEvent) => void} */ - this._onViewNoteButtonClickBind = this._onViewNoteButtonClick.bind(this); + this._onViewNotesButtonClickBind = this._onViewNotesButtonClick.bind(this); /** @type {(event: MouseEvent) => void} */ - this._onViewNoteButtonContextMenuBind = this._onViewNoteButtonContextMenu.bind(this); + this._onViewNotesButtonContextMenuBind = this._onViewNotesButtonContextMenu.bind(this); /** @type {(event: import('popup-menu').MenuCloseEvent) => void} */ - this._onViewNoteButtonMenuCloseBind = this._onViewNoteButtonMenuClose.bind(this); + this._onViewNotesButtonMenuCloseBind = this._onViewNotesButtonMenuClose.bind(this); } /** */ @@ -114,7 +114,7 @@ export class DisplayAnki { ['addNoteKanji', () => { this._tryAddAnkiNoteForSelectedEntry('kanji'); }], ['addNoteTermKanji', () => { this._tryAddAnkiNoteForSelectedEntry('term-kanji'); }], ['addNoteTermKana', () => { this._tryAddAnkiNoteForSelectedEntry('term-kana'); }], - ['viewNote', this._viewNoteForSelectedEntry.bind(this)] + ['viewNotes', this._viewNotesForSelectedEntry.bind(this)] ]); /* eslint-enable @stylistic/no-multi-spaces */ this._display.on('optionsUpdated', this._onOptionsUpdated.bind(this)); @@ -249,9 +249,9 @@ export class DisplayAnki { eventListeners.addEventListener(node, 'click', this._onNoteAddBind); } for (const node of element.querySelectorAll('.action-button[data-action=view-note]')) { - eventListeners.addEventListener(node, 'click', this._onViewNoteButtonClickBind); - eventListeners.addEventListener(node, 'contextmenu', this._onViewNoteButtonContextMenuBind); - eventListeners.addEventListener(node, 'menuClose', this._onViewNoteButtonMenuCloseBind); + eventListeners.addEventListener(node, 'click', this._onViewNotesButtonClickBind); + eventListeners.addEventListener(node, 'contextmenu', this._onViewNotesButtonContextMenuBind); + eventListeners.addEventListener(node, 'menuClose', this._onViewNotesButtonMenuCloseBind); } } @@ -777,34 +777,34 @@ export class DisplayAnki { /** * @param {MouseEvent} e */ - _onViewNoteButtonClick(e) { + _onViewNotesButtonClick(e) { const element = /** @type {HTMLElement} */ (e.currentTarget); e.preventDefault(); if (e.shiftKey) { - this._showViewNoteMenu(element); + this._showViewNotesMenu(element); } else { - this._viewNote(element); + this._viewNotes(element); } } /** * @param {MouseEvent} e */ - _onViewNoteButtonContextMenu(e) { + _onViewNotesButtonContextMenu(e) { const element = /** @type {HTMLElement} */ (e.currentTarget); e.preventDefault(); - this._showViewNoteMenu(element); + this._showViewNotesMenu(element); } /** * @param {import('popup-menu').MenuCloseEvent} e */ - _onViewNoteButtonMenuClose(e) { + _onViewNotesButtonMenuClose(e) { const {detail: {action, item}} = e; switch (action) { - case 'viewNote': + case 'viewNotes': if (item !== null) { - this._viewNote(item); + this._viewNotes(item); } break; } @@ -848,11 +848,11 @@ export class DisplayAnki { /** * @param {HTMLElement} node */ - async _viewNote(node) { + async _viewNotes(node) { const noteIds = this._getNodeNoteIds(node); if (noteIds.length === 0) { return; } try { - await this._display.application.api.noteView(noteIds[0], this._noteGuiMode, false); + await this._display.application.api.viewNotes(noteIds, this._noteGuiMode, false); } catch (e) { const displayErrors = ( toError(e).message === 'Mode not supported' ? @@ -867,7 +867,7 @@ export class DisplayAnki { /** * @param {HTMLElement} node */ - _showViewNoteMenu(node) { + _showViewNotesMenu(node) { const noteIds = this._getNodeNoteIds(node); if (noteIds.length === 0) { return; } @@ -883,7 +883,7 @@ export class DisplayAnki { /** @type {Element} */ const label = querySelectorNotNull(item, '.popup-menu-item-label'); label.textContent = `Note ${i + 1}: ${noteId}`; - item.dataset.menuAction = 'viewNote'; + item.dataset.menuAction = 'viewNotes'; item.dataset.noteIds = `${noteId}`; menuBodyNode.appendChild(item); } @@ -920,12 +920,14 @@ export class DisplayAnki { return entry !== null ? entry.querySelector('.action-button[data-action=view-note]') : null; } - /** */ - _viewNoteForSelectedEntry() { + /** + * Shows notes for selected pop-up entry when "View Notes" hotkey is used. + */ + _viewNotesForSelectedEntry() { const index = this._display.selectedIndex; const button = this._getViewNoteButton(index); if (button !== null) { - this._viewNote(button); + this._viewNotes(button); } } diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js index a2948eda..3a6345ed 100644 --- a/ext/js/pages/settings/anki-controller.js +++ b/ext/js/pages/settings/anki-controller.js @@ -455,7 +455,7 @@ export class AnkiController { throw new Error('Could not find a note to test with'); } - await this._settingsController.application.api.noteView(noteId, mode, false); + await this._settingsController.application.api.viewNotes([noteId], mode, false); } /** diff --git a/ext/js/pages/settings/keyboard-shortcuts-controller.js b/ext/js/pages/settings/keyboard-shortcuts-controller.js index 24d34d5b..7b28a322 100644 --- a/ext/js/pages/settings/keyboard-shortcuts-controller.js +++ b/ext/js/pages/settings/keyboard-shortcuts-controller.js @@ -62,7 +62,7 @@ export class KeyboardShortcutController { ['addNoteKanji', {scopes: new Set(['popup', 'search'])}], ['addNoteTermKanji', {scopes: new Set(['popup', 'search'])}], ['addNoteTermKana', {scopes: new Set(['popup', 'search'])}], - ['viewNote', {scopes: new Set(['popup', 'search'])}], + ['viewNotes', {scopes: new Set(['popup', 'search'])}], ['playAudio', {scopes: new Set(['popup', 'search'])}], ['playAudioFromSource', {scopes: new Set(['popup', 'search']), argument: {template: 'hotkey-argument-audio-source', default: 'jpod101'}}], ['copyHostSelection', {scopes: new Set(['popup'])}], diff --git a/ext/settings.html b/ext/settings.html index 7cddaf32..91d6d7b9 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -3878,7 +3878,7 @@ <option value="addNoteKanji">Add kanji note</option> <option value="addNoteTermKanji">Add term note</option> <option value="addNoteTermKana">Add term note (reading)</option> - <option value="viewNote">View note</option> + <option value="viewNotes">View notes</option> <option value="playAudio">Play audio</option> <option value="playAudioFromSource">Play audio from source</option> <option value="copyHostSelection">Copy host window selection</option> diff --git a/test/options-util.test.js b/test/options-util.test.js index 78ac0009..c2050c3f 100644 --- a/test/options-util.test.js +++ b/test/options-util.test.js @@ -491,7 +491,7 @@ function createProfileOptionsUpdatedTestData1() { {action: 'addNoteTermKanji', argument: '', key: 'KeyE', modifiers: ['alt'], scopes: ['popup', 'search'], enabled: true}, {action: 'addNoteTermKana', argument: '', key: 'KeyR', modifiers: ['alt'], scopes: ['popup', 'search'], enabled: true}, {action: 'playAudio', argument: '', key: 'KeyP', modifiers: ['alt'], scopes: ['popup', 'search'], enabled: true}, - {action: 'viewNote', argument: '', key: 'KeyV', modifiers: ['alt'], scopes: ['popup', 'search'], enabled: true}, + {action: 'viewNotes', argument: '', key: 'KeyV', modifiers: ['alt'], scopes: ['popup', 'search'], enabled: true}, {action: 'copyHostSelection', argument: '', key: 'KeyC', modifiers: ['ctrl'], scopes: ['popup'], enabled: true} ] /* eslint-enable @stylistic/no-multi-spaces */ @@ -604,7 +604,7 @@ function createOptionsUpdatedTestData1() { } ], profileCurrent: 0, - version: 24, + version: 25, global: { database: { prefixWildcardsSupported: false diff --git a/types/ext/api.d.ts b/types/ext/api.d.ts index 4f1b9026..1f4fc0a9 100644 --- a/types/ext/api.d.ts +++ b/types/ext/api.d.ts @@ -193,9 +193,9 @@ type ApiSurface = { errors: Core.SerializedError[]; }; }; - noteView: { + viewNotes: { params: { - noteId: Anki.NoteId; + noteIds: Anki.NoteId[]; mode: Settings.AnkiNoteGuiMode; allowFallback: boolean; }; |