Storage controller refactor (#905)

* Use hidden instead of storage-hidden class

* Refactor storage events

* Make ID more generic

* Update how persistent storage is activated

* Add null checks

* Update HTML/ID

* Disallow disabling persistent storage

* Refactoring

* Update more IDs

* Disable multiple simultaneous stats updates

* Store node references

* Move undefined assignment
This commit is contained in:
toasted-nutbread 2020-10-10 16:54:52 -04:00 committed by GitHub
parent 3174f3c657
commit 199dd7d763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 48 deletions

View File

@ -16,7 +16,7 @@
*/ */
.storage-hidden { .help-block[hidden] {
display: none; display: none;
} }

View File

@ -20,15 +20,35 @@ class StorageController {
this._mostRecentStorageEstimate = null; this._mostRecentStorageEstimate = null;
this._storageEstimateFailed = false; this._storageEstimateFailed = false;
this._isUpdating = false; this._isUpdating = false;
this._persistentStorageCheckbox = false;
this._storageUsageNode = null;
this._storageQuotaNode = null;
this._storageUseFiniteNode = null;
this._storageUseInfiniteNode = null;
this._storageUseUndefinedNode = null;
this._storageUseNode = null;
this._storageErrorNode = null;
} }
prepare() { prepare() {
this._persistentStorageCheckbox = document.querySelector('#storage-persistent-checkbox');
this._storageUsageNode = document.querySelector('#storage-usage');
this._storageQuotaNode = document.querySelector('#storage-quota');
this._storageUseFiniteNode = document.querySelector('#storage-use-finite');
this._storageUseInfiniteNode = document.querySelector('#storage-use-infinite');
this._storageUseUndefinedNode = document.querySelector('#storage-use-undefined');
this._storageUseNode = document.querySelector('#storage-use');
this._storageErrorNode = document.querySelector('#storage-error');
this._preparePersistentStorage(); this._preparePersistentStorage();
this.updateStats(); 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.updateStats.bind(this), false);
} }
async updateStats() { async updateStats() {
if (this._isUpdating) { return; }
try { try {
this._isUpdating = true; this._isUpdating = true;
@ -39,15 +59,16 @@ class StorageController {
// Firefox reports usage as 0 when persistent storage is enabled. // Firefox reports usage as 0 when persistent storage is enabled.
const finite = (estimate.usage > 0 || !(await this._isStoragePeristent())); const finite = (estimate.usage > 0 || !(await this._isStoragePeristent()));
if (finite) { if (finite) {
document.querySelector('#storage-usage').textContent = this._bytesToLabeledString(estimate.usage); this._storageUsageNode.textContent = this._bytesToLabeledString(estimate.usage);
document.querySelector('#storage-quota').textContent = this._bytesToLabeledString(estimate.quota); this._storageQuotaNode.textContent = this._bytesToLabeledString(estimate.quota);
} }
document.querySelector('#storage-use-finite').classList.toggle('storage-hidden', !finite); this._storageUseFiniteNode.hidden = !finite;
document.querySelector('#storage-use-infinite').classList.toggle('storage-hidden', finite); this._storageUseInfiniteNode.hidden = finite;
} }
document.querySelector('#storage-use').classList.toggle('storage-hidden', !valid); if (this._storageUseUndefinedNode !== null) { this._storageUseUndefinedNode.hidden = !valid; }
document.querySelector('#storage-error').classList.toggle('storage-hidden', valid); if (this._storageUseNode !== null) { this._storageUseNode.hidden = !valid; }
if (this._storageErrorNode !== null) { this._storageErrorNode.hidden = valid; }
return valid; return valid;
} finally { } finally {
@ -63,35 +84,53 @@ class StorageController {
return; return;
} }
const info = document.querySelector('#storage-persist-info'); const info = document.querySelector('#storage-persistent-info');
const button = document.querySelector('#storage-persist-button'); if (info !== null) { info.hidden = false; }
const checkbox = document.querySelector('#storage-persist-button-checkbox');
info.classList.remove('storage-hidden'); const isStoragePeristent = await this._isStoragePeristent();
button.classList.remove('storage-hidden'); this._updateCheckbox(isStoragePeristent);
let persisted = await this._isStoragePeristent(); const button = document.querySelector('#storage-persistent-button');
checkbox.checked = persisted; if (button !== null) {
button.hidden = false;
button.addEventListener('click', this._onPersistStorageButtonClick.bind(this), false);
}
}
button.addEventListener('click', async () => { _onPersistentStorageCheckboxChange(e) {
if (persisted) { const node = e.currentTarget;
return; if (!node.checked) {
} node.checked = true;
let result = false; return;
try { }
result = await navigator.storage.persist(); this._attemptPersistStorage();
} catch (e) { }
// NOP
}
if (result) { _onPersistStorageButtonClick() {
persisted = true; const {checked} = this._persistentStorageCheckbox;
checkbox.checked = true; if (checked) { return; }
this.updateStats(); this._persistentStorageCheckbox.checked = !checked;
} else { this._persistentStorageCheckbox.dispatchEvent(new Event('change'));
document.querySelector('.storage-persist-fail-warning').classList.remove('storage-hidden'); }
}
}, false); 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() { async _storageEstimate() {
@ -128,4 +167,10 @@ class StorageController {
} }
return false; return false;
} }
_updateCheckbox(isStoragePeristent) {
const checkbox = this._persistentStorageCheckbox;
checkbox.checked = isStoragePeristent;
checkbox.readOnly = isStoragePeristent;
}
} }

View File

@ -796,7 +796,7 @@
<h3>Storage</h3> <h3>Storage</h3>
</div> </div>
<div id="storage-persist-info" class="storage-hidden"> <div id="storage-persistent-info" hidden>
<p class="help-block"> <p class="help-block">
Web browsers may sometimes clear stored data if the device is running low on storage space. Web browsers may sometimes clear stored data if the device is running low on storage space.
This can result in the stored dictionary data being deleted unexpectedly, causing Yomichan to stop working for no apparent reason. This can result in the stored dictionary data being deleted unexpectedly, causing Yomichan to stop working for no apparent reason.
@ -804,16 +804,19 @@
</p> </p>
</div> </div>
<div id="storage-use" class="storage-hidden"> <div id="storage-use" hidden>
<p class="help-block storage-hidden" id="storage-use-finite"> <p class="help-block" id="storage-use-undefined">
Yomichan is using an indeterminate amount of storage.
</p>
<p class="help-block" id="storage-use-finite" hidden>
Yomichan is using approximately <strong id="storage-usage"></strong> of <strong id="storage-quota"></strong>. Yomichan is using approximately <strong id="storage-usage"></strong> of <strong id="storage-quota"></strong>.
</p> </p>
<p class="help-block storage-hidden" id="storage-use-infinite"> <p class="help-block" id="storage-use-infinite" hidden>
Yomichan is permitted <strong>unlimited storage</strong>. Yomichan is permitted <strong>unlimited storage</strong>.
</p> </p>
</div> </div>
<div id="storage-error" class="storage-hidden"> <div id="storage-error" hidden>
<p class="help-block"> <p class="help-block">
Could not detect how much storage Yomichan is using. Could not detect how much storage Yomichan is using.
</p> </p>
@ -832,22 +835,24 @@
<div> <div>
<button class="btn btn-default" id="storage-refresh"><span class="btn-inner-middle">Refresh</span></button> <button class="btn btn-default" id="storage-refresh"><span class="btn-inner-middle">Refresh</span></button>
<button class="btn btn-default storage-hidden ignore-form-changes" id="storage-persist-button"><span class="storage-button-inner"><input type="checkbox" class="btn-inner-middle storage-button-checkbox" id="storage-persist-button-checkbox" readonly /><span class="btn-inner-middle">Persistent Storage</span></span></button> <button class="btn btn-default ignore-form-changes" id="storage-persistent-button" hidden><span class="storage-button-inner"><input type="checkbox" class="btn-inner-middle storage-button-checkbox" id="storage-persistent-checkbox" readonly /><span class="btn-inner-middle">Persistent Storage</span></span></button>
</div> </div>
<p></p> <p></p>
<div data-show-for-browser="firefox-mobile"><div class="alert alert-warning storage-persist-fail-warning storage-hidden"> <div id="storage-persistent-fail-warning" hidden>
<p>It may not be possible to enable Persistent Storage on Firefox for Android.</p> <div data-show-for-browser="firefox-mobile"><div class="alert alert-warning">
</div></div> <p>It may not be possible to enable Persistent Storage on Firefox for Android.</p>
</div></div>
<div data-show-for-browser="chrome"><div class="alert alert-warning storage-persist-fail-warning storage-hidden"> <div data-show-for-browser="chrome"><div class="alert alert-warning">
<p> <p>
It may not be possible to enable Persistent Storage on Chrome-based browsers. It may not be possible to enable Persistent Storage on Chrome-based browsers.
However, the Yomichan extension has permission for unlimited storage which should However, the Yomichan extension has permission for unlimited storage which should
prevent Chrome from deleting data.<sup><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=680392#c15" target="_blank" rel="noopener">[1]</a></sup> prevent Chrome from deleting data.<sup><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=680392#c15" target="_blank" rel="noopener">[1]</a></sup>
</p> </p>
</div></div> </div></div>
</div>
</div> </div>
<div> <div>