diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/css/settings.css | 2 | ||||
| -rw-r--r-- | ext/bg/js/settings/dictionaries.js | 544 | ||||
| -rw-r--r-- | ext/bg/js/settings/main.js | 25 | ||||
| -rw-r--r-- | ext/bg/js/settings/storage.js | 199 | ||||
| -rw-r--r-- | ext/bg/settings.html | 1 | 
5 files changed, 384 insertions, 387 deletions
| diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css index f55082e7..eb11d77e 100644 --- a/ext/bg/css/settings.css +++ b/ext/bg/css/settings.css @@ -18,7 +18,7 @@  #anki-spinner,  #dict-spinner, #dict-import-progress, -.storage-hidden, #storage-spinner { +.storage-hidden {      display: none;  } diff --git a/ext/bg/js/settings/dictionaries.js b/ext/bg/js/settings/dictionaries.js index 4d307f0f..dd6dd1c1 100644 --- a/ext/bg/js/settings/dictionaries.js +++ b/ext/bg/js/settings/dictionaries.js @@ -22,14 +22,9 @@   * getOptionsFullMutable   * getOptionsMutable   * settingsSaveOptions - * storageEstimate - * storageUpdateStats   * utilBackgroundIsolate   */ -let dictionaryUI = null; - -  class SettingsDictionaryListUI {      constructor(container, template, extraContainer, extraTemplate) {          this.container = container; @@ -308,13 +303,13 @@ class SettingsDictionaryEntryUI {              await api.deleteDictionary(this.dictionaryInfo.title, onProgress);          } catch (e) { -            dictionaryErrorsShow([e]); +            this.dictionaryErrorsShow([e]);          } finally {              prevention.end();              this.isDeleting = false;              progress.hidden = true; -            onDatabaseUpdated(); +            this.onDatabaseUpdated();          }      } @@ -388,340 +383,341 @@ 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() { -    dictionaryUI = new SettingsDictionaryListUI( -        document.querySelector('#dict-groups'), -        document.querySelector('#dict-template'), -        document.querySelector('#dict-groups-extra'), -        document.querySelector('#dict-extra-template') -    ); -    dictionaryUI.save = settingsSaveOptions; - -    document.querySelector('#dict-purge-button').addEventListener('click', onDictionaryPurgeButtonClick, false); -    document.querySelector('#dict-purge-confirm').addEventListener('click', onDictionaryPurge, false); -    document.querySelector('#dict-file-button').addEventListener('click', onDictionaryImportButtonClick, false); -    document.querySelector('#dict-file').addEventListener('change', onDictionaryImport, false); -    document.querySelector('#dict-main').addEventListener('change', onDictionaryMainChanged, false); -    document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', onDatabaseEnablePrefixWildcardSearchesChanged, false); - -    await onDictionaryOptionsChanged(); -    await onDatabaseUpdated(); -} - -async function onDictionaryOptionsChanged() { -    if (dictionaryUI === null) { return; } - -    const optionsContext = getOptionsContext(); -    const options = await getOptionsMutable(optionsContext); +    async prepare() { +        this._dictionaryUI = new SettingsDictionaryListUI( +            document.querySelector('#dict-groups'), +            document.querySelector('#dict-template'), +            document.querySelector('#dict-groups-extra'), +            document.querySelector('#dict-extra-template') +        ); +        this._dictionaryUI.save = settingsSaveOptions; -    dictionaryUI.setOptionsDictionaries(options.dictionaries); +        document.querySelector('#dict-purge-button').addEventListener('click', this._onPurgeButtonClick.bind(this), false); +        document.querySelector('#dict-purge-confirm').addEventListener('click', this._onPurgeConfirmButtonClick.bind(this), false); +        document.querySelector('#dict-file-button').addEventListener('click', this._onImportButtonClick.bind(this), false); +        document.querySelector('#dict-file').addEventListener('change', this._onImportFileChange.bind(this), false); +        document.querySelector('#dict-main').addEventListener('change', this._onDictionaryMainChanged.bind(this), false); +        document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', this._onDatabaseEnablePrefixWildcardSearchesChanged.bind(this), false); -    const optionsFull = await api.optionsGetFull(); -    document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported; +        await this.optionsChanged(); +        await this._onDatabaseUpdated(); +    } -    await updateMainDictionarySelectValue(); -} +    async optionsChanged() { +        if (this._dictionaryUI === null) { return; } -async function onDatabaseUpdated() { -    try { -        const dictionaries = await api.getDictionaryInfo(); -        dictionaryUI.setDictionaries(dictionaries); +        const optionsContext = getOptionsContext(); +        const options = await getOptionsMutable(optionsContext); -        document.querySelector('#dict-warning').hidden = (dictionaries.length > 0); +        this._dictionaryUI.setOptionsDictionaries(options.dictionaries); -        updateMainDictionarySelectOptions(dictionaries); -        await updateMainDictionarySelectValue(); +        const optionsFull = await api.optionsGetFull(); +        document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported; -        const {counts, total} = await api.getDictionaryCounts(dictionaries.map((v) => v.title), true); -        dictionaryUI.setCounts(counts, total); -    } catch (e) { -        dictionaryErrorsShow([e]); +        await this._updateMainDictionarySelectValue();      } -} - -function updateMainDictionarySelectOptions(dictionaries) { -    const select = document.querySelector('#dict-main'); -    select.textContent = ''; // Empty -    let option = document.createElement('option'); -    option.className = 'text-muted'; -    option.value = ''; -    option.textContent = 'Not selected'; -    select.appendChild(option); +    // Private -    for (const {title, sequenced} of toIterable(dictionaries)) { -        if (!sequenced) { continue; } +    _updateMainDictionarySelectOptions(dictionaries) { +        const select = document.querySelector('#dict-main'); +        select.textContent = ''; // Empty -        option = document.createElement('option'); -        option.value = title; -        option.textContent = title; +        let option = document.createElement('option'); +        option.className = 'text-muted'; +        option.value = ''; +        option.textContent = 'Not selected';          select.appendChild(option); -    } -} - -async function updateMainDictionarySelectValue() { -    const optionsContext = getOptionsContext(); -    const options = await api.optionsGet(optionsContext); -    const value = options.general.mainDictionary; +        for (const {title, sequenced} of toIterable(dictionaries)) { +            if (!sequenced) { continue; } -    const select = document.querySelector('#dict-main'); -    let selectValue = null; -    for (const child of select.children) { -        if (child.nodeName.toUpperCase() === 'OPTION' && child.value === value) { -            selectValue = value; -            break; +            option = document.createElement('option'); +            option.value = title; +            option.textContent = title; +            select.appendChild(option);          }      } -    let missingNodeOption = select.querySelector('option[data-not-installed=true]'); -    if (selectValue === null) { -        if (missingNodeOption === null) { -            missingNodeOption = document.createElement('option'); -            missingNodeOption.className = 'text-muted'; -            missingNodeOption.value = value; -            missingNodeOption.textContent = `${value} (Not installed)`; -            missingNodeOption.dataset.notInstalled = 'true'; -            select.appendChild(missingNodeOption); +    async _updateMainDictionarySelectValue() { +        const optionsContext = getOptionsContext(); +        const options = await api.optionsGet(optionsContext); + +        const value = options.general.mainDictionary; + +        const select = document.querySelector('#dict-main'); +        let selectValue = null; +        for (const child of select.children) { +            if (child.nodeName.toUpperCase() === 'OPTION' && child.value === value) { +                selectValue = value; +                break; +            }          } -    } else { -        if (missingNodeOption !== null) { -            missingNodeOption.parentNode.removeChild(missingNodeOption); + +        let missingNodeOption = select.querySelector('option[data-not-installed=true]'); +        if (selectValue === null) { +            if (missingNodeOption === null) { +                missingNodeOption = document.createElement('option'); +                missingNodeOption.className = 'text-muted'; +                missingNodeOption.value = value; +                missingNodeOption.textContent = `${value} (Not installed)`; +                missingNodeOption.dataset.notInstalled = 'true'; +                select.appendChild(missingNodeOption); +            } +        } else { +            if (missingNodeOption !== null) { +                missingNodeOption.parentNode.removeChild(missingNodeOption); +            }          } + +        select.value = value;      } -    select.value = value; -} +    _dictionaryErrorToString(error) { +        if (error.toString) { +            error = error.toString(); +        } else { +            error = `${error}`; +        } -async function onDictionaryMainChanged(e) { -    const select = e.target; -    const value = select.value; +        for (const [match, subst] of this._dictionaryErrorToStringOverrides) { +            if (error.includes(match)) { +                error = subst; +                break; +            } +        } -    const missingNodeOption = select.querySelector('option[data-not-installed=true]'); -    if (missingNodeOption !== null && missingNodeOption.value !== value) { -        missingNodeOption.parentNode.removeChild(missingNodeOption); +        return error;      } -    const optionsContext = getOptionsContext(); -    const options = await getOptionsMutable(optionsContext); -    options.general.mainDictionary = value; -    await settingsSaveOptions(); -} +    _dictionaryErrorsShow(errors) { +        const dialog = document.querySelector('#dict-error'); +        dialog.textContent = ''; +        if (errors !== null && errors.length > 0) { +            const uniqueErrors = new Map(); +            for (let e of errors) { +                yomichan.logError(e); +                e = this._dictionaryErrorToString(e); +                let count = uniqueErrors.get(e); +                if (typeof count === 'undefined') { +                    count = 0; +                } +                uniqueErrors.set(e, count + 1); +            } -function dictionaryErrorToString(error) { -    if (error.toString) { -        error = error.toString(); -    } else { -        error = `${error}`; -    } +            for (const [e, count] of uniqueErrors.entries()) { +                const div = document.createElement('p'); +                if (count > 1) { +                    div.textContent = `${e} `; +                    const em = document.createElement('em'); +                    em.textContent = `(${count})`; +                    div.appendChild(em); +                } else { +                    div.textContent = `${e}`; +                } +                dialog.appendChild(div); +            } -    for (const [match, subst] of dictionaryErrorToString.overrides) { -        if (error.includes(match)) { -            error = subst; -            break; +            dialog.hidden = false; +        } else { +            dialog.hidden = true;          }      } -    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) { -    const dialog = document.querySelector('#dict-error'); -    dialog.textContent = ''; - -    if (errors !== null && errors.length > 0) { -        const uniqueErrors = new Map(); -        for (let e of errors) { -            yomichan.logError(e); -            e = dictionaryErrorToString(e); -            let count = uniqueErrors.get(e); -            if (typeof count === 'undefined') { -                count = 0; -            } -            uniqueErrors.set(e, count + 1); -        } - -        for (const [e, count] of uniqueErrors.entries()) { -            const div = document.createElement('p'); -            if (count > 1) { -                div.textContent = `${e} `; -                const em = document.createElement('em'); -                em.textContent = `(${count})`; -                div.appendChild(em); -            } else { -                div.textContent = `${e}`; -            } -            dialog.appendChild(div); +    _dictionarySpinnerShow(show) { +        const spinner = $('#dict-spinner'); +        if (show) { +            spinner.show(); +        } else { +            spinner.hide();          } +    } -        dialog.hidden = false; -    } else { -        dialog.hidden = true; +    _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); +        } -function dictionarySpinnerShow(show) { -    const spinner = $('#dict-spinner'); -    if (show) { -        spinner.show(); -    } else { -        spinner.hide(); +        const optionsContext = getOptionsContext(); +        const options = await getOptionsMutable(optionsContext); +        options.general.mainDictionary = value; +        await settingsSaveOptions();      } -} -function onDictionaryImportButtonClick() { -    const dictFile = document.querySelector('#dict-file'); -    dictFile.click(); -} +    _onImportButtonClick() { +        const dictFile = document.querySelector('#dict-file'); +        dictFile.click(); +    } -function onDictionaryPurgeButtonClick(e) { -    e.preventDefault(); -    $('#dict-purge-modal').modal('show'); -} +    _onPurgeButtonClick(e) { +        e.preventDefault(); +        $('#dict-purge-modal').modal('show'); +    } -async function onDictionaryPurge(e) { -    e.preventDefault(); +    async _onPurgeConfirmButtonClick(e) { +        e.preventDefault(); -    $('#dict-purge-modal').modal('hide'); +        $('#dict-purge-modal').modal('hide'); -    const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide(); -    const dictProgress = document.querySelector('#dict-purge'); -    dictProgress.hidden = false; +        const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide(); +        const dictProgress = document.querySelector('#dict-purge'); +        dictProgress.hidden = false; -    const prevention = new PageExitPrevention(); +        const prevention = new PageExitPrevention(); -    try { -        prevention.start(); -        dictionaryErrorsShow(null); -        dictionarySpinnerShow(true); +        try { +            prevention.start(); +            this._dictionaryErrorsShow(null); +            this._dictionarySpinnerShow(true); -        await api.purgeDatabase(); -        for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { -            options.dictionaries = utilBackgroundIsolate({}); -            options.general.mainDictionary = ''; -        } -        await settingsSaveOptions(); +            await api.purgeDatabase(); +            for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { +                options.dictionaries = utilBackgroundIsolate({}); +                options.general.mainDictionary = ''; +            } +            await settingsSaveOptions(); -        onDatabaseUpdated(); -    } catch (err) { -        dictionaryErrorsShow([err]); -    } finally { -        prevention.end(); +            this._onDatabaseUpdated(); +        } catch (err) { +            this._dictionaryErrorsShow([err]); +        } finally { +            prevention.end(); -        dictionarySpinnerShow(false); +            this._dictionarySpinnerShow(false); -        dictControls.show(); -        dictProgress.hidden = true; +            dictControls.show(); +            dictProgress.hidden = true; -        if (storageEstimate.mostRecent !== null) { -            storageUpdateStats(); +            this._storageController.updateStats();          }      } -} -async function onDictionaryImport(e) { -    const files = [...e.target.files]; -    e.target.value = null; +    async _onImportFileChange(e) { +        const files = [...e.target.files]; +        e.target.value = null; -    const dictFile = $('#dict-file'); -    const dictControls = $('#dict-importer').hide(); -    const dictProgress = $('#dict-import-progress').show(); -    const dictImportInfo = document.querySelector('#dict-import-info'); +        const dictFile = $('#dict-file'); +        const dictControls = $('#dict-importer').hide(); +        const dictProgress = $('#dict-import-progress').show(); +        const dictImportInfo = document.querySelector('#dict-import-info'); -    const prevention = new PageExitPrevention(); +        const prevention = new PageExitPrevention(); -    try { -        prevention.start(); -        dictionaryErrorsShow(null); -        dictionarySpinnerShow(true); +        try { +            prevention.start(); +            this._dictionaryErrorsShow(null); +            this._dictionarySpinnerShow(true); -        const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`); -        const updateProgress = (total, current) => { -            setProgress(current / total * 100.0); -            if (storageEstimate.mostRecent !== null && !storageUpdateStats.isUpdating) { -                storageUpdateStats(); -            } -        }; +            const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`); +            const updateProgress = (total, current) => { +                setProgress(current / total * 100.0); +                this._storageController.updateStats(); +            }; -        const optionsFull = await api.optionsGetFull(); +            const optionsFull = await api.optionsGetFull(); -        const importDetails = { -            prefixWildcardsSupported: optionsFull.global.database.prefixWildcardsSupported -        }; +            const importDetails = { +                prefixWildcardsSupported: optionsFull.global.database.prefixWildcardsSupported +            }; -        for (let i = 0, ii = files.length; i < ii; ++i) { -            setProgress(0.0); -            if (ii > 1) { -                dictImportInfo.hidden = false; -                dictImportInfo.textContent = `(${i + 1} of ${ii})`; -            } +            for (let i = 0, ii = files.length; i < ii; ++i) { +                setProgress(0.0); +                if (ii > 1) { +                    dictImportInfo.hidden = false; +                    dictImportInfo.textContent = `(${i + 1} of ${ii})`; +                } -            const archiveContent = await dictReadFile(files[i]); -            const {result, errors} = await api.importDictionaryArchive(archiveContent, importDetails, updateProgress); -            for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { -                const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions(); -                dictionaryOptions.enabled = true; -                options.dictionaries[result.title] = dictionaryOptions; -                if (result.sequenced && options.general.mainDictionary === '') { -                    options.general.mainDictionary = result.title; +                const archiveContent = await this._dictReadFile(files[i]); +                const {result, errors} = await api.importDictionaryArchive(archiveContent, importDetails, updateProgress); +                for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { +                    const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions(); +                    dictionaryOptions.enabled = true; +                    options.dictionaries[result.title] = dictionaryOptions; +                    if (result.sequenced && options.general.mainDictionary === '') { +                        options.general.mainDictionary = result.title; +                    }                  } -            } -            await settingsSaveOptions(); +                await settingsSaveOptions(); + +                if (errors.length > 0) { +                    const errors2 = errors.map((error) => jsonToError(error)); +                    errors2.push(`Dictionary may not have been imported properly: ${errors2.length} error${errors2.length === 1 ? '' : 's'} reported.`); +                    this._dictionaryErrorsShow(errors2); +                } -            if (errors.length > 0) { -                const errors2 = errors.map((error) => jsonToError(error)); -                errors2.push(`Dictionary may not have been imported properly: ${errors2.length} error${errors2.length === 1 ? '' : 's'} reported.`); -                dictionaryErrorsShow(errors2); +                this._onDatabaseUpdated();              } +        } catch (err) { +            this._dictionaryErrorsShow([err]); +        } finally { +            prevention.end(); +            this._dictionarySpinnerShow(false); -            onDatabaseUpdated(); +            dictImportInfo.hidden = false; +            dictImportInfo.textContent = ''; +            dictFile.val(''); +            dictControls.show(); +            dictProgress.hide();          } -    } catch (err) { -        dictionaryErrorsShow([err]); -    } finally { -        prevention.end(); -        dictionarySpinnerShow(false); - -        dictImportInfo.hidden = false; -        dictImportInfo.textContent = ''; -        dictFile.val(''); -        dictControls.show(); -        dictProgress.hide();      } -} - -function 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 function onDatabaseEnablePrefixWildcardSearchesChanged(e) { -    const optionsFull = await getOptionsFullMutable(); -    const v = !!e.target.checked; -    if (optionsFull.global.database.prefixWildcardsSupported === v) { return; } -    optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked; -    await settingsSaveOptions(); +    async _onDatabaseEnablePrefixWildcardSearchesChanged(e) { +        const optionsFull = await getOptionsFullMutable(); +        const v = !!e.target.checked; +        if (optionsFull.global.database.prefixWildcardsSupported === v) { return; } +        optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked; +        await settingsSaveOptions(); +    }  } diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index dddaef6c..1d387749 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -19,14 +19,13 @@   * AnkiController   * AnkiTemplatesController   * AudioController + * DictionaryController   * ProfileController   * SettingsBackup   * SettingsController + * StorageController   * api   * appearanceInitialize - * dictSettingsInitialize - * onDictionaryOptionsChanged - * storageInfoInitialize   * utilBackend   * utilBackgroundIsolate   */ @@ -270,7 +269,9 @@ async function onOptionsUpdated({source}) {      if (ankiTemplatesController !== null) {          ankiTemplatesController.updateValue();      } -    onDictionaryOptionsChanged(); +    if (dictionaryController !== null) { +        dictionaryController.optionsChanged(); +    }      if (ankiController !== null) {          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 ankiTemplatesController = null; +let dictionaryController = null;  async function onReady() {      api.forwardLogsToBackend(); @@ -314,22 +322,25 @@ async function onReady() {      const settingsController = new SettingsController();      settingsController.prepare(); +    setupEnvironmentInfo();      showExtensionInformation(); +    const storageController = new StorageController(); +    storageController.prepare(); +      await settingsPopulateModifierKeys();      formSetupEventListeners();      appearanceInitialize();      new AudioController().prepare();      await (new ProfileController()).prepare(); -    await dictSettingsInitialize(); +    dictionaryController = new DictionaryController(storageController); +    dictionaryController.prepare();      ankiController = new AnkiController();      ankiController.prepare();      ankiTemplatesController = new AnkiTemplatesController(ankiController);      ankiTemplatesController.prepare();      new SettingsBackup().prepare(); -    storageInfoInitialize(); -      yomichan.on('optionsUpdated', onOptionsUpdated);  } diff --git a/ext/bg/js/settings/storage.js b/ext/bg/js/settings/storage.js index 73c93fa1..24c6d7ef 100644 --- a/ext/bg/js/settings/storage.js +++ b/ext/bg/js/settings/storage.js @@ -15,126 +15,117 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -/* global - * api - */ - -function storageBytesToLabeledString(size) { -    const base = 1000; -    const labels = [' bytes', 'KB', 'MB', 'GB']; -    let labelIndex = 0; -    while (size >= base) { -        size /= base; -        ++labelIndex; +class StorageController { +    constructor() { +        this._mostRecentStorageEstimate = null; +        this._storageEstimateFailed = false; +        this._isUpdating = false;      } -    const label = labelIndex === 0 ? `${size}` : size.toFixed(1); -    return `${label}${labels[labelIndex]}`; -} -async function storageEstimate() { -    try { -        return (storageEstimate.mostRecent = await navigator.storage.estimate()); -    } catch (e) { -        // NOP +    prepare() { +        this._preparePersistentStorage(); +        this.updateStats(); +        document.querySelector('#storage-refresh').addEventListener('click', this.updateStats.bind(this), false);      } -    return null; -} -storageEstimate.mostRecent = null; - -async function isStoragePeristent() { -    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); - -    if (valid) { -        // Firefox reports usage as 0 when persistent storage is enabled. -        const finite = (estimate.usage > 0 || !(await isStoragePeristent())); -        if (finite) { -            document.querySelector('#storage-usage').textContent = storageBytesToLabeledString(estimate.usage); -            document.querySelector('#storage-quota').textContent = storageBytesToLabeledString(estimate.quota); +    async updateStats() { +        try { +            this._isUpdating = true; + +            const estimate = await this._storageEstimate(); +            const valid = (estimate !== null); + +            if (valid) { +                // Firefox reports usage as 0 when persistent storage is enabled. +                const finite = (estimate.usage > 0 || !(await this._isStoragePeristent())); +                if (finite) { +                    document.querySelector('#storage-usage').textContent = this._bytesToLabeledString(estimate.usage); +                    document.querySelector('#storage-quota').textContent = this._bytesToLabeledString(estimate.quota); +                } +                document.querySelector('#storage-use-finite').classList.toggle('storage-hidden', !finite); +                document.querySelector('#storage-use-infinite').classList.toggle('storage-hidden', finite); +            } + +            document.querySelector('#storage-use').classList.toggle('storage-hidden', !valid); +            document.querySelector('#storage-error').classList.toggle('storage-hidden', valid); + +            return valid; +        } finally { +            this._isUpdating = false;          } -        document.querySelector('#storage-use-finite').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-error').classList.toggle('storage-hidden', valid); +    // Private -    storageSpinnerShow(false); -} +    async _preparePersistentStorage() { +        if (!(navigator.storage && navigator.storage.persist)) { +            // Not supported +            return; +        } -function storageSpinnerShow(show) { -    const spinner = $('#storage-spinner'); -    if (show) { -        spinner.show(); -    } else { -        spinner.hide(); +        const info = document.querySelector('#storage-persist-info'); +        const button = document.querySelector('#storage-persist-button'); +        const checkbox = document.querySelector('#storage-persist-button-checkbox'); + +        info.classList.remove('storage-hidden'); +        button.classList.remove('storage-hidden'); + +        let persisted = await this._isStoragePeristent(); +        checkbox.checked = persisted; + +        button.addEventListener('click', async () => { +            if (persisted) { +                return; +            } +            let result = false; +            try { +                result = await navigator.storage.persist(); +            } catch (e) { +                // NOP +            } + +            if (result) { +                persisted = true; +                checkbox.checked = true; +                this.updateStats(); +            } else { +                document.querySelector('.storage-persist-fail-warning').classList.remove('storage-hidden'); +            } +        }, false);      } -} -async function storagePersistInitialize() { -    if (!(navigator.storage && navigator.storage.persist)) { -        // Not supported -        return; +    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;      } -    const info = document.querySelector('#storage-persist-info'); -    const button = document.querySelector('#storage-persist-button'); -    const checkbox = document.querySelector('#storage-persist-button-checkbox'); - -    info.classList.remove('storage-hidden'); -    button.classList.remove('storage-hidden'); - -    let persisted = await isStoragePeristent(); -    checkbox.checked = persisted; - -    button.addEventListener('click', async () => { -        if (persisted) { -            return; +    _bytesToLabeledString(size) { +        const base = 1000; +        const labels = [' bytes', 'KB', 'MB', 'GB']; +        let labelIndex = 0; +        while (size >= base) { +            size /= base; +            ++labelIndex;          } -        let result = false; +        const label = labelIndex === 0 ? `${size}` : size.toFixed(1); +        return `${label}${labels[labelIndex]}`; +    } + +    async _isStoragePeristent() {          try { -            result = await navigator.storage.persist(); +            return await navigator.storage.persisted();          } catch (e) {              // NOP          } - -        if (result) { -            persisted = true; -            checkbox.checked = true; -            storageShowInfo(); -        } else { -            $('.storage-persist-fail-warning').removeClass('storage-hidden'); -        } -    }, false); +        return false; +    }  } diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 5c7fde41..4856b0b4 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -711,7 +711,6 @@              <div id="storage-info">                  <div> -                    <img src="/mixed/img/spinner.gif" class="pull-right" id="storage-spinner" />                      <h3>Storage</h3>                  </div> |