Files
Florian Zumpe 5c4d20bd1d
Sonarqube Scanner / Prepare environment (push) Successful in 19s
Sonarqube Scanner / Build and test (push) Successful in 43s
Sonarqube Scanner / SonarQube analysis (push) Failing after 2m28s
Sonarqube Scanner / Deploy to Server (push) Successful in 16s
fix sonar findings and translation structure
2026-10-01 16:31:46 +02:00

94 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 / 1000000).toLocaleString(ns.I18n.language, { maximumFractionDigits: 1 })} MB`;
}
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;
}
}
function siteDescription(usage, quota, known) {
if (!known) return ns.I18n.t("storage.error.estimate");
const warning = usage / quota >= .9 ? ns.I18n.t("storage.site-warning") : '';
return ns.I18n.t("storage.site-usage", { value1: Math.round(usage / quota * 100), value2: format(Math.max(0, quota - usage)), value3: warning });
}
function localDescription(local) {
if (local == null) return ns.I18n.t("storage.error.local-unavailable");
const warning = local >= LOCAL_REFERENCE * .9 ? ns.I18n.t("storage.local-warning") : '';
return ns.I18n.t("storage.local-usage", { value1: format(local), value2: warning });
}
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, siteDescription(usage, quota, known));
let local = null;
try { local = localBytes(); } catch { /* Storage can be blocked by browser settings. */ }
show('local', local, local == null ? null : LOCAL_REFERENCE, localDescription(local));
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 = ns.I18n.t("storage.error.quota");
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));