Add note errors (#1329)
* Update _addAnkiNote to track errors * Change comparison * Update anki note adding to show errors * Fix template * Show errors when Anki card creation behaves unexpectedly * Update some errors related to anki media injection * Update addAnkiNote error handling * Improve Anki errors * Simplify error messages related to template rendering
This commit is contained in:
parent
af6e9a8153
commit
d0b8b605db
@ -117,7 +117,11 @@ class AnkiNoteBuilder {
|
|||||||
try {
|
try {
|
||||||
return await this._renderTemplate(templates, data, marker);
|
return await this._renderTemplate(templates, data, marker);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (errors) { errors.push(e); }
|
if (errors) {
|
||||||
|
const error = new Error(`Template render error for {${marker}}`);
|
||||||
|
error.data = {error: e};
|
||||||
|
errors.push(error);
|
||||||
|
}
|
||||||
return `{${marker}-render-error}`;
|
return `{${marker}-render-error}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -162,22 +162,49 @@ class AnkiConnect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _invoke(action, params) {
|
async _invoke(action, params) {
|
||||||
const response = await fetch(this._server, {
|
let response;
|
||||||
method: 'POST',
|
try {
|
||||||
mode: 'cors',
|
response = await fetch(this._server, {
|
||||||
cache: 'default',
|
method: 'POST',
|
||||||
credentials: 'omit',
|
mode: 'cors',
|
||||||
redirect: 'follow',
|
cache: 'default',
|
||||||
referrerPolicy: 'no-referrer',
|
credentials: 'omit',
|
||||||
body: JSON.stringify({action, params, version: this._localVersion})
|
redirect: 'follow',
|
||||||
});
|
referrerPolicy: 'no-referrer',
|
||||||
const result = await response.json();
|
body: JSON.stringify({action, params, version: this._localVersion})
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
const error = new Error('Anki connection failure');
|
||||||
|
error.data = {action, params};
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = new Error(`Anki connection error: ${response.status}`);
|
||||||
|
error.data = {action, params, status: response.status};
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
let responseText = null;
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
responseText = await response.text();
|
||||||
|
result = JSON.parse(responseText);
|
||||||
|
} catch (e) {
|
||||||
|
const error = new Error('Invalid Anki response');
|
||||||
|
error.data = {action, params, status: response.status, responseText};
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
if (isObject(result)) {
|
if (isObject(result)) {
|
||||||
const error = result.error;
|
const apiError = result.error;
|
||||||
if (typeof error !== 'undefined') {
|
if (typeof apiError !== 'undefined') {
|
||||||
throw new Error(`AnkiConnect error: ${error}`);
|
const error = new Error(`Anki error: ${apiError}`);
|
||||||
|
error.data = {action, params, status: response.status, apiError};
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1539,13 +1539,14 @@ class Backend {
|
|||||||
let clipboardImageFileName = null;
|
let clipboardImageFileName = null;
|
||||||
let clipboardText = null;
|
let clipboardText = null;
|
||||||
let audioFileName = null;
|
let audioFileName = null;
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (screenshotDetails !== null) {
|
if (screenshotDetails !== null) {
|
||||||
screenshotFileName = await this._injectAnkNoteScreenshot(ankiConnect, timestamp, definitionDetails, screenshotDetails);
|
screenshotFileName = await this._injectAnkNoteScreenshot(ankiConnect, timestamp, definitionDetails, screenshotDetails);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// NOP
|
errors.push(serializeError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1553,7 +1554,7 @@ class Backend {
|
|||||||
clipboardImageFileName = await this._injectAnkNoteClipboardImage(ankiConnect, timestamp, definitionDetails);
|
clipboardImageFileName = await this._injectAnkNoteClipboardImage(ankiConnect, timestamp, definitionDetails);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// NOP
|
errors.push(serializeError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1561,7 +1562,7 @@ class Backend {
|
|||||||
clipboardText = await this._clipboardReader.getText();
|
clipboardText = await this._clipboardReader.getText();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// NOP
|
errors.push(serializeError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1569,34 +1570,50 @@ class Backend {
|
|||||||
audioFileName = await this._injectAnkNoteAudio(ankiConnect, timestamp, definitionDetails, audioDetails);
|
audioFileName = await this._injectAnkNoteAudio(ankiConnect, timestamp, definitionDetails, audioDetails);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// NOP
|
errors.push(serializeError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {screenshotFileName, clipboardImageFileName, clipboardText, audioFileName};
|
return {
|
||||||
|
result: {
|
||||||
|
screenshotFileName,
|
||||||
|
clipboardImageFileName,
|
||||||
|
clipboardText,
|
||||||
|
audioFileName
|
||||||
|
},
|
||||||
|
errors
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _injectAnkNoteAudio(ankiConnect, timestamp, definitionDetails, details) {
|
async _injectAnkNoteAudio(ankiConnect, timestamp, definitionDetails, details) {
|
||||||
const {type, expression, reading} = definitionDetails;
|
const {type, expression, reading} = definitionDetails;
|
||||||
if (type === 'kanji') {
|
if (
|
||||||
throw new Error('Cannot inject audio for kanji');
|
type === 'kanji' ||
|
||||||
}
|
typeof expression !== 'string' ||
|
||||||
if (!reading && !expression) {
|
typeof reading !== 'string' ||
|
||||||
throw new Error('Invalid reading and expression');
|
(expression.length === 0 && reading.length === 0)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {sources, customSourceUrl, customSourceType} = details;
|
const {sources, customSourceUrl, customSourceType} = details;
|
||||||
const data = await this._downloadDefinitionAudio(
|
let data;
|
||||||
sources,
|
try {
|
||||||
expression,
|
data = await this._downloadDefinitionAudio(
|
||||||
reading,
|
sources,
|
||||||
{
|
expression,
|
||||||
textToSpeechVoice: null,
|
reading,
|
||||||
customSourceUrl,
|
{
|
||||||
customSourceType,
|
textToSpeechVoice: null,
|
||||||
binary: true,
|
customSourceUrl,
|
||||||
disableCache: true
|
customSourceType,
|
||||||
}
|
binary: true,
|
||||||
);
|
disableCache: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// No audio
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let fileName = this._generateAnkiNoteMediaFileName('yomichan_audio', '.mp3', timestamp, definitionDetails);
|
let fileName = this._generateAnkiNoteMediaFileName('yomichan_audio', '.mp3', timestamp, definitionDetails);
|
||||||
fileName = fileName.replace(/\]/g, '');
|
fileName = fileName.replace(/\]/g, '');
|
||||||
@ -1611,7 +1628,9 @@ class Backend {
|
|||||||
|
|
||||||
const {mediaType, data} = this._getDataUrlInfo(dataUrl);
|
const {mediaType, data} = this._getDataUrlInfo(dataUrl);
|
||||||
const extension = this._mediaUtility.getFileExtensionFromImageMediaType(mediaType);
|
const extension = this._mediaUtility.getFileExtensionFromImageMediaType(mediaType);
|
||||||
if (extension === null) { throw new Error('Unknown image media type'); }
|
if (extension === null) {
|
||||||
|
throw new Error('Unknown media type for screenshot image');
|
||||||
|
}
|
||||||
|
|
||||||
const fileName = this._generateAnkiNoteMediaFileName('yomichan_browser_screenshot', extension, timestamp, definitionDetails);
|
const fileName = this._generateAnkiNoteMediaFileName('yomichan_browser_screenshot', extension, timestamp, definitionDetails);
|
||||||
await ankiConnect.storeMediaFile(fileName, data);
|
await ankiConnect.storeMediaFile(fileName, data);
|
||||||
@ -1622,12 +1641,14 @@ class Backend {
|
|||||||
async _injectAnkNoteClipboardImage(ankiConnect, timestamp, definitionDetails) {
|
async _injectAnkNoteClipboardImage(ankiConnect, timestamp, definitionDetails) {
|
||||||
const dataUrl = await this._clipboardReader.getImage();
|
const dataUrl = await this._clipboardReader.getImage();
|
||||||
if (dataUrl === null) {
|
if (dataUrl === null) {
|
||||||
throw new Error('No clipboard image');
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {mediaType, data} = this._getDataUrlInfo(dataUrl);
|
const {mediaType, data} = this._getDataUrlInfo(dataUrl);
|
||||||
const extension = this._mediaUtility.getFileExtensionFromImageMediaType(mediaType);
|
const extension = this._mediaUtility.getFileExtensionFromImageMediaType(mediaType);
|
||||||
if (extension === null) { throw new Error('Unknown image media type'); }
|
if (extension === null) {
|
||||||
|
throw new Error('Unknown media type for clipboard image');
|
||||||
|
}
|
||||||
|
|
||||||
const fileName = this._generateAnkiNoteMediaFileName('yomichan_clipboard_image', extension, timestamp, definitionDetails);
|
const fileName = this._generateAnkiNoteMediaFileName('yomichan_clipboard_image', extension, timestamp, definitionDetails);
|
||||||
await ankiConnect.storeMediaFile(fileName, data);
|
await ankiConnect.storeMediaFile(fileName, data);
|
||||||
|
@ -73,7 +73,7 @@ class ClipboardReader {
|
|||||||
|
|
||||||
const document = this._document;
|
const document = this._document;
|
||||||
if (document === null) {
|
if (document === null) {
|
||||||
throw new Error('Not supported');
|
throw new Error('Clipboard reading not supported in this context');
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = this._pasteTarget;
|
let target = this._pasteTarget;
|
||||||
@ -118,7 +118,7 @@ class ClipboardReader {
|
|||||||
|
|
||||||
const document = this._document;
|
const document = this._document;
|
||||||
if (document === null) {
|
if (document === null) {
|
||||||
throw new Error('Not supported');
|
throw new Error('Clipboard reading not supported in this context');
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = this._imagePasteTarget;
|
let target = this._imagePasteTarget;
|
||||||
|
@ -165,7 +165,7 @@ class AnkiTemplatesController {
|
|||||||
|
|
||||||
async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) {
|
async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) {
|
||||||
const text = this._renderTextInput.value || '';
|
const text = this._renderTextInput.value || '';
|
||||||
const exceptions = [];
|
const errors = [];
|
||||||
let result = `No definition found for ${text}`;
|
let result = `No definition found for ${text}`;
|
||||||
try {
|
try {
|
||||||
const optionsContext = this._settingsController.getOptionsContext();
|
const optionsContext = this._settingsController.getOptionsContext();
|
||||||
@ -193,20 +193,36 @@ class AnkiTemplatesController {
|
|||||||
resultOutputMode,
|
resultOutputMode,
|
||||||
glossaryLayoutMode,
|
glossaryLayoutMode,
|
||||||
compactTags,
|
compactTags,
|
||||||
errors: exceptions
|
errors
|
||||||
});
|
});
|
||||||
result = note.fields.field;
|
result = note.fields.field;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exceptions.push(e);
|
errors.push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasException = exceptions.length > 0;
|
const errorToMessageString = (e) => {
|
||||||
infoNode.hidden = !(showSuccessResult || hasException);
|
if (isObject(e)) {
|
||||||
infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : '');
|
let v = e.data;
|
||||||
infoNode.classList.toggle('text-danger', hasException);
|
if (isObject(v)) {
|
||||||
|
v = v.error;
|
||||||
|
if (isObject(v)) {
|
||||||
|
e = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v = e.message;
|
||||||
|
if (typeof v === 'string') { return v; }
|
||||||
|
}
|
||||||
|
return `${e}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasError = errors.length > 0;
|
||||||
|
infoNode.hidden = !(showSuccessResult || hasError);
|
||||||
|
infoNode.textContent = hasError ? errors.map(errorToMessageString).join('\n') : (showSuccessResult ? result : '');
|
||||||
|
infoNode.classList.toggle('text-danger', hasError);
|
||||||
if (invalidateInput) {
|
if (invalidateInput) {
|
||||||
this._fieldTemplatesTextarea.dataset.invalid = `${hasException}`;
|
this._fieldTemplatesTextarea.dataset.invalid = `${hasError}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1675,6 +1675,14 @@ button.footer-notification-close-button:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Anki errors */
|
||||||
|
.anki-note-error-list {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Conditional styles */
|
/* Conditional styles */
|
||||||
:root:not([data-enable-search-tags=true]) .tag[data-category=search] {
|
:root:not([data-enable-search-tags=true]) .tag[data-category=search] {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -145,6 +145,11 @@
|
|||||||
<template id="footer-notification-tag-details-template" data-remove-whitespace-text="true">
|
<template id="footer-notification-tag-details-template" data-remove-whitespace-text="true">
|
||||||
<div class="tag-details"></div>
|
<div class="tag-details"></div>
|
||||||
<div class="tag-details-disambiguation-list"></div>
|
<div class="tag-details-disambiguation-list"></div>
|
||||||
|
</template>
|
||||||
|
<template id="footer-notification-anki-errors-content-template" data-remove-whitespace-text="true"><div class="anki-note-error-info">
|
||||||
|
<div class="anki-note-error-header"></div>
|
||||||
|
<ul class="anki-note-error-list"></ul>
|
||||||
|
<div class="anki-note-error-log-container"><a class="anki-note-error-log-link">Log debug info to console</a></div>
|
||||||
</div></template>
|
</div></template>
|
||||||
<template id="profile-list-item-template"><label class="profile-list-item">
|
<template id="profile-list-item-template"><label class="profile-list-item">
|
||||||
<div class="profile-list-item-selection"><label class="radio"><input type="radio" class="profile-entry-is-default-radio" name="profile-entry-default-radio"><span class="radio-body"><span class="radio-border"></span><span class="radio-dot"></span></span></label></div>
|
<div class="profile-list-item-selection"><label class="radio"><input type="radio" class="profile-entry-is-default-radio" name="profile-entry-default-radio"><span class="radio-body"><span class="radio-border"></span><span class="radio-dot"></span></span></label></div>
|
||||||
|
@ -192,6 +192,23 @@ class DisplayGenerator {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createAnkiNoteErrorsNotificationContent(errors) {
|
||||||
|
const content = this._templates.instantiate('footer-notification-anki-errors-content');
|
||||||
|
|
||||||
|
const header = content.querySelector('.anki-note-error-header');
|
||||||
|
header.textContent = (errors.length === 1 ? 'An error occurred:' : `${errors.length} errors occurred:`);
|
||||||
|
|
||||||
|
const list = content.querySelector('.anki-note-error-list');
|
||||||
|
for (const error of errors) {
|
||||||
|
const div = document.createElement('li');
|
||||||
|
div.className = 'anki-note-error-message';
|
||||||
|
div.textContent = isObject(error) && typeof error.message === 'string' ? error.message : `${error}`;
|
||||||
|
list.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
createProfileListItem() {
|
createProfileListItem() {
|
||||||
return this._templates.instantiate('profile-list-item');
|
return this._templates.instantiate('profile-list-item');
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,10 @@ class Display extends EventDispatcher {
|
|||||||
this._frameResizeStartOffset = null;
|
this._frameResizeStartOffset = null;
|
||||||
this._frameResizeEventListeners = new EventListenerCollection();
|
this._frameResizeEventListeners = new EventListenerCollection();
|
||||||
this._tagNotification = null;
|
this._tagNotification = null;
|
||||||
this._tagNotificationContainer = document.querySelector('#content-footer');
|
this._footerNotificationContainer = document.querySelector('#content-footer');
|
||||||
this._displayAudio = new DisplayAudio(this);
|
this._displayAudio = new DisplayAudio(this);
|
||||||
|
this._ankiNoteNotification = null;
|
||||||
|
this._ankiNoteNotificationEventListeners = null;
|
||||||
|
|
||||||
this._hotkeyHandler.registerActions([
|
this._hotkeyHandler.registerActions([
|
||||||
['close', () => { this._onHotkeyClose(); }],
|
['close', () => { this._onHotkeyClose(); }],
|
||||||
@ -525,6 +527,7 @@ class Display extends EventDispatcher {
|
|||||||
this._mediaLoader.unloadAll();
|
this._mediaLoader.unloadAll();
|
||||||
this._displayAudio.cleanupEntries();
|
this._displayAudio.cleanupEntries();
|
||||||
this._hideTagNotification(false);
|
this._hideTagNotification(false);
|
||||||
|
this._hideAnkiNoteErrors(false);
|
||||||
this._definitions = [];
|
this._definitions = [];
|
||||||
this._definitionNodes = [];
|
this._definitionNodes = [];
|
||||||
|
|
||||||
@ -791,7 +794,7 @@ class Display extends EventDispatcher {
|
|||||||
if (this._tagNotification === null) {
|
if (this._tagNotification === null) {
|
||||||
const node = this._displayGenerator.createEmptyFooterNotification();
|
const node = this._displayGenerator.createEmptyFooterNotification();
|
||||||
node.classList.add('click-scannable');
|
node.classList.add('click-scannable');
|
||||||
this._tagNotification = new DisplayNotification(this._tagNotificationContainer, node);
|
this._tagNotification = new DisplayNotification(this._footerNotificationContainer, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = this._displayGenerator.createTagFooterNotificationDetails(tagNode);
|
const content = this._displayGenerator.createTagFooterNotificationDetails(tagNode);
|
||||||
@ -1170,38 +1173,85 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _addAnkiNote(definitionIndex, mode) {
|
async _addAnkiNote(definitionIndex, mode) {
|
||||||
if (definitionIndex < 0 || definitionIndex >= this._definitions.length) { return false; }
|
if (definitionIndex < 0 || definitionIndex >= this._definitions.length) { return; }
|
||||||
const definition = this._definitions[definitionIndex];
|
const definition = this._definitions[definitionIndex];
|
||||||
|
|
||||||
const button = this._adderButtonFind(definitionIndex, mode);
|
const button = this._adderButtonFind(definitionIndex, mode);
|
||||||
if (button === null || button.disabled) { return false; }
|
if (button === null || button.disabled) { return; }
|
||||||
|
|
||||||
|
this._hideAnkiNoteErrors(true);
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
const overrideToken = this._progressIndicatorVisible.setOverride(true);
|
const overrideToken = this._progressIndicatorVisible.setOverride(true);
|
||||||
try {
|
try {
|
||||||
const {anki: {suspendNewCards}} = this._options;
|
const {anki: {suspendNewCards}} = this._options;
|
||||||
const noteContext = this._getNoteContext();
|
const noteContext = this._getNoteContext();
|
||||||
const note = await this._createNote(definition, mode, noteContext, true);
|
const note = await this._createNote(definition, mode, noteContext, true, errors);
|
||||||
const noteId = await api.addAnkiNote(note);
|
|
||||||
if (noteId) {
|
let noteId = null;
|
||||||
if (suspendNewCards) {
|
let addNoteOkay = false;
|
||||||
try {
|
try {
|
||||||
await api.suspendAnkiCardsForNote(noteId);
|
noteId = await api.addAnkiNote(note);
|
||||||
} catch (e) {
|
addNoteOkay = true;
|
||||||
// NOP
|
} catch (e) {
|
||||||
|
errors.length = 0;
|
||||||
|
errors.push(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addNoteOkay) {
|
||||||
|
if (noteId === null) {
|
||||||
|
errors.push(new Error('Note could not be added'));
|
||||||
|
} else {
|
||||||
|
if (suspendNewCards) {
|
||||||
|
try {
|
||||||
|
await api.suspendAnkiCardsForNote(noteId);
|
||||||
|
} catch (e) {
|
||||||
|
errors.push(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
button.disabled = true;
|
||||||
|
this._viewerButtonShow(definitionIndex, noteId);
|
||||||
}
|
}
|
||||||
button.disabled = true;
|
|
||||||
this._viewerButtonShow(definitionIndex, noteId);
|
|
||||||
} else {
|
|
||||||
throw new Error('Note could not be added');
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
errors.push(e);
|
||||||
return false;
|
|
||||||
} finally {
|
} finally {
|
||||||
this._progressIndicatorVisible.clearOverride(overrideToken);
|
this._progressIndicatorVisible.clearOverride(overrideToken);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
this._showAnkiNoteErrors(errors);
|
||||||
|
} else {
|
||||||
|
this._hideAnkiNoteErrors(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_showAnkiNoteErrors(errors) {
|
||||||
|
if (this._ankiNoteNotificationEventListeners !== null) {
|
||||||
|
this._ankiNoteNotificationEventListeners.removeAllEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._ankiNoteNotification === null) {
|
||||||
|
const node = this._displayGenerator.createEmptyFooterNotification();
|
||||||
|
this._ankiNoteNotification = new DisplayNotification(this._footerNotificationContainer, node);
|
||||||
|
this._ankiNoteNotificationEventListeners = new EventListenerCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = this._displayGenerator.createAnkiNoteErrorsNotificationContent(errors);
|
||||||
|
for (const node of content.querySelectorAll('.anki-note-error-log-link')) {
|
||||||
|
this._ankiNoteNotificationEventListeners.addEventListener(node, 'click', () => {
|
||||||
|
console.log({ankiNoteErrors: errors});
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ankiNoteNotification.setContent(content);
|
||||||
|
this._ankiNoteNotification.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideAnkiNoteErrors(animate) {
|
||||||
|
if (this._ankiNoteNotification === null) { return; }
|
||||||
|
this._ankiNoteNotification.close(animate);
|
||||||
|
this._ankiNoteNotificationEventListeners.removeAllEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _playAudioCurrent() {
|
async _playAudioCurrent() {
|
||||||
@ -1372,7 +1422,7 @@ class Display extends EventDispatcher {
|
|||||||
const notePromises = [];
|
const notePromises = [];
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
for (const mode of modes) {
|
for (const mode of modes) {
|
||||||
const notePromise = this._createNote(definition, mode, context, false);
|
const notePromise = this._createNote(definition, mode, context, false, null);
|
||||||
notePromises.push(notePromise);
|
notePromises.push(notePromise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1400,7 +1450,7 @@ class Display extends EventDispatcher {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createNote(definition, mode, context, injectMedia) {
|
async _createNote(definition, mode, context, injectMedia, errors) {
|
||||||
const options = this._options;
|
const options = this._options;
|
||||||
const templates = this._ankiFieldTemplates;
|
const templates = this._ankiFieldTemplates;
|
||||||
const {
|
const {
|
||||||
@ -1412,7 +1462,16 @@ class Display extends EventDispatcher {
|
|||||||
const {deck: deckName, model: modelName} = modeOptions;
|
const {deck: deckName, model: modelName} = modeOptions;
|
||||||
const fields = Object.entries(modeOptions.fields);
|
const fields = Object.entries(modeOptions.fields);
|
||||||
|
|
||||||
const injectedMedia = (injectMedia ? await this._injectAnkiNoteMedia(definition, mode, options, fields) : null);
|
let injectedMedia = null;
|
||||||
|
if (injectMedia) {
|
||||||
|
let errors2;
|
||||||
|
({result: injectedMedia, errors: errors2} = await this._injectAnkiNoteMedia(definition, mode, options, fields));
|
||||||
|
if (Array.isArray(errors)) {
|
||||||
|
for (const error of errors2) {
|
||||||
|
errors.push(deserializeError(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return await this._ankiNoteBuilder.createNote({
|
return await this._ankiNoteBuilder.createNote({
|
||||||
definition,
|
definition,
|
||||||
@ -1428,7 +1487,8 @@ class Display extends EventDispatcher {
|
|||||||
resultOutputMode,
|
resultOutputMode,
|
||||||
glossaryLayoutMode,
|
glossaryLayoutMode,
|
||||||
compactTags,
|
compactTags,
|
||||||
injectedMedia
|
injectedMedia,
|
||||||
|
errors
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user