Persistent storage controller (#1478)

* Add PersistentStorageController

* Update DictionaryController

* Update DictionaryImportController

* Update when storage stats are updated
This commit is contained in:
toasted-nutbread 2021-03-02 20:27:51 -05:00 committed by GitHub
parent 571b865ae5
commit 4208595293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 144 additions and 98 deletions

View File

@ -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,

View File

@ -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');
}
}

View File

@ -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);

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -1319,6 +1319,7 @@
<script src="/js/pages/settings/modal-controller.js"></script>
<script src="/js/pages/settings/modal-jquery.js"></script>
<script src="/js/pages/settings/permissions-toggle-controller.js"></script>
<script src="/js/pages/settings/persistent-storage-controller.js"></script>
<script src="/js/pages/settings/popup-preview-controller.js"></script>
<script src="/js/pages/settings/profile-conditions-ui.js"></script>
<script src="/js/pages/settings/profile-controller.js"></script>

View File

@ -3270,6 +3270,7 @@
<script src="/js/pages/settings/modal-controller.js"></script>
<script src="/js/pages/settings/nested-popups-controller.js"></script>
<script src="/js/pages/settings/permissions-toggle-controller.js"></script>
<script src="/js/pages/settings/persistent-storage-controller.js"></script>
<script src="/js/pages/settings/popup-preview-controller.js"></script>
<script src="/js/pages/settings/popup-window-controller.js"></script>
<script src="/js/pages/settings/profile-conditions-ui.js"></script>