104 lines
4.4 KiB
JavaScript
104 lines
4.4 KiB
JavaScript
(function (root, factory) {
|
|
if (typeof module === 'object' && module.exports) module.exports = factory;
|
|
else {
|
|
root.createDeviceStorage = factory;
|
|
root.createDeviceStorage.mount = document => factory({
|
|
summary:document.querySelector('#device-storage-summary'),
|
|
detail:document.querySelector('#device-storage-detail'),
|
|
clearCachesButton:document.querySelector('#clear-device-caches'),
|
|
clearAllButton:document.querySelector('#clear-private-device-data'),
|
|
localStorage:root.localStorage,
|
|
sessionStorage:root.sessionStorage,
|
|
caches:root.caches,
|
|
storageManager:root.navigator?.storage,
|
|
privateDatabases:root.stackchainPrivateDatabases,
|
|
inspectPrivateDatabases:root.inspectStackchainPrivateDatabases,
|
|
clearPrivateDeviceData:root.stackchainPrivateDeviceData,
|
|
});
|
|
}
|
|
})(typeof self !== 'undefined' ? self : this, function createDeviceStorage(options) {
|
|
let privateItemCount = 0;
|
|
let privateRecordCount = 0;
|
|
let inventoryUnavailable = false;
|
|
let fullClearArmed = false;
|
|
const ownedCaches = async () => (await options.caches?.keys?.() || [])
|
|
.filter(name => name.startsWith('stackchain-dashboard-'));
|
|
|
|
function ownedStorageCount(storage) {
|
|
if (!storage) return 0;
|
|
let count = 0;
|
|
for (let index = 0; index < storage.length; index += 1) {
|
|
if (storage.key(index)?.startsWith('stackchain.')) count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
function megabytes(value) {
|
|
return Math.round((Number(value) || 0) / (1024 * 1024));
|
|
}
|
|
|
|
async function refresh() {
|
|
let estimate = null;
|
|
try { estimate = await options.storageManager?.estimate?.(); }
|
|
catch (_error) { /* Storage estimates are optional. */ }
|
|
options.summary.textContent = estimate?.quota
|
|
? `${megabytes(estimate.usage)} MB of ${megabytes(estimate.quota)} MB browser storage used.`
|
|
: 'Browser storage usage is unavailable.';
|
|
const itemCount = ownedStorageCount(options.localStorage)
|
|
+ (options.sessionStorage === options.localStorage ? 0 : ownedStorageCount(options.sessionStorage));
|
|
privateItemCount = itemCount;
|
|
const inventory = await options.inspectPrivateDatabases?.(options.privateDatabases)
|
|
|| { recordCount: 0, unavailable: true };
|
|
privateRecordCount = inventory.recordCount;
|
|
inventoryUnavailable = inventory.unavailable;
|
|
const cacheCount = (await ownedCaches()).length;
|
|
options.detail.textContent = `${itemCount} private browser item${itemCount === 1 ? '' : 's'} · `
|
|
+ `${inventoryUnavailable ? 'private work status unknown' : `${privateRecordCount} private work record${privateRecordCount === 1 ? '' : 's'}`} · `
|
|
+ `${cacheCount} cached app cop${cacheCount === 1 ? 'y' : 'ies'}`;
|
|
return { itemCount, privateRecordCount, inventoryUnavailable, cacheCount };
|
|
}
|
|
|
|
async function clearCaches() {
|
|
options.clearCachesButton.disabled = true;
|
|
try {
|
|
await Promise.all((await ownedCaches()).map(name => options.caches.delete(name)));
|
|
await refresh();
|
|
options.summary.textContent = 'Cached app copies cleared. Private work was kept.';
|
|
} catch (error) {
|
|
options.summary.textContent = `Cached copies could not be cleared: ${error.message}`;
|
|
} finally {
|
|
options.clearCachesButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function clearAll() {
|
|
if ((privateItemCount > 0 || privateRecordCount > 0 || inventoryUnavailable) && !fullClearArmed) {
|
|
fullClearArmed = true;
|
|
options.clearAllButton.textContent = 'Confirm: clear private work';
|
|
options.summary.textContent = 'Private drafts or queued work may not be synced. Press confirm to clear them from this device.';
|
|
return;
|
|
}
|
|
options.clearAllButton.disabled = true;
|
|
try {
|
|
await options.clearPrivateDeviceData();
|
|
privateItemCount = 0;
|
|
fullClearArmed = false;
|
|
options.clearAllButton.textContent = 'Clear all private data';
|
|
options.detail.textContent = '0 private browser items · 0 private work records · 0 cached app copies';
|
|
options.summary.textContent = 'All Stackchain private data was cleared from this device.';
|
|
} catch (error) {
|
|
options.summary.textContent = `Private data was not fully cleared: ${error.message}`;
|
|
} finally {
|
|
options.clearAllButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function start() {
|
|
options.clearCachesButton.addEventListener('click', clearCaches);
|
|
options.clearAllButton.addEventListener('click', clearAll);
|
|
return refresh();
|
|
}
|
|
|
|
return { refresh, start };
|
|
});
|