Files
Wegwichtel/public/js/storage-usage.js
T
Florian Zumpe 8f4664282b
Sonarqube Scanner / Prepare environment (push) Successful in 14s
Sonarqube Scanner / Build and test (push) Successful in 22s
Sonarqube Scanner / SonarQube analysis (push) Failing after 1m14s
Sonarqube Scanner / Deploy to Server (push) Successful in 11s
add offline support
2026-09-30 23:27:42 +02:00

86 lines
3.8 KiB
JavaScript

globalThis.Wegwichtel ??= {};
(function (ns) {
'use strict';
const LOCAL_REFERENCE = 5 * 1024 * 1024;
let initialized = false;
let lastError = '';
let refreshTask = null;
function format(bytes) {
return `${(bytes / 1024 / 1024).toLocaleString('de-DE', { maximumFractionDigits: 1 })} MiB`;
}
function localBytes() {
const storage = globalThis.localStorage;
if (!storage) return null;
let bytes = 0;
for (let index = 0; index < storage.length; index += 1) {
const key = storage.key(index);
if (key != null) bytes += 2 * (key.length + (storage.getItem(key) || '').length);
}
return bytes;
}
function show(selector, usage, quota, text) {
for (const panel of document.querySelectorAll('[data-storage-usage]')) {
const meter = panel.querySelector(`[data-storage-${selector}-meter]`);
const label = panel.querySelector(`[data-storage-${selector}-text]`);
if (meter) {
meter.hidden = !quota;
if (quota) { meter.max = quota; meter.value = Math.min(usage, quota); }
}
if (label) label.textContent = text;
}
}
async function refresh() {
if (refreshTask) return refreshTask;
refreshTask = (async () => {
let estimate = null;
try { estimate = await navigator.storage?.estimate?.(); } catch { /* Display unknown rather than a guessed quota. */ }
const usage = estimate?.usage;
const quota = estimate?.quota;
const known = Number.isFinite(usage) && Number.isFinite(quota) && quota > 0;
show('site', usage, known ? quota : null, known
? `Website-Speicher: ${format(usage)} von ${format(quota)} belegt (${Math.round(usage / quota * 100)} %). Etwa ${format(Math.max(0, quota - usage))} verfügbar.${usage / quota >= .9 ? ' Speicher fast voll.' : ''}`
: 'Der Browser meldet keine Speicherkapazität.');
let local = null;
try { local = localBytes(); } catch { /* Storage can be blocked by browser settings. */ }
show('local', local, local == null ? null : LOCAL_REFERENCE, local == null
? 'Local Storage ist nicht verfügbar.'
: `Local Storage: ungefähr ${format(local)} belegt. Balken bezogen auf einen Richtwert von 5 MiB; die tatsächliche Grenze hängt vom Browser ab.${local >= LOCAL_REFERENCE * .9 ? ' Richtwert fast erreicht: Große Bilder können das Speichern verhindern.' : ''}`);
for (const panel of document.querySelectorAll('[data-storage-usage]')) {
const warning = panel.querySelector('[data-storage-warning]');
if (warning) { warning.hidden = !lastError; warning.textContent = lastError; }
}
})().finally(() => { refreshTask = null; });
return refreshTask;
}
function reportError(error) {
let current = error;
while (current) {
if (current.name === 'QuotaExceededError') {
lastError = 'Speicher voll: Entferne nicht mehr benötigte Offline-Strecken oder lokale Entwürfe. Eine Browser-Quote oder die separate Local-Storage-Grenze kann erreicht sein.';
break;
}
current = current.cause;
}
void refresh();
}
function initialize() {
if (initialized) return;
initialized = true;
document.addEventListener('wegwichtel:local-routes-changed', () => { lastError = ''; void refresh(); });
document.addEventListener('wegwichtel:storage-error', event => reportError(event.detail));
document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') void refresh(); });
globalThis.addEventListener?.('storage', () => { void refresh(); });
globalThis.addEventListener?.('focus', () => { void refresh(); });
for (const button of document.querySelectorAll('[data-storage-refresh]')) button.addEventListener('click', () => { void refresh(); });
void refresh();
}
ns.StorageUsage = Object.freeze({ initialize, refresh, reportError });
}(globalThis.Wegwichtel));