aboutsummaryrefslogtreecommitdiff
path: root/ext/mixed
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-09-10 18:03:46 -0400
committerGitHub <noreply@github.com>2020-09-10 18:03:46 -0400
commita531618c48481a30d63112cf59b7806291f0bda4 (patch)
tree04b185c11406952a179ee229e39847234d592ea4 /ext/mixed
parent9ce682272c5d665bbbe9cbd1380416c3d22f9b04 (diff)
Use Anki classes directly in Display (#804)
* Add _getTemplates function * Add template renderer to display pages * Add AnkiNoteBuilder to Display * Update AnkiTemplatesController to directly use TemplateRenderer * Remove old APIs
Diffstat (limited to 'ext/mixed')
-rw-r--r--ext/mixed/js/api.js12
-rw-r--r--ext/mixed/js/display.js128
2 files changed, 115 insertions, 25 deletions
diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js
index 4a1b0c11..1e7625da 100644
--- a/ext/mixed/js/api.js
+++ b/ext/mixed/js/api.js
@@ -89,22 +89,10 @@ const api = (() => {
return this._invoke('injectAnkiNoteMedia', {expression, reading, timestamp, audioDetails, screenshotDetails, clipboardImage});
}
- definitionAdd(definition, mode, context, ownerFrameId, optionsContext) {
- return this._invoke('definitionAdd', {definition, mode, context, ownerFrameId, optionsContext});
- }
-
- definitionsAddable(definitions, modes, context, optionsContext) {
- return this._invoke('definitionsAddable', {definitions, modes, context, optionsContext});
- }
-
noteView(noteId) {
return this._invoke('noteView', {noteId});
}
- templateRender(template, data, marker) {
- return this._invoke('templateRender', {data, template, marker});
- }
-
audioGetUri(source, expression, reading, details) {
return this._invoke('audioGetUri', {source, expression, reading, details});
}
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index 7dd5920a..4c42a48d 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -16,6 +16,7 @@
*/
/* global
+ * AnkiNoteBuilder
* AudioSystem
* DisplayGenerator
* DisplayHistory
@@ -24,6 +25,7 @@
* MediaLoader
* PopupFactory
* QueryParser
+ * TemplateRenderer
* WindowScroll
* api
* dynamicLoader
@@ -83,6 +85,12 @@ class Display extends EventDispatcher {
});
this._mode = null;
this._ownerFrameId = null;
+ this._defaultAnkiFieldTemplates = null;
+ this._defaultAnkiFieldTemplatesPromise = null;
+ this._templateRenderer = new TemplateRenderer();
+ this._ankiNoteBuilder = new AnkiNoteBuilder({
+ renderTemplate: this._renderTemplate.bind(this)
+ });
this.registerActions([
['close', () => { this.onEscape(); }],
@@ -891,7 +899,8 @@ class Display extends EventDispatcher {
const modes = isTerms ? ['term-kanji', 'term-kana'] : ['kanji'];
let states;
try {
- states = await this._getDefinitionsAddable(definitions, modes);
+ const noteContext = await this._getNoteContext();
+ states = await this._areDefinitionsAddable(definitions, modes, noteContext);
} catch (e) {
return;
}
@@ -1054,10 +1063,8 @@ class Display extends EventDispatcher {
try {
this.setSpinnerVisible(true);
- const ownerFrameId = this._ownerFrameId;
- const optionsContext = this.getOptionsContext();
const noteContext = await this._getNoteContext();
- const noteId = await api.definitionAdd(definition, mode, noteContext, ownerFrameId, optionsContext);
+ const noteId = await this._addDefinition(definition, mode, noteContext);
if (noteId) {
const index = this._definitions.indexOf(definition);
const adderButton = this._adderButtonFind(index, mode);
@@ -1196,15 +1203,6 @@ class Display extends EventDispatcher {
return container !== null ? container.querySelector('.action-play-audio>img') : null;
}
- async _getDefinitionsAddable(definitions, modes) {
- try {
- const noteContext = await this._getNoteContext();
- return await api.definitionsAddable(definitions, modes, noteContext, this.getOptionsContext());
- } catch (e) {
- return [];
- }
- }
-
_indexOf(nodeList, node) {
for (let i = 0, ii = nodeList.length; i < ii; ++i) {
if (nodeList[i] === node) {
@@ -1315,4 +1313,108 @@ class Display extends EventDispatcher {
this._mode = mode;
this.trigger('modeChange', {mode});
}
+
+ async _getTemplates(options) {
+ let templates = options.anki.fieldTemplates;
+ if (typeof templates === 'string') { return templates; }
+
+ templates = this._defaultAnkiFieldTemplates;
+ if (typeof templates === 'string') { return templates; }
+
+ return await this._getDefaultTemplatesPromise();
+ }
+
+ _getDefaultTemplatesPromise() {
+ if (this._defaultAnkiFieldTemplatesPromise === null) {
+ this._defaultAnkiFieldTemplatesPromise = this._getDefaultTemplates();
+ this._defaultAnkiFieldTemplatesPromise.then(
+ () => { this._defaultAnkiFieldTemplatesPromise = null; },
+ () => {} // NOP
+ );
+ }
+ return this._defaultAnkiFieldTemplatesPromise;
+ }
+
+ async _getDefaultTemplates() {
+ const value = await api.getDefaultAnkiFieldTemplates();
+ this._defaultAnkiFieldTemplates = value;
+ return value;
+ }
+
+ async _renderTemplate(template, data, marker) {
+ return await this._templateRenderer.render(template, data, marker);
+ }
+
+ async _addDefinition(definition, mode, context) {
+ const options = this._options;
+ const templates = await this._getTemplates(options);
+ const note = await this._createNote(definition, mode, context, options, templates, true);
+ return await api.addAnkiNote(note);
+ }
+
+ async _areDefinitionsAddable(definitions, modes, context) {
+ const options = this._options;
+ const templates = await this._getTemplates(options);
+
+ const modeCount = modes.length;
+ const {duplicateScope} = options.anki;
+ const notePromises = [];
+ for (const definition of definitions) {
+ for (const mode of modes) {
+ const notePromise = this._createNote(definition, mode, context, options, templates, false);
+ notePromises.push(notePromise);
+ }
+ }
+ const notes = await Promise.all(notePromises);
+
+ const infos = await api.getAnkiNoteInfo(notes, duplicateScope);
+ const results = [];
+ for (let i = 0, ii = infos.length; i < ii; i += modeCount) {
+ results.push(infos.slice(i, i + modeCount));
+ }
+ return results;
+ }
+
+ async _createNote(definition, mode, context, options, templates, injectMedia) {
+ const {
+ general: {resultOutputMode, compactGlossaries},
+ anki: {tags, duplicateScope, kanji, terms, screenshot: {format, quality}},
+ audio: {sources, customSourceUrl}
+ } = options;
+ const modeOptions = (mode === 'kanji') ? kanji : terms;
+
+ if (injectMedia) {
+ const timestamp = Date.now();
+ const ownerFrameId = this._ownerFrameId;
+ const {fields} = modeOptions;
+ const definitionExpressions = definition.expressions;
+ const {expression, reading} = Array.isArray(definitionExpressions) ? definitionExpressions[0] : definition;
+ const audioDetails = (mode !== 'kanji' && this._ankiNoteBuilder.containsMarker(fields, 'audio') ? {sources, customSourceUrl} : null);
+ const screenshotDetails = (this._ankiNoteBuilder.containsMarker(fields, 'screenshot') ? {ownerFrameId, format, quality} : null);
+ const clipboardImage = (this._ankiNoteBuilder.containsMarker(fields, 'clipboard-image'));
+ const {screenshotFileName, clipboardImageFileName, audioFileName} = await api.injectAnkiNoteMedia(
+ expression,
+ reading,
+ timestamp,
+ audioDetails,
+ screenshotDetails,
+ clipboardImage
+ );
+ if (screenshotFileName !== null) { definition.screenshotFileName = screenshotFileName; }
+ if (clipboardImageFileName !== null) { definition.clipboardImageFileName = clipboardImageFileName; }
+ if (audioFileName !== null) { definition.audioFileName = audioFileName; }
+ }
+
+ return await this._ankiNoteBuilder.createNote({
+ definition,
+ mode,
+ context,
+ templates,
+ tags,
+ duplicateScope,
+ resultOutputMode,
+ compactGlossaries,
+ modeOptions
+ });
+ }
}