summaryrefslogtreecommitdiff
path: root/ext/bg/js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2019-11-02 16:21:06 -0400
committertoasted-nutbread <toasted-nutbread@users.noreply.github.com>2019-11-07 20:30:55 -0500
commite091c7ebe2f6780b6a88df313c9f20170a8e5c1c (patch)
tree54eaeb00c11fa591321dc4d6a934fd449cd4c5d3 /ext/bg/js
parente355b839142a8bab0edc446c7da08256ad5b938c (diff)
Add support for deleting individual dictionaries
Diffstat (limited to 'ext/bg/js')
-rw-r--r--ext/bg/js/database.js104
-rw-r--r--ext/bg/js/settings-dictionaries.js64
-rw-r--r--ext/bg/js/translator.js5
-rw-r--r--ext/bg/js/util.js4
4 files changed, 177 insertions, 0 deletions
diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js
index fc0af049..dc2198ac 100644
--- a/ext/bg/js/database.js
+++ b/ext/bg/js/database.js
@@ -56,6 +56,42 @@ class Database {
await this.prepare();
}
+ async deleteDictionary(dictionaryName, onProgress, progressSettings) {
+ this.validate();
+
+ const targets = [
+ ['dictionaries', 'title'],
+ ['kanji', 'dictionary'],
+ ['kanjiMeta', 'dictionary'],
+ ['terms', 'dictionary'],
+ ['termMeta', 'dictionary'],
+ ['tagMeta', 'dictionary']
+ ];
+ const promises = [];
+ const progressData = {
+ count: 0,
+ processed: 0,
+ storeCount: targets.length,
+ storesProcesed: 0
+ };
+ let progressRate = (typeof progressSettings === 'object' && progressSettings !== null ? progressSettings.rate : 0);
+ if (typeof progressRate !== 'number' || progressRate <= 0) {
+ progressRate = 1000;
+ }
+
+ const db = this.db.backendDB();
+
+ for (const [objectStoreName, index] of targets) {
+ const dbTransaction = db.transaction([objectStoreName], 'readwrite');
+ const dbObjectStore = dbTransaction.objectStore(objectStoreName);
+ const dbIndex = dbObjectStore.index(index);
+ const only = IDBKeyRange.only(dictionaryName);
+ promises.push(Database.deleteValues(dbObjectStore, dbIndex, only, onProgress, progressData, progressRate));
+ }
+
+ await Promise.all(promises);
+ }
+
async findTermsBulk(termList, titles) {
this.validate();
@@ -612,4 +648,72 @@ class Database {
request.onsuccess = (e) => resolve(e.target.result);
});
}
+
+ static getAllKeys(dbIndex, query) {
+ const fn = typeof dbIndex.getAllKeys === 'function' ? Database.getAllKeysFast : Database.getAllKeysUsingCursor;
+ return fn(dbIndex, query);
+ }
+
+ static getAllKeysFast(dbIndex, query) {
+ return new Promise((resolve, reject) => {
+ const request = dbIndex.getAllKeys(query);
+ request.onerror = (e) => reject(e);
+ request.onsuccess = (e) => resolve(e.target.result);
+ });
+ }
+
+ static getAllKeysUsingCursor(dbIndex, query) {
+ return new Promise((resolve, reject) => {
+ const primaryKeys = [];
+ const request = dbIndex.openKeyCursor(query, 'next');
+ request.onerror = (e) => reject(e);
+ request.onsuccess = (e) => {
+ const cursor = e.target.result;
+ if (cursor) {
+ primaryKeys.push(cursor.primaryKey);
+ cursor.continue();
+ } else {
+ resolve(primaryKeys);
+ }
+ };
+ });
+ }
+
+ static async deleteValues(dbObjectStore, dbIndex, query, onProgress, progressData, progressRate) {
+ const hasProgress = (typeof onProgress === 'function');
+ const count = await Database.getCount(dbIndex, query);
+ ++progressData.storesProcesed;
+ progressData.count += count;
+ if (hasProgress) {
+ onProgress(progressData);
+ }
+
+ const onValueDeleted = (
+ hasProgress ?
+ () => {
+ const p = ++progressData.processed;
+ if ((p % progressRate) === 0 || p === progressData.count) {
+ onProgress(progressData);
+ }
+ } :
+ () => {}
+ );
+
+ const promises = [];
+ const primaryKeys = await Database.getAllKeys(dbIndex, query);
+ for (const key of primaryKeys) {
+ const promise = Database.deleteValue(dbObjectStore, key).then(onValueDeleted);
+ promises.push(promise);
+ }
+
+ await Promise.all(promises);
+ }
+
+ static deleteValue(dbObjectStore, key) {
+ return new Promise((resolve, reject) => {
+ const request = dbObjectStore.delete(key);
+ request.onerror = (e) => reject(e);
+ request.onsuccess = () => resolve();
+ });
+ }
}
diff --git a/ext/bg/js/settings-dictionaries.js b/ext/bg/js/settings-dictionaries.js
index 2f33d1ac..bf1b232f 100644
--- a/ext/bg/js/settings-dictionaries.js
+++ b/ext/bg/js/settings-dictionaries.js
@@ -30,6 +30,8 @@ class SettingsDictionaryListUI {
this.dictionaryEntries = [];
this.extra = null;
+
+ document.querySelector('#dict-delete-confirm').addEventListener('click', (e) => this.onDictionaryConfirmDelete(e), false);
}
setDictionaries(dictionaries) {
@@ -126,6 +128,19 @@ class SettingsDictionaryListUI {
save() {
// Overwrite
}
+
+ onDictionaryConfirmDelete(e) {
+ e.preventDefault();
+ const n = document.querySelector('#dict-delete-modal');
+ const title = n.dataset.dict;
+ delete n.dataset.dict;
+ $(n).modal('hide');
+
+ const index = this.dictionaryEntries.findIndex(e => e.dictionaryInfo.title === title);
+ if (index >= 0) {
+ this.dictionaryEntries[index].deleteDictionary();
+ }
+ }
}
class SettingsDictionaryEntryUI {
@@ -135,11 +150,13 @@ class SettingsDictionaryEntryUI {
this.optionsDictionary = optionsDictionary;
this.counts = null;
this.eventListeners = [];
+ this.isDeleting = false;
this.content = content;
this.enabledCheckbox = this.content.querySelector('.dict-enabled');
this.allowSecondarySearchesCheckbox = this.content.querySelector('.dict-allow-secondary-searches');
this.priorityInput = this.content.querySelector('.dict-priority');
+ this.deleteButton = this.content.querySelector('.dict-delete-button');
this.content.querySelector('.dict-title').textContent = this.dictionaryInfo.title;
this.content.querySelector('.dict-revision').textContent = `rev.${this.dictionaryInfo.revision}`;
@@ -149,6 +166,7 @@ class SettingsDictionaryEntryUI {
this.addEventListener(this.enabledCheckbox, 'change', (e) => this.onEnabledChanged(e), false);
this.addEventListener(this.allowSecondarySearchesCheckbox, 'change', (e) => this.onAllowSecondarySearchesChanged(e), false);
this.addEventListener(this.priorityInput, 'change', (e) => this.onPriorityChanged(e), false);
+ this.addEventListener(this.deleteButton, 'click', (e) => this.onDeleteButtonClicked(e), false);
}
cleanup() {
@@ -194,6 +212,38 @@ class SettingsDictionaryEntryUI {
this.priorityInput.value = `${this.optionsDictionary.priority}`;
}
+ async deleteDictionary() {
+ if (this.isDeleting) {
+ return;
+ }
+
+ const progress = this.content.querySelector('.progress');
+ progress.hidden = false;
+ const progressBar = this.content.querySelector('.progress-bar');
+ this.isDeleting = true;
+
+ try {
+ const onProgress = ({processed, count, storeCount, storesProcesed}) => {
+ let percent = 0.0;
+ if (count > 0 && storesProcesed > 0) {
+ percent = (processed / count) * (storesProcesed / storeCount) * 100.0;
+ }
+ progressBar.style.width = `${percent}%`;
+ };
+
+ await utilDatabaseDeleteDictionary(this.dictionaryInfo.title, onProgress, {rate: 1000});
+ } catch (e) {
+ dictionaryErrorsShow([e]);
+ } finally {
+ this.isDeleting = false;
+ progress.hidden = true;
+
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ onDatabaseUpdated(options);
+ }
+ }
+
onEnabledChanged(e) {
this.optionsDictionary.enabled = !!e.target.checked;
this.save();
@@ -215,6 +265,20 @@ class SettingsDictionaryEntryUI {
e.target.value = `${value}`;
}
+
+ onDeleteButtonClicked(e) {
+ e.preventDefault();
+
+ if (this.isDeleting) {
+ return;
+ }
+
+ const title = this.dictionaryInfo.title;
+ const n = document.querySelector('#dict-delete-modal');
+ n.dataset.dict = title;
+ document.querySelector('#dict-remove-modal-dict-name').textContent = title;
+ $(n).modal('show');
+ }
}
class SettingsDictionaryExtraUI {
diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js
index 9d90136b..ff1d24f3 100644
--- a/ext/bg/js/translator.js
+++ b/ext/bg/js/translator.js
@@ -42,6 +42,11 @@ class Translator {
await this.database.purge();
}
+ async deleteDictionary(dictionaryName) {
+ this.tagCache = {};
+ await this.database.deleteDictionary(dictionaryName);
+ }
+
async findTermsGrouped(text, dictionaries, alphanumeric, options) {
const titles = Object.keys(dictionaries);
const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js
index 3554ec3d..f9686943 100644
--- a/ext/bg/js/util.js
+++ b/ext/bg/js/util.js
@@ -100,6 +100,10 @@ function utilDatabasePurge() {
return utilBackend().translator.purgeDatabase();
}
+function utilDatabaseDeleteDictionary(dictionaryName, onProgress) {
+ return utilBackend().translator.database.deleteDictionary(dictionaryName, onProgress);
+}
+
async function utilDatabaseImport(data, progress, exceptions) {
// Edge cannot read data on the background page due to the File object
// being created from a different window. Read on the same page instead.