aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEloy Robillard <eloy.robillard@gmail.com>2024-02-15 04:02:36 +0100
committerGitHub <noreply@github.com>2024-02-15 03:02:36 +0000
commit7a4096240ce4faf70a785d047945388baa0daab3 (patch)
tree841da38533391a095fdb932c528c4b5c3366ba1b
parent043ac79203abbc9c7f3aa971e2de8ddedb3c0e90 (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.json2
-rw-r--r--ext/display-templates.html4
-rw-r--r--ext/js/background/backend.js13
-rw-r--r--ext/js/comm/anki-connect.js8
-rw-r--r--ext/js/comm/api.js12
-rw-r--r--ext/js/data/options-util.js17
-rw-r--r--ext/js/display/display-anki.js46
-rw-r--r--ext/js/pages/settings/anki-controller.js2
-rw-r--r--ext/js/pages/settings/keyboard-shortcuts-controller.js2
-rw-r--r--ext/settings.html2
-rw-r--r--test/options-util.test.js4
-rw-r--r--types/ext/api.d.ts4
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;
};