diff --git a/ext/js/pages/settings/dictionary-controller.js b/ext/js/pages/settings/dictionary-controller.js index e12017f2..17abfa13 100644 --- a/ext/js/pages/settings/dictionary-controller.js +++ b/ext/js/pages/settings/dictionary-controller.js @@ -190,10 +190,9 @@ class DictionaryEntry { } class DictionaryController { - constructor(settingsController, modalController, storageController, statusFooter) { + constructor(settingsController, modalController, statusFooter) { this._settingsController = settingsController; this._modalController = modalController; - this._storageController = storageController; this._statusFooter = statusFooter; this._dictionaries = null; this._dictionaryEntries = []; @@ -436,7 +435,6 @@ class DictionaryController { const index = this._dictionaryEntries.findIndex((entry) => entry.dictionaryTitle === dictionaryTitle); if (index < 0) { return; } - const storageController = this._storageController; const statusFooter = this._statusFooter; const {node} = this._dictionaryEntries[index]; const progressSelector = '.dictionary-delete-progress'; @@ -483,7 +481,7 @@ class DictionaryController { if (statusFooter !== null) { statusFooter.setTaskActive(progressSelector, false); } this._setButtonsEnabled(true); this._isDeleting = false; - if (storageController !== null) { storageController.updateStats(); } + this._triggerStorageChanged(); } } @@ -547,6 +545,10 @@ class DictionaryController { } } + _triggerStorageChanged() { + yomichan.trigger('storageChanged'); + } + static createDefaultDictionarySettings() { return { enabled: false, diff --git a/ext/js/pages/settings/dictionary-import-controller.js b/ext/js/pages/settings/dictionary-import-controller.js index 1389b7f0..ce724263 100644 --- a/ext/js/pages/settings/dictionary-import-controller.js +++ b/ext/js/pages/settings/dictionary-import-controller.js @@ -22,10 +22,9 @@ */ class DictionaryImportController { - constructor(settingsController, modalController, storageController, statusFooter) { + constructor(settingsController, modalController, statusFooter) { this._settingsController = settingsController; this._modalController = modalController; - this._storageController = storageController; this._statusFooter = statusFooter; this._modifying = false; this._purgeButton = null; @@ -92,7 +91,6 @@ class DictionaryImportController { if (this._modifying) { return; } const purgeNotification = this._purgeNotification; - const storageController = this._storageController; const prevention = this._preventPageExit(); try { @@ -114,7 +112,7 @@ class DictionaryImportController { if (purgeNotification !== null) { purgeNotification.hidden = true; } this._setSpinnerVisible(false); this._setModifying(false); - if (storageController !== null) { storageController.updateStats(); } + this._triggerStorageChanged(); } } @@ -122,7 +120,6 @@ class DictionaryImportController { if (this._modifying) { return; } const statusFooter = this._statusFooter; - const storageController = this._storageController; const importInfo = document.querySelector('#dictionary-import-info'); const progressSelector = '.dictionary-import-progress'; const progressContainers = [ @@ -156,7 +153,7 @@ class DictionaryImportController { const statusString = `${percent.toFixed(0)}%`; for (const progressBar of progressBars) { progressBar.style.width = cssString; } for (const label of statusLabels) { label.textContent = statusString; } - if (storageController !== null) { storageController.updateStats(); } + this._triggerStorageChanged(); }; const fileCount = files.length; @@ -186,7 +183,7 @@ class DictionaryImportController { } this._setSpinnerVisible(false); this._setModifying(false); - if (storageController !== null) { storageController.updateStats(); } + this._triggerStorageChanged(); } } @@ -342,4 +339,8 @@ class DictionaryImportController { } return errors; } + + _triggerStorageChanged() { + yomichan.trigger('storageChanged'); + } } diff --git a/ext/js/pages/settings/main.js b/ext/js/pages/settings/main.js index 9785ee0e..3ccf2dd6 100644 --- a/ext/js/pages/settings/main.js +++ b/ext/js/pages/settings/main.js @@ -25,6 +25,7 @@ * GenericSettingController * ModalController * PermissionsToggleController + * PersistentStorageController * PopupPreviewController * ProfileController * ScanInputsController @@ -63,7 +64,10 @@ async function setupEnvironmentInfo() { const settingsController = new SettingsController(optionsFull.profileCurrent); settingsController.prepare(); - const storageController = new StorageController(); + const persistentStorageController = new PersistentStorageController(); + persistentStorageController.prepare(); + + const storageController = new StorageController(persistentStorageController); storageController.prepare(); const genericSettingController = new GenericSettingController(settingsController); @@ -81,10 +85,10 @@ async function setupEnvironmentInfo() { const profileController = new ProfileController(settingsController, modalController); profileController.prepare(); - const dictionaryController = new DictionaryController(settingsController, modalController, storageController, null); + const dictionaryController = new DictionaryController(settingsController, modalController, null); dictionaryController.prepare(); - const dictionaryImportController = new DictionaryImportController(settingsController, modalController, storageController, null); + const dictionaryImportController = new DictionaryImportController(settingsController, modalController, null); dictionaryImportController.prepare(); const ankiController = new AnkiController(settingsController); diff --git a/ext/js/pages/settings/persistent-storage-controller.js b/ext/js/pages/settings/persistent-storage-controller.js new file mode 100644 index 00000000..e46dbe87 --- /dev/null +++ b/ext/js/pages/settings/persistent-storage-controller.js @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class PersistentStorageController { + constructor() { + this._persistentStorageCheckbox = false; + } + + async prepare() { + this._persistentStorageCheckbox = document.querySelector('#storage-persistent-checkbox'); + this._persistentStorageCheckbox.addEventListener('change', this._onPersistentStorageCheckboxChange.bind(this), false); + + const button = document.querySelector('#storage-persistent-button'); + if (button !== null) { + button.hidden = false; + button.addEventListener('click', this._onPersistStorageButtonClick.bind(this), false); + } + + if (!this._isPersistentStorageSupported()) { return; } + + const info = document.querySelector('#storage-persistent-info'); + if (info !== null) { info.hidden = false; } + + const isStoragePeristent = await this.isStoragePeristent(); + this._updateCheckbox(isStoragePeristent); + } + + async isStoragePeristent() { + try { + return await navigator.storage.persisted(); + } catch (e) { + // NOP + } + return false; + } + + // Private + + _onPersistentStorageCheckboxChange(e) { + const node = e.currentTarget; + if (node.checked) { + node.checked = false; + this._attemptPersistStorage(); + } else { + node.checked = true; + } + } + + _onPersistStorageButtonClick() { + const {checked} = this._persistentStorageCheckbox; + if (checked) { return; } + this._persistentStorageCheckbox.checked = !checked; + this._persistentStorageCheckbox.dispatchEvent(new Event('change')); + } + + async _attemptPersistStorage() { + let isStoragePeristent = false; + try { + isStoragePeristent = await navigator.storage.persist(); + } catch (e) { + // NOP + } + + this._updateCheckbox(isStoragePeristent); + + const node = document.querySelector('#storage-persistent-fail-warning'); + if (node !== null) { node.hidden = isStoragePeristent; } + + yomichan.trigger('storageChanged'); + } + + _isPersistentStorageSupported() { + return isObject(navigator.storage) && typeof navigator.storage.persist === 'function'; + } + + _updateCheckbox(isStoragePeristent) { + this._persistentStorageCheckbox.checked = isStoragePeristent; + this._persistentStorageCheckbox.readOnly = isStoragePeristent; + } +} diff --git a/ext/js/pages/settings/settings-main.js b/ext/js/pages/settings/settings-main.js index 273142cd..3618836c 100644 --- a/ext/js/pages/settings/settings-main.js +++ b/ext/js/pages/settings/settings-main.js @@ -30,6 +30,7 @@ * ModalController * NestedPopupsController * PermissionsToggleController + * PersistentStorageController * PopupPreviewController * PopupWindowController * ProfileController @@ -79,13 +80,16 @@ async function setupGenericSettingsController(genericSettingController) { const settingsController = new SettingsController(optionsFull.profileCurrent); settingsController.prepare(); - const storageController = new StorageController(); + const persistentStorageController = new PersistentStorageController(); + persistentStorageController.prepare(); + + const storageController = new StorageController(persistentStorageController); storageController.prepare(); - const dictionaryController = new DictionaryController(settingsController, modalController, storageController, statusFooter); + const dictionaryController = new DictionaryController(settingsController, modalController, statusFooter); dictionaryController.prepare(); - const dictionaryImportController = new DictionaryImportController(settingsController, modalController, storageController, statusFooter); + const dictionaryImportController = new DictionaryImportController(settingsController, modalController, statusFooter); dictionaryImportController.prepare(); const genericSettingController = new GenericSettingController(settingsController); diff --git a/ext/js/pages/settings/storage-controller.js b/ext/js/pages/settings/storage-controller.js index c27c8690..d92ecff0 100644 --- a/ext/js/pages/settings/storage-controller.js +++ b/ext/js/pages/settings/storage-controller.js @@ -16,11 +16,11 @@ */ class StorageController { - constructor() { + constructor(persistentStorageController) { + this._persistentStorageController = persistentStorageController; this._mostRecentStorageEstimate = null; this._storageEstimateFailed = false; this._isUpdating = false; - this._persistentStorageCheckbox = false; this._storageUsageNode = null; this._storageQuotaNode = null; this._storageUseFiniteNodes = null; @@ -30,7 +30,6 @@ class StorageController { } prepare() { - this._persistentStorageCheckbox = document.querySelector('#storage-persistent-checkbox'); this._storageUsageNodes = document.querySelectorAll('.storage-usage'); this._storageQuotaNodes = document.querySelectorAll('.storage-quota'); this._storageUseFiniteNodes = document.querySelectorAll('.storage-use-finite'); @@ -38,13 +37,23 @@ class StorageController { this._storageUseValidNodes = document.querySelectorAll('.storage-use-valid'); this._storageUseInvalidNodes = document.querySelectorAll('.storage-use-invalid'); - this._preparePersistentStorage(); - this.updateStats(); - this._persistentStorageCheckbox.addEventListener('change', this._onPersistentStorageCheckboxChange.bind(this), false); - document.querySelector('#storage-refresh').addEventListener('click', this.updateStats.bind(this), false); + document.querySelector('#storage-refresh').addEventListener('click', this._onStorageRefreshButtonClick.bind(this), false); + yomichan.on('storageChanged', this._onStorageChanged.bind(this)); + + this._updateStats(); } - async updateStats() { + // Private + + _onStorageRefreshButtonClick() { + this._updateStats(); + } + + _onStorageChanged() { + this._updateStats(); + } + + async _updateStats() { if (this._isUpdating) { return; } try { @@ -54,7 +63,7 @@ class StorageController { const valid = (estimate !== null); // Firefox reports usage as 0 when persistent storage is enabled. - const finite = valid && (estimate.usage > 0 || !(await this._isStoragePeristent())); + const finite = valid && (estimate.usage > 0 || !(await this._persistentStorageController.isStoragePeristent())); if (finite) { for (const node of this._storageUsageNodes) { node.textContent = this._bytesToLabeledString(estimate.usage); @@ -77,61 +86,6 @@ class StorageController { // Private - async _preparePersistentStorage() { - if (!(navigator.storage && navigator.storage.persist)) { - // Not supported - return; - } - - const info = document.querySelector('#storage-persistent-info'); - if (info !== null) { info.hidden = false; } - - const isStoragePeristent = await this._isStoragePeristent(); - this._updateCheckbox(isStoragePeristent); - - const button = document.querySelector('#storage-persistent-button'); - if (button !== null) { - button.hidden = false; - button.addEventListener('click', this._onPersistStorageButtonClick.bind(this), false); - } - } - - _onPersistentStorageCheckboxChange(e) { - const node = e.currentTarget; - if (!node.checked) { - node.checked = true; - return; - } - this._attemptPersistStorage(); - } - - _onPersistStorageButtonClick() { - const {checked} = this._persistentStorageCheckbox; - if (checked) { return; } - this._persistentStorageCheckbox.checked = !checked; - this._persistentStorageCheckbox.dispatchEvent(new Event('change')); - } - - async _attemptPersistStorage() { - if (await this._isStoragePeristent()) { return; } - - let isStoragePeristent = false; - try { - isStoragePeristent = await navigator.storage.persist(); - } catch (e) { - // NOP - } - - this._updateCheckbox(isStoragePeristent); - - if (isStoragePeristent) { - this.updateStats(); - } else { - const node = document.querySelector('#storage-persistent-fail-warning'); - if (node !== null) { node.hidden = false; } - } - } - async _storageEstimate() { if (this._storageEstimateFailed && this._mostRecentStorageEstimate === null) { return null; @@ -159,21 +113,6 @@ class StorageController { return `${label}${labels[labelIndex]}`; } - async _isStoragePeristent() { - try { - return await navigator.storage.persisted(); - } catch (e) { - // NOP - } - return false; - } - - _updateCheckbox(isStoragePeristent) { - const checkbox = this._persistentStorageCheckbox; - checkbox.checked = isStoragePeristent; - checkbox.readOnly = isStoragePeristent; - } - _setElementsVisible(elements, visible) { visible = !visible; for (const element of elements) { diff --git a/ext/js/pages/welcome-main.js b/ext/js/pages/welcome-main.js index 27d0af6a..90067282 100644 --- a/ext/js/pages/welcome-main.js +++ b/ext/js/pages/welcome-main.js @@ -62,10 +62,10 @@ async function setupGenericSettingsController(genericSettingController) { const settingsController = new SettingsController(optionsFull.profileCurrent); settingsController.prepare(); - const dictionaryController = new DictionaryController(settingsController, modalController, null, statusFooter); + const dictionaryController = new DictionaryController(settingsController, modalController, statusFooter); dictionaryController.prepare(); - const dictionaryImportController = new DictionaryImportController(settingsController, modalController, null, statusFooter); + const dictionaryImportController = new DictionaryImportController(settingsController, modalController, statusFooter); dictionaryImportController.prepare(); const genericSettingController = new GenericSettingController(settingsController); diff --git a/ext/settings-old.html b/ext/settings-old.html index be791bb2..562ca95d 100644 --- a/ext/settings-old.html +++ b/ext/settings-old.html @@ -1319,6 +1319,7 @@ + diff --git a/ext/settings.html b/ext/settings.html index 023e8026..9a176886 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -3270,6 +3270,7 @@ +