Skip to content

Commit

Permalink
Avoid extra API call
Browse files Browse the repository at this point in the history
Also fix naming and lint errors.
  • Loading branch information
eloyrobillard committed Feb 19, 2024
1 parent d97edb5 commit cf514b2
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 32 deletions.
54 changes: 29 additions & 25 deletions ext/js/background/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -537,86 +537,90 @@ export class Backend {
* It further sets the `isDuplicate` strings for notes that have a duplicate
* but also have the `allowDuplicate` option on.
* @param {import('anki').Note[]} notes
* @returns {Promise<{ canAddArray: { note: import('anki').Note, isDuplicate: boolean }[], cannotAdd: import('anki').Note[] }>}
* @returns {Promise<{ canAddArray: { note: import('anki').Note, isDuplicate: boolean }[], cannotAddArray: import('anki').Note[] }>}
*/
async partitionAddibleNotes(notes) {
// HACK to figure out which notes have are duplicates
// We compare which notes with `allowDuplicate` on get rejected when `allowDuplicate` is off.
const canAddIfDuplicatesAllowed = await this._anki.canAddNotes(notes);
const notesNoDuplicatesAllowed = notes.map((note) => ({...note, options: {...note.options, allowDuplicate: false}}));
const canAddIfNoDuplicatesAllowed = await this._anki.canAddNotes(notesNoDuplicatesAllowed);

// Check all notes right away to avoid calling Anki twice
const canAddResult = await this._anki.canAddNotes([...notes, ...notesNoDuplicatesAllowed]);
const canAddIfDuplicatesAllowed = canAddResult.slice(0, notes.length);
const canAddIfNoDuplicatesAllowed = canAddResult.slice(notes.length);

/** @type {{ note: import('anki').Note, isDuplicate: boolean }[]} */
const canAddArray = [];

/** @type {import('anki').Note[]} */
const cannotAdd = [];
const cannotAddArray = [];

canAddIfDuplicatesAllowed.forEach((canAdd, i) => {
if (canAdd !== canAddIfNoDuplicatesAllowed[i]) {
for (let i = 0; i < canAddIfDuplicatesAllowed.length; i++) {
if (canAddIfDuplicatesAllowed[i] !== canAddIfNoDuplicatesAllowed[i]) {
canAddArray.push({note: notes[i], isDuplicate: true});
} else if (canAdd) {
} else if (canAddIfDuplicatesAllowed[i]) {
canAddArray.push({note: notes[i], isDuplicate: false});
} else {
cannotAdd.push(notes[i]);
cannotAddArray.push(notes[i]);
}
});
}

return {canAddArray, cannotAdd};
return {canAddArray, cannotAddArray};
}

/** @type {import('api').ApiHandler<'getAnkiNoteInfo'>} */
async _onApiGetAnkiNoteInfo({notes, fetchAdditionalInfo}) {
// duplicateCards will only have elements if some cards have allowDuplicate == true
const {canAddArray, cannotAdd} = await this.partitionAddibleNotes(notes);
const {canAddArray, cannotAddArray} = await this.partitionAddibleNotes(notes);

/** @type {{note: import('anki').Note, info: import('anki').NoteInfoWrapper}[]} */
const cannotAddInfo = cannotAdd.filter((note) => isNoteDataValid(note)).map((note) => ({note, info: {canAdd: false, valid: false, noteIds: null}}));
const cannotAdd = cannotAddArray.filter((note) => isNoteDataValid(note)).map((note) => ({note, info: {canAdd: false, valid: false, noteIds: null}}));

/** @type {import('anki').NoteInfoWrapper[]} */
const results = cannotAddInfo.map(({info}) => info);
const results = cannotAdd.map(({info}) => info);

/** @type {import('anki').Note[]} */
const duplicateNotes = [];

/** @type {number[]} */
const indices = [];
const originalIndices = [];

canAddArray.forEach((noteInfo, i) => {
if (noteInfo.isDuplicate) {
duplicateNotes.push(noteInfo.note);
indices.push(i);
for (let i = 0; i < canAddArray.length; i++) {
if (canAddArray[i].isDuplicate) {
duplicateNotes.push(canAddArray[i].note);
// Keep original indices to locate duplicate inside `duplicateNoteIds`
originalIndices.push(i);
}
});
}

const duplicateNoteIdsArray = await this._anki.findNoteIds(duplicateNotes);
const duplicateNoteIds = await this._anki.findNoteIds(duplicateNotes);

for (let i = 0; i < canAddArray.length; ++i) {
const {note, isDuplicate} = canAddArray[i];

const valid = isNoteDataValid(note);

const info = {canAdd: valid, valid, noteIds: isDuplicate ? duplicateNoteIdsArray[indices.indexOf(i)] : null};
const info = {canAdd: valid, valid, noteIds: isDuplicate ? duplicateNoteIds[originalIndices.indexOf(i)] : null};

results.push(info);

if (!valid) {
cannotAddInfo.push({note, info});
cannotAdd.push({note, info});
}
}

if (cannotAdd.length > 0) {
const cannotAddNotes = cannotAddInfo.map(({note}) => note);
const cannotAddNotes = cannotAdd.map(({note}) => note);
const noteIdsArray = await this._anki.findNoteIds(cannotAddNotes);

for (let i = 0, ii = Math.min(cannotAdd.length, noteIdsArray.length); i < ii; ++i) {
const noteIds = noteIdsArray[i];

if (noteIds.length > 0) {
cannotAddInfo[i].info.noteIds = noteIds;
cannotAdd[i].info.noteIds = noteIds;

if (fetchAdditionalInfo) {
cannotAddInfo[i].info.noteInfos = await this._anki.notesInfo(noteIds);
cannotAdd[i].info.noteInfos = await this._anki.notesInfo(noteIds);
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions ext/js/display/display-anki.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ export class DisplayAnki {
allErrors.push(toError(e));
}
}
// disable add button when adding note, unless duplicates are allowed
// Disable add button when adding note, unless duplicates are allowed
button.disabled = !this._allowDuplicate;
this._updateViewNoteButton(dictionaryEntryIndex, [noteId], true);
}
Expand Down Expand Up @@ -642,18 +642,16 @@ export class DisplayAnki {
let infos;
let ankiError = null;
try {
// force if we are not checking for duplicates
// Force if we are not checking for duplicates
// or if the "Enable add buttons" option is on
if (!this._checkForDuplicates || this._allowDuplicate) {
if (!await this._display.application.api.isAnkiConnected()) {
throw new Error('Anki not connected');
}

if (this._checkForDuplicates) {
infos = await this._display.application.api.getAnkiNoteInfo(notes, fetchAdditionalInfo);
} else {
infos = this._getAnkiNoteInfoForceValue(notes, true);
}
infos = this._checkForDuplicates ?
await this._display.application.api.getAnkiNoteInfo(notes, fetchAdditionalInfo) :
this._getAnkiNoteInfoForceValue(notes, true);
} else {
infos = await this._display.application.api.getAnkiNoteInfo(notes, fetchAdditionalInfo);
}
Expand Down

0 comments on commit cf514b2

Please sign in to comment.