diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2022-05-30 12:03:24 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-30 12:03:24 -0400 |
commit | 19bba07a8bccb51a9db85c13fd921d825defe753 (patch) | |
tree | 4354e2d3396f5957a005256a85f60d239ab30c0d /ext/js | |
parent | 0b5d54e7c66c17383e23855a1c3d4dbb1ea817fc (diff) |
Add support for Anki API key (#2169)
* Update material.css to support password fields
* Support password
* Add "apiKey" setting
* Use apiKey
* Update options if API key changes
* Update tests
Diffstat (limited to 'ext/js')
-rw-r--r-- | ext/js/background/backend.js | 3 | ||||
-rw-r--r-- | ext/js/comm/anki-connect.js | 13 | ||||
-rw-r--r-- | ext/js/data/options-util.js | 2 | ||||
-rw-r--r-- | ext/js/dom/dom-data-binder.js | 61 | ||||
-rw-r--r-- | ext/js/pages/settings/anki-controller.js | 29 |
5 files changed, 77 insertions, 31 deletions
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 07d6fd98..75ff7bee 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -998,8 +998,11 @@ class Backend { const enabled = options.general.enable; + let {apiKey} = options.anki; + if (apiKey === '') { apiKey = null; } this._anki.server = options.anki.server; this._anki.enabled = options.anki.enable && enabled; + this._anki.apiKey = apiKey; this._mecab.setEnabled(options.parsing.enableMecabParser && enabled); diff --git a/ext/js/comm/anki-connect.js b/ext/js/comm/anki-connect.js index f5dc62f2..f0aff8fa 100644 --- a/ext/js/comm/anki-connect.js +++ b/ext/js/comm/anki-connect.js @@ -26,6 +26,7 @@ class AnkiConnect { this._localVersion = 2; this._remoteVersion = 0; this._versionCheckPromise = null; + this._apiKey = null; } get server() { @@ -44,6 +45,14 @@ class AnkiConnect { this._enabled = value; } + get apiKey() { + return this._apiKey; + } + + set apiKey(value) { + this._apiKey = value; + } + async isConnected() { try { await this._invoke('version'); @@ -230,6 +239,8 @@ class AnkiConnect { } async _invoke(action, params) { + const body = {action, params, version: this._localVersion}; + if (this._apiKey !== null) { body.key = this._apiKey; } let response; try { response = await fetch(this._server, { @@ -242,7 +253,7 @@ class AnkiConnect { }, redirect: 'follow', referrerPolicy: 'no-referrer', - body: JSON.stringify({action, params, version: this._localVersion}) + body: JSON.stringify(body) }); } catch (e) { const error = new Error('Anki connection failure'); diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index f87bfa4b..f19094df 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -952,8 +952,10 @@ class OptionsUtil { _updateVersion19(options) { // Version 19 changes: // Added anki.noteGuiMode. + // Added anki.apiKey. for (const profile of options.profiles) { profile.options.anki.noteGuiMode = 'browse'; + profile.options.anki.apiKey = ''; } return options; } diff --git a/ext/js/dom/dom-data-binder.js b/ext/js/dom/dom-data-binder.js index 4f35ba33..185dc439 100644 --- a/ext/js/dom/dom-data-binder.js +++ b/ext/js/dom/dom-data-binder.js @@ -126,10 +126,9 @@ class DOMDataBinder { _createObserver(element) { const metadata = this._createElementMetadata(element); - const nodeName = element.nodeName.toUpperCase(); const observer = { element, - type: (nodeName === 'INPUT' ? element.type : null), + type: this._getNormalizedElementType(element), value: null, hasValue: false, onChange: null, @@ -157,28 +156,21 @@ class DOMDataBinder { _isObserverStale(element, observer) { const {type, metadata} = observer; - const nodeName = element.nodeName.toUpperCase(); return !( - type === (nodeName === 'INPUT' ? element.type : null) && + type === this._getNormalizedElementType(element) && this._compareElementMetadata(metadata, this._createElementMetadata(element)) ); } _setElementValue(element, value) { - switch (element.nodeName.toUpperCase()) { - case 'INPUT': - switch (element.type) { - case 'checkbox': - element.checked = value; - break; - case 'text': - case 'number': - element.value = value; - break; - } + switch (this._getNormalizedElementType(element)) { + case 'checkbox': + element.checked = value; break; - case 'TEXTAREA': - case 'SELECT': + case 'text': + case 'number': + case 'textarea': + case 'select': element.value = value; break; } @@ -188,22 +180,35 @@ class DOMDataBinder { } _getElementValue(element) { + switch (this._getNormalizedElementType(element)) { + case 'checkbox': + return !!element.checked; + case 'text': + return `${element.value}`; + case 'number': + return DOMDataBinder.convertToNumber(element.value, element); + case 'textarea': + case 'select': + return element.value; + } + return null; + } + + _getNormalizedElementType(element) { switch (element.nodeName.toUpperCase()) { case 'INPUT': - switch (element.type) { - case 'checkbox': - return !!element.checked; - case 'text': - return `${element.value}`; - case 'number': - return DOMDataBinder.convertToNumber(element.value, element); - } - break; + { + let {type} = element; + if (type === 'password') { type = 'text'; } + return type; + } case 'TEXTAREA': + return 'textarea'; case 'SELECT': - return element.value; + return 'select'; + default: + return null; } - return null; } // Utilities diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js index d03fa535..cfbac0ea 100644 --- a/ext/js/pages/settings/anki-controller.js +++ b/ext/js/pages/settings/anki-controller.js @@ -61,6 +61,7 @@ class AnkiController { this._ankiErrorInvalidResponseInfo = document.querySelector('#anki-error-invalid-response-info'); this._ankiEnableCheckbox = document.querySelector('[data-setting="anki.enable"]'); this._ankiCardPrimary = document.querySelector('#anki-card-primary'); + const ankiApiKeyInput = document.querySelector('#anki-api-key-input'); const ankiCardPrimaryTypeRadios = document.querySelectorAll('input[type=radio][name=anki-card-primary-type]'); this._setupFieldMenus(); @@ -79,9 +80,17 @@ class AnkiController { document.querySelector('#anki-error-log').addEventListener('click', this._onAnkiErrorLogLinkClick.bind(this)); - const options = await this._settingsController.getOptions(); + ankiApiKeyInput.addEventListener('focus', this._onApiKeyInputFocus.bind(this)); + ankiApiKeyInput.addEventListener('blur', this._onApiKeyInputBlur.bind(this)); + + const onAnkiSettingChanged = () => { this._updateOptions(); }; + const nodes = [ankiApiKeyInput, ...document.querySelectorAll('[data-setting="anki.enable"]')]; + for (const node of nodes) { + node.addEventListener('settingChanged', onAnkiSettingChanged); + } + + await this._updateOptions(); this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); - this._onOptionsChanged({options}); } getFieldMarkers(type) { @@ -164,9 +173,17 @@ class AnkiController { // Private + async _updateOptions() { + const options = await this._settingsController.getOptions(); + this._onOptionsChanged({options}); + } + async _onOptionsChanged({options: {anki}}) { + let {apiKey} = anki; + if (apiKey === '') { apiKey = null; } this._ankiConnect.server = anki.server; this._ankiConnect.enabled = anki.enable; + this._ankiConnect.apiKey = apiKey; this._selectorObserver.disconnect(); this._selectorObserver.observe(document.documentElement, true); @@ -202,6 +219,14 @@ class AnkiController { this._testAnkiNoteViewerSafe(e.currentTarget.dataset.mode); } + _onApiKeyInputFocus(e) { + e.currentTarget.type = 'text'; + } + + _onApiKeyInputBlur(e) { + e.currentTarget.type = 'password'; + } + _setAnkiCardPrimaryType(ankiCardType, ankiCardMenu) { if (this._ankiCardPrimary === null) { return; } this._ankiCardPrimary.dataset.ankiCardType = ankiCardType; |