diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 323219a4..f04006bc 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -17,6 +17,7 @@ /* global * AnkiConnect + * AnkiUtil * AudioDownloader * ClipboardMonitor * ClipboardReader @@ -456,10 +457,12 @@ class Backend { for (let i = 0; i < notes.length; ++i) { const note = notes[i]; - const canAdd = canAddArray[i]; - const info = {canAdd, noteIds: null}; + let canAdd = canAddArray[i]; + const valid = AnkiUtil.isNoteDataValid(note); + if (!valid) { canAdd = false; } + const info = {canAdd, valid, noteIds: null}; results.push(info); - if (!canAdd) { + if (!canAdd && valid) { cannotAdd.push({note, info}); } } diff --git a/ext/js/data/anki-util.js b/ext/js/data/anki-util.js index fc081ddc..c815ccc7 100644 --- a/ext/js/data/anki-util.js +++ b/ext/js/data/anki-util.js @@ -81,6 +81,21 @@ class AnkiUtil { static cloneFieldMarkerPattern(global) { return new RegExp(this._markerPattern.source, global ? 'g' : ''); } + + /** + * Checks whether or not a note object is valid. + * @param note A note object to check. + * @return `true` if the note is valid, `false` otherwise. + */ + static isNoteDataValid(note) { + if (!isObject(note)) { return false; } + const {fields, deckName, modelName} = note; + return ( + typeof deckName === 'string' && + typeof modelName === 'string' && + Object.entries(fields).length > 0 + ); + } } // eslint-disable-next-line no-underscore-dangle diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 0e029748..30a8e294 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -1058,15 +1058,9 @@ class Display extends EventDispatcher { const modes = isTerms ? ['term-kanji', 'term-kana'] : ['kanji']; let states; try { - if (this._options.anki.checkForDuplicates) { - const noteContext = this._getNoteContext(); - states = await this._areDefinitionsAddable(definitions, modes, noteContext); - } else { - if (!await yomichan.api.isAnkiConnected()) { - throw new Error('Anki not connected'); - } - states = this._areDefinitionsAddableForcedValue(definitions, modes, true); - } + const noteContext = this._getNoteContext(); + const {checkForDuplicates} = this._options.anki; + states = await this._areDefinitionsAddable(definitions, modes, noteContext, checkForDuplicates ? null : true); } catch (e) { return; } @@ -1408,7 +1402,7 @@ class Display extends EventDispatcher { return templates; } - async _areDefinitionsAddable(definitions, modes, context) { + async _areDefinitionsAddable(definitions, modes, context, forceCanAddValue) { const modeCount = modes.length; const notePromises = []; for (const definition of definitions) { @@ -1419,7 +1413,16 @@ class Display extends EventDispatcher { } const notes = await Promise.all(notePromises); - const infos = await yomichan.api.getAnkiNoteInfo(notes); + let infos; + if (forceCanAddValue !== null) { + if (!await yomichan.api.isAnkiConnected()) { + throw new Error('Anki not connected'); + } + infos = this._getAnkiNoteInfoForceValue(notes, forceCanAddValue); + } else { + infos = await yomichan.api.getAnkiNoteInfo(notes); + } + const results = []; for (let i = 0, ii = infos.length; i < ii; i += modeCount) { results.push(infos.slice(i, i + modeCount)); @@ -1427,16 +1430,11 @@ class Display extends EventDispatcher { return results; } - _areDefinitionsAddableForcedValue(definitions, modes, canAdd) { + _getAnkiNoteInfoForceValue(notes, canAdd) { const results = []; - const definitionCount = definitions.length; - const modeCount = modes.length; - for (let i = 0; i < definitionCount; ++i) { - const modeArray = []; - for (let j = 0; j < modeCount; ++j) { - modeArray.push({canAdd, noteIds: null}); - } - results.push(modeArray); + for (const note of notes) { + const valid = AnkiUtil.isNoteDataValid(note); + results.push({canAdd, valid, noteIds: null}); } return results; }