/* * Copyright (C) 2019-2020 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 StorageController { constructor() { this._mostRecentStorageEstimate = null; this._storageEstimateFailed = false; this._isUpdating = false; } prepare() { this._preparePersistentStorage(); this.updateStats(); document.querySelector('#storage-refresh').addEventListener('click', this.updateStats.bind(this), false); } 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; } } // Private async _preparePersistentStorage() { if (!(navigator.storage && navigator.storage.persist)) { // Not supported return; } 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 _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; } }