Convert dictionaries.js and storage.js to classes (#570)

* Convert dictionaries.js to a class

* Remove storage spinner

* Convert storage.js to a class

* Move dataset assignments into main.js
This commit is contained in:
toasted-nutbread 2020-05-29 20:25:22 -04:00 committed by GitHub
parent c62f980f37
commit 418e8a57bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 432 additions and 435 deletions

View File

@ -18,7 +18,7 @@
#anki-spinner, #anki-spinner,
#dict-spinner, #dict-import-progress, #dict-spinner, #dict-import-progress,
.storage-hidden, #storage-spinner { .storage-hidden {
display: none; display: none;
} }

View File

@ -22,14 +22,9 @@
* getOptionsFullMutable * getOptionsFullMutable
* getOptionsMutable * getOptionsMutable
* settingsSaveOptions * settingsSaveOptions
* storageEstimate
* storageUpdateStats
* utilBackgroundIsolate * utilBackgroundIsolate
*/ */
let dictionaryUI = null;
class SettingsDictionaryListUI { class SettingsDictionaryListUI {
constructor(container, template, extraContainer, extraTemplate) { constructor(container, template, extraContainer, extraTemplate) {
this.container = container; this.container = container;
@ -308,13 +303,13 @@ class SettingsDictionaryEntryUI {
await api.deleteDictionary(this.dictionaryInfo.title, onProgress); await api.deleteDictionary(this.dictionaryInfo.title, onProgress);
} catch (e) { } catch (e) {
dictionaryErrorsShow([e]); this.dictionaryErrorsShow([e]);
} finally { } finally {
prevention.end(); prevention.end();
this.isDeleting = false; this.isDeleting = false;
progress.hidden = true; progress.hidden = true;
onDatabaseUpdated(); this.onDatabaseUpdated();
} }
} }
@ -388,59 +383,63 @@ class SettingsDictionaryExtraUI {
} }
} }
class DictionaryController {
constructor(storageController) {
this._storageController = storageController;
this._dictionaryUI = null;
this._dictionaryErrorToStringOverrides = [
[
'A mutation operation was attempted on a database that did not allow mutations.',
'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
],
[
'The operation failed for reasons unrelated to the database itself and not covered by any other error code.',
'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
],
[
'BulkError',
'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
]
];
}
async function dictSettingsInitialize() { async prepare() {
dictionaryUI = new SettingsDictionaryListUI( this._dictionaryUI = new SettingsDictionaryListUI(
document.querySelector('#dict-groups'), document.querySelector('#dict-groups'),
document.querySelector('#dict-template'), document.querySelector('#dict-template'),
document.querySelector('#dict-groups-extra'), document.querySelector('#dict-groups-extra'),
document.querySelector('#dict-extra-template') document.querySelector('#dict-extra-template')
); );
dictionaryUI.save = settingsSaveOptions; this._dictionaryUI.save = settingsSaveOptions;
document.querySelector('#dict-purge-button').addEventListener('click', onDictionaryPurgeButtonClick, false); document.querySelector('#dict-purge-button').addEventListener('click', this._onPurgeButtonClick.bind(this), false);
document.querySelector('#dict-purge-confirm').addEventListener('click', onDictionaryPurge, false); document.querySelector('#dict-purge-confirm').addEventListener('click', this._onPurgeConfirmButtonClick.bind(this), false);
document.querySelector('#dict-file-button').addEventListener('click', onDictionaryImportButtonClick, false); document.querySelector('#dict-file-button').addEventListener('click', this._onImportButtonClick.bind(this), false);
document.querySelector('#dict-file').addEventListener('change', onDictionaryImport, false); document.querySelector('#dict-file').addEventListener('change', this._onImportFileChange.bind(this), false);
document.querySelector('#dict-main').addEventListener('change', onDictionaryMainChanged, false); document.querySelector('#dict-main').addEventListener('change', this._onDictionaryMainChanged.bind(this), false);
document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', onDatabaseEnablePrefixWildcardSearchesChanged, false); document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', this._onDatabaseEnablePrefixWildcardSearchesChanged.bind(this), false);
await onDictionaryOptionsChanged(); await this.optionsChanged();
await onDatabaseUpdated(); await this._onDatabaseUpdated();
} }
async function onDictionaryOptionsChanged() { async optionsChanged() {
if (dictionaryUI === null) { return; } if (this._dictionaryUI === null) { return; }
const optionsContext = getOptionsContext(); const optionsContext = getOptionsContext();
const options = await getOptionsMutable(optionsContext); const options = await getOptionsMutable(optionsContext);
dictionaryUI.setOptionsDictionaries(options.dictionaries); this._dictionaryUI.setOptionsDictionaries(options.dictionaries);
const optionsFull = await api.optionsGetFull(); const optionsFull = await api.optionsGetFull();
document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported; document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported;
await updateMainDictionarySelectValue(); await this._updateMainDictionarySelectValue();
}
async function onDatabaseUpdated() {
try {
const dictionaries = await api.getDictionaryInfo();
dictionaryUI.setDictionaries(dictionaries);
document.querySelector('#dict-warning').hidden = (dictionaries.length > 0);
updateMainDictionarySelectOptions(dictionaries);
await updateMainDictionarySelectValue();
const {counts, total} = await api.getDictionaryCounts(dictionaries.map((v) => v.title), true);
dictionaryUI.setCounts(counts, total);
} catch (e) {
dictionaryErrorsShow([e]);
} }
}
function updateMainDictionarySelectOptions(dictionaries) { // Private
_updateMainDictionarySelectOptions(dictionaries) {
const select = document.querySelector('#dict-main'); const select = document.querySelector('#dict-main');
select.textContent = ''; // Empty select.textContent = ''; // Empty
@ -458,9 +457,9 @@ function updateMainDictionarySelectOptions(dictionaries) {
option.textContent = title; option.textContent = title;
select.appendChild(option); select.appendChild(option);
} }
} }
async function updateMainDictionarySelectValue() { async _updateMainDictionarySelectValue() {
const optionsContext = getOptionsContext(); const optionsContext = getOptionsContext();
const options = await api.optionsGet(optionsContext); const options = await api.optionsGet(optionsContext);
@ -492,32 +491,16 @@ async function updateMainDictionarySelectValue() {
} }
select.value = value; select.value = value;
}
async function onDictionaryMainChanged(e) {
const select = e.target;
const value = select.value;
const missingNodeOption = select.querySelector('option[data-not-installed=true]');
if (missingNodeOption !== null && missingNodeOption.value !== value) {
missingNodeOption.parentNode.removeChild(missingNodeOption);
} }
const optionsContext = getOptionsContext(); _dictionaryErrorToString(error) {
const options = await getOptionsMutable(optionsContext);
options.general.mainDictionary = value;
await settingsSaveOptions();
}
function dictionaryErrorToString(error) {
if (error.toString) { if (error.toString) {
error = error.toString(); error = error.toString();
} else { } else {
error = `${error}`; error = `${error}`;
} }
for (const [match, subst] of dictionaryErrorToString.overrides) { for (const [match, subst] of this._dictionaryErrorToStringOverrides) {
if (error.includes(match)) { if (error.includes(match)) {
error = subst; error = subst;
break; break;
@ -525,23 +508,9 @@ function dictionaryErrorToString(error) {
} }
return error; return error;
} }
dictionaryErrorToString.overrides = [
[
'A mutation operation was attempted on a database that did not allow mutations.',
'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
],
[
'The operation failed for reasons unrelated to the database itself and not covered by any other error code.',
'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
],
[
'BulkError',
'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
]
];
function dictionaryErrorsShow(errors) { _dictionaryErrorsShow(errors) {
const dialog = document.querySelector('#dict-error'); const dialog = document.querySelector('#dict-error');
dialog.textContent = ''; dialog.textContent = '';
@ -549,7 +518,7 @@ function dictionaryErrorsShow(errors) {
const uniqueErrors = new Map(); const uniqueErrors = new Map();
for (let e of errors) { for (let e of errors) {
yomichan.logError(e); yomichan.logError(e);
e = dictionaryErrorToString(e); e = this._dictionaryErrorToString(e);
let count = uniqueErrors.get(e); let count = uniqueErrors.get(e);
if (typeof count === 'undefined') { if (typeof count === 'undefined') {
count = 0; count = 0;
@ -574,29 +543,69 @@ function dictionaryErrorsShow(errors) {
} else { } else {
dialog.hidden = true; dialog.hidden = true;
} }
} }
_dictionarySpinnerShow(show) {
function dictionarySpinnerShow(show) {
const spinner = $('#dict-spinner'); const spinner = $('#dict-spinner');
if (show) { if (show) {
spinner.show(); spinner.show();
} else { } else {
spinner.hide(); spinner.hide();
} }
} }
function onDictionaryImportButtonClick() { _dictReadFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsBinaryString(file);
});
}
async _onDatabaseUpdated() {
try {
const dictionaries = await api.getDictionaryInfo();
this._dictionaryUI.setDictionaries(dictionaries);
document.querySelector('#dict-warning').hidden = (dictionaries.length > 0);
this._updateMainDictionarySelectOptions(dictionaries);
await this._updateMainDictionarySelectValue();
const {counts, total} = await api.getDictionaryCounts(dictionaries.map((v) => v.title), true);
this._dictionaryUI.setCounts(counts, total);
} catch (e) {
this._dictionaryErrorsShow([e]);
}
}
async _onDictionaryMainChanged(e) {
const select = e.target;
const value = select.value;
const missingNodeOption = select.querySelector('option[data-not-installed=true]');
if (missingNodeOption !== null && missingNodeOption.value !== value) {
missingNodeOption.parentNode.removeChild(missingNodeOption);
}
const optionsContext = getOptionsContext();
const options = await getOptionsMutable(optionsContext);
options.general.mainDictionary = value;
await settingsSaveOptions();
}
_onImportButtonClick() {
const dictFile = document.querySelector('#dict-file'); const dictFile = document.querySelector('#dict-file');
dictFile.click(); dictFile.click();
} }
function onDictionaryPurgeButtonClick(e) { _onPurgeButtonClick(e) {
e.preventDefault(); e.preventDefault();
$('#dict-purge-modal').modal('show'); $('#dict-purge-modal').modal('show');
} }
async function onDictionaryPurge(e) { async _onPurgeConfirmButtonClick(e) {
e.preventDefault(); e.preventDefault();
$('#dict-purge-modal').modal('hide'); $('#dict-purge-modal').modal('hide');
@ -609,8 +618,8 @@ async function onDictionaryPurge(e) {
try { try {
prevention.start(); prevention.start();
dictionaryErrorsShow(null); this._dictionaryErrorsShow(null);
dictionarySpinnerShow(true); this._dictionarySpinnerShow(true);
await api.purgeDatabase(); await api.purgeDatabase();
for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) {
@ -619,24 +628,22 @@ async function onDictionaryPurge(e) {
} }
await settingsSaveOptions(); await settingsSaveOptions();
onDatabaseUpdated(); this._onDatabaseUpdated();
} catch (err) { } catch (err) {
dictionaryErrorsShow([err]); this._dictionaryErrorsShow([err]);
} finally { } finally {
prevention.end(); prevention.end();
dictionarySpinnerShow(false); this._dictionarySpinnerShow(false);
dictControls.show(); dictControls.show();
dictProgress.hidden = true; dictProgress.hidden = true;
if (storageEstimate.mostRecent !== null) { this._storageController.updateStats();
storageUpdateStats();
} }
} }
}
async function onDictionaryImport(e) { async _onImportFileChange(e) {
const files = [...e.target.files]; const files = [...e.target.files];
e.target.value = null; e.target.value = null;
@ -649,15 +656,13 @@ async function onDictionaryImport(e) {
try { try {
prevention.start(); prevention.start();
dictionaryErrorsShow(null); this._dictionaryErrorsShow(null);
dictionarySpinnerShow(true); this._dictionarySpinnerShow(true);
const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`); const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`);
const updateProgress = (total, current) => { const updateProgress = (total, current) => {
setProgress(current / total * 100.0); setProgress(current / total * 100.0);
if (storageEstimate.mostRecent !== null && !storageUpdateStats.isUpdating) { this._storageController.updateStats();
storageUpdateStats();
}
}; };
const optionsFull = await api.optionsGetFull(); const optionsFull = await api.optionsGetFull();
@ -673,7 +678,7 @@ async function onDictionaryImport(e) {
dictImportInfo.textContent = `(${i + 1} of ${ii})`; dictImportInfo.textContent = `(${i + 1} of ${ii})`;
} }
const archiveContent = await dictReadFile(files[i]); const archiveContent = await this._dictReadFile(files[i]);
const {result, errors} = await api.importDictionaryArchive(archiveContent, importDetails, updateProgress); const {result, errors} = await api.importDictionaryArchive(archiveContent, importDetails, updateProgress);
for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) {
const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions(); const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions();
@ -689,16 +694,16 @@ async function onDictionaryImport(e) {
if (errors.length > 0) { if (errors.length > 0) {
const errors2 = errors.map((error) => jsonToError(error)); const errors2 = errors.map((error) => jsonToError(error));
errors2.push(`Dictionary may not have been imported properly: ${errors2.length} error${errors2.length === 1 ? '' : 's'} reported.`); errors2.push(`Dictionary may not have been imported properly: ${errors2.length} error${errors2.length === 1 ? '' : 's'} reported.`);
dictionaryErrorsShow(errors2); this._dictionaryErrorsShow(errors2);
} }
onDatabaseUpdated(); this._onDatabaseUpdated();
} }
} catch (err) { } catch (err) {
dictionaryErrorsShow([err]); this._dictionaryErrorsShow([err]);
} finally { } finally {
prevention.end(); prevention.end();
dictionarySpinnerShow(false); this._dictionarySpinnerShow(false);
dictImportInfo.hidden = false; dictImportInfo.hidden = false;
dictImportInfo.textContent = ''; dictImportInfo.textContent = '';
@ -706,22 +711,13 @@ async function onDictionaryImport(e) {
dictControls.show(); dictControls.show();
dictProgress.hide(); dictProgress.hide();
} }
} }
function dictReadFile(file) { async _onDatabaseEnablePrefixWildcardSearchesChanged(e) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsBinaryString(file);
});
}
async function onDatabaseEnablePrefixWildcardSearchesChanged(e) {
const optionsFull = await getOptionsFullMutable(); const optionsFull = await getOptionsFullMutable();
const v = !!e.target.checked; const v = !!e.target.checked;
if (optionsFull.global.database.prefixWildcardsSupported === v) { return; } if (optionsFull.global.database.prefixWildcardsSupported === v) { return; }
optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked; optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked;
await settingsSaveOptions(); await settingsSaveOptions();
}
} }

View File

@ -19,14 +19,13 @@
* AnkiController * AnkiController
* AnkiTemplatesController * AnkiTemplatesController
* AudioController * AudioController
* DictionaryController
* ProfileController * ProfileController
* SettingsBackup * SettingsBackup
* SettingsController * SettingsController
* StorageController
* api * api
* appearanceInitialize * appearanceInitialize
* dictSettingsInitialize
* onDictionaryOptionsChanged
* storageInfoInitialize
* utilBackend * utilBackend
* utilBackgroundIsolate * utilBackgroundIsolate
*/ */
@ -270,7 +269,9 @@ async function onOptionsUpdated({source}) {
if (ankiTemplatesController !== null) { if (ankiTemplatesController !== null) {
ankiTemplatesController.updateValue(); ankiTemplatesController.updateValue();
} }
onDictionaryOptionsChanged(); if (dictionaryController !== null) {
dictionaryController.optionsChanged();
}
if (ankiController !== null) { if (ankiController !== null) {
ankiController.optionsChanged(); ankiController.optionsChanged();
} }
@ -304,8 +305,15 @@ async function settingsPopulateModifierKeys() {
} }
} }
async function setupEnvironmentInfo() {
const {browser, platform} = await api.getEnvironmentInfo();
document.documentElement.dataset.browser = browser;
document.documentElement.dataset.operatingSystem = platform.os;
}
let ankiController = null; let ankiController = null;
let ankiTemplatesController = null; let ankiTemplatesController = null;
let dictionaryController = null;
async function onReady() { async function onReady() {
api.forwardLogsToBackend(); api.forwardLogsToBackend();
@ -314,22 +322,25 @@ async function onReady() {
const settingsController = new SettingsController(); const settingsController = new SettingsController();
settingsController.prepare(); settingsController.prepare();
setupEnvironmentInfo();
showExtensionInformation(); showExtensionInformation();
const storageController = new StorageController();
storageController.prepare();
await settingsPopulateModifierKeys(); await settingsPopulateModifierKeys();
formSetupEventListeners(); formSetupEventListeners();
appearanceInitialize(); appearanceInitialize();
new AudioController().prepare(); new AudioController().prepare();
await (new ProfileController()).prepare(); await (new ProfileController()).prepare();
await dictSettingsInitialize(); dictionaryController = new DictionaryController(storageController);
dictionaryController.prepare();
ankiController = new AnkiController(); ankiController = new AnkiController();
ankiController.prepare(); ankiController.prepare();
ankiTemplatesController = new AnkiTemplatesController(ankiController); ankiTemplatesController = new AnkiTemplatesController(ankiController);
ankiTemplatesController.prepare(); ankiTemplatesController.prepare();
new SettingsBackup().prepare(); new SettingsBackup().prepare();
storageInfoInitialize();
yomichan.on('optionsUpdated', onOptionsUpdated); yomichan.on('optionsUpdated', onOptionsUpdated);
} }

View File

@ -15,94 +15,49 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
/* global class StorageController {
* api constructor() {
*/ this._mostRecentStorageEstimate = null;
this._storageEstimateFailed = false;
function storageBytesToLabeledString(size) { this._isUpdating = false;
const base = 1000;
const labels = [' bytes', 'KB', 'MB', 'GB'];
let labelIndex = 0;
while (size >= base) {
size /= base;
++labelIndex;
} }
const label = labelIndex === 0 ? `${size}` : size.toFixed(1);
return `${label}${labels[labelIndex]}`;
}
async function storageEstimate() { prepare() {
this._preparePersistentStorage();
this.updateStats();
document.querySelector('#storage-refresh').addEventListener('click', this.updateStats.bind(this), false);
}
async updateStats() {
try { try {
return (storageEstimate.mostRecent = await navigator.storage.estimate()); this._isUpdating = true;
} catch (e) {
// NOP
}
return null;
}
storageEstimate.mostRecent = null;
async function isStoragePeristent() { const estimate = await this._storageEstimate();
try {
return await navigator.storage.persisted();
} catch (e) {
// NOP
}
return false;
}
async function storageInfoInitialize() {
storagePersistInitialize();
const {browser, platform} = await api.getEnvironmentInfo();
document.documentElement.dataset.browser = browser;
document.documentElement.dataset.operatingSystem = platform.os;
await storageShowInfo();
document.querySelector('#storage-refresh').addEventListener('click', storageShowInfo, false);
}
async function storageUpdateStats() {
storageUpdateStats.isUpdating = true;
const estimate = await storageEstimate();
const valid = (estimate !== null); const valid = (estimate !== null);
if (valid) { if (valid) {
// 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 isStoragePeristent())); const finite = (estimate.usage > 0 || !(await this._isStoragePeristent()));
if (finite) { if (finite) {
document.querySelector('#storage-usage').textContent = storageBytesToLabeledString(estimate.usage); document.querySelector('#storage-usage').textContent = this._bytesToLabeledString(estimate.usage);
document.querySelector('#storage-quota').textContent = storageBytesToLabeledString(estimate.quota); document.querySelector('#storage-quota').textContent = this._bytesToLabeledString(estimate.quota);
} }
document.querySelector('#storage-use-finite').classList.toggle('storage-hidden', !finite); document.querySelector('#storage-use-finite').classList.toggle('storage-hidden', !finite);
document.querySelector('#storage-use-infinite').classList.toggle('storage-hidden', finite); document.querySelector('#storage-use-infinite').classList.toggle('storage-hidden', finite);
} }
storageUpdateStats.isUpdating = false;
return valid;
}
storageUpdateStats.isUpdating = false;
async function storageShowInfo() {
storageSpinnerShow(true);
const valid = await storageUpdateStats();
document.querySelector('#storage-use').classList.toggle('storage-hidden', !valid); document.querySelector('#storage-use').classList.toggle('storage-hidden', !valid);
document.querySelector('#storage-error').classList.toggle('storage-hidden', valid); document.querySelector('#storage-error').classList.toggle('storage-hidden', valid);
storageSpinnerShow(false); return valid;
} } finally {
this._isUpdating = false;
function storageSpinnerShow(show) { }
const spinner = $('#storage-spinner');
if (show) {
spinner.show();
} else {
spinner.hide();
} }
}
async function storagePersistInitialize() { // Private
async _preparePersistentStorage() {
if (!(navigator.storage && navigator.storage.persist)) { if (!(navigator.storage && navigator.storage.persist)) {
// Not supported // Not supported
return; return;
@ -115,7 +70,7 @@ async function storagePersistInitialize() {
info.classList.remove('storage-hidden'); info.classList.remove('storage-hidden');
button.classList.remove('storage-hidden'); button.classList.remove('storage-hidden');
let persisted = await isStoragePeristent(); let persisted = await this._isStoragePeristent();
checkbox.checked = persisted; checkbox.checked = persisted;
button.addEventListener('click', async () => { button.addEventListener('click', async () => {
@ -132,9 +87,45 @@ async function storagePersistInitialize() {
if (result) { if (result) {
persisted = true; persisted = true;
checkbox.checked = true; checkbox.checked = true;
storageShowInfo(); this.updateStats();
} else { } else {
$('.storage-persist-fail-warning').removeClass('storage-hidden'); document.querySelector('.storage-persist-fail-warning').classList.remove('storage-hidden');
} }
}, false); }, false);
}
async _storageEstimate() {
if (this._storageEstimateFailed && this._mostRecentStorageEstimate === null) {
return null;
}
try {
const value = await navigator.storage.estimate();
this._mostRecentStorageEstimate = value;
return value;
} catch (e) {
this._storageEstimateFailed = true;
}
return null;
}
_bytesToLabeledString(size) {
const base = 1000;
const labels = [' bytes', 'KB', 'MB', 'GB'];
let labelIndex = 0;
while (size >= base) {
size /= base;
++labelIndex;
}
const label = labelIndex === 0 ? `${size}` : size.toFixed(1);
return `${label}${labels[labelIndex]}`;
}
async _isStoragePeristent() {
try {
return await navigator.storage.persisted();
} catch (e) {
// NOP
}
return false;
}
} }

View File

@ -711,7 +711,6 @@
<div id="storage-info"> <div id="storage-info">
<div> <div>
<img src="/mixed/img/spinner.gif" class="pull-right" id="storage-spinner" />
<h3>Storage</h3> <h3>Storage</h3>
</div> </div>