fix(sys-bar+fix-sw): accurate button labels and full storage reset
- sys bar: fix wrong title on Full Reset button (was "Clear IDB only", now accurately describes SW + Cache + IDB + localStorage + sessionStorage) - sys bar: rename "Reload Page" → "Clear IDB & Reload" and wire it to handle_clear_idb_only instead of a bare window.location.reload() - testing/fix-sw: expand from SW+Cache only to full storage nuke — adds IDB (via indexedDB.databases() with known-names fallback), localStorage, sessionStorage; auto-reloads to / after 3s; shows per-step status log with success/warn/error colour coding Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -549,24 +549,24 @@ function apply_theme(value: string) {
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<!-- Reload -->
|
||||
<!-- Clear IDB + Reload (preserves localStorage/sessionStorage + SW) -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm preset-tonal-warning w-full justify-end"
|
||||
onclick={() => window.location.reload()}
|
||||
title="Hard reload the page">
|
||||
onclick={handle_clear_idb_only}
|
||||
title="Clear IDB & Reload: delete all IndexedDB databases, then reload. Preserves localStorage, sessionStorage, and service worker cache.">
|
||||
<RefreshCw size="1em" class="opacity-60" />
|
||||
Reload Page
|
||||
Clear IDB & Reload
|
||||
</button>
|
||||
|
||||
<!-- Clear localStorage, sessionStorage, and all IndexedDB databases — full reset -->
|
||||
<!-- Full reset: SW + Cache Storage + IDB + localStorage + sessionStorage -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm preset-tonal-error w-full justify-end"
|
||||
onclick={handle_clear_storage_and_idb}
|
||||
title="Clear all IndexedDB tables only — then reload">
|
||||
title="Full Reset: unregister service workers, clear SW Cache Storage, delete all IndexedDB databases, clear localStorage and sessionStorage, then reload.">
|
||||
<Eraser size="1em" class="opacity-60" />
|
||||
Clear All Storage + IDB
|
||||
Full Reset
|
||||
</button>
|
||||
|
||||
<!-- URL param builder -->
|
||||
|
||||
@@ -2,75 +2,184 @@
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let status: string[] = $state([]);
|
||||
let done = $state(false);
|
||||
let has_error = $state(false);
|
||||
|
||||
function log(msg: string) {
|
||||
status = [...status, `${new Date().toLocaleTimeString()} - ${msg}`];
|
||||
console.log(`[FIX-SW] ${msg}`);
|
||||
}
|
||||
|
||||
async function nukeServiceWorkers() {
|
||||
log('Starting Service Worker cleanup...');
|
||||
|
||||
if (!('serviceWorker' in navigator)) {
|
||||
log('Service Workers not supported in this browser.');
|
||||
return;
|
||||
function log_ok(msg: string) {
|
||||
log(`✓ ${msg}`);
|
||||
}
|
||||
|
||||
function log_warn(msg: string) {
|
||||
log(`⚠ ${msg}`);
|
||||
}
|
||||
|
||||
async function nuke_everything() {
|
||||
log('Starting full storage reset...');
|
||||
|
||||
// 1. Unregister all service workers
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
log(`Found ${registrations.length} registrations.`);
|
||||
|
||||
for (const registration of registrations) {
|
||||
const scope = registration.scope;
|
||||
log(`Unregistering SW at scope: ${scope}`);
|
||||
const success = await registration.unregister();
|
||||
log(`Unregister success: ${success}`);
|
||||
}
|
||||
|
||||
if (registrations.length === 0) {
|
||||
log('No active Service Workers found.');
|
||||
log_warn('No active service worker registrations found.');
|
||||
} else {
|
||||
log(`Found ${registrations.length} service worker registration(s).`);
|
||||
for (const reg of registrations) {
|
||||
const ok = await reg.unregister();
|
||||
log_ok(`Unregistered SW at scope: ${reg.scope} (success: ${ok})`);
|
||||
}
|
||||
|
||||
log('Clearing Cache Storage...');
|
||||
const cacheKeys = await caches.keys();
|
||||
for (const key of cacheKeys) {
|
||||
log(`Deleting cache: ${key}`);
|
||||
await caches.delete(key);
|
||||
}
|
||||
log('Cache storage cleared.');
|
||||
|
||||
log('DONE. Please reload the application now.');
|
||||
} catch (err: any) {
|
||||
log(`ERROR: ${err.message}`);
|
||||
console.error(err);
|
||||
has_error = true;
|
||||
log(`ERROR unregistering service workers: ${err.message}`);
|
||||
}
|
||||
} else {
|
||||
log_warn('Service Workers not supported in this browser.');
|
||||
}
|
||||
|
||||
// 2. Clear all Cache Storage caches
|
||||
try {
|
||||
const cache_keys = await caches.keys();
|
||||
if (cache_keys.length === 0) {
|
||||
log_warn('No Cache Storage entries found.');
|
||||
} else {
|
||||
for (const key of cache_keys) {
|
||||
await caches.delete(key);
|
||||
log_ok(`Cleared cache: ${key}`);
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
has_error = true;
|
||||
log(`ERROR clearing Cache Storage: ${err.message}`);
|
||||
}
|
||||
|
||||
// 3. Clear all IndexedDB databases
|
||||
try {
|
||||
if ('databases' in indexedDB) {
|
||||
const db_list = await indexedDB.databases();
|
||||
if (db_list.length === 0) {
|
||||
log_warn('No IndexedDB databases found.');
|
||||
} else {
|
||||
for (const db_info of db_list) {
|
||||
if (!db_info.name) continue;
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const req = indexedDB.deleteDatabase(db_info.name!);
|
||||
req.onsuccess = () => {
|
||||
log_ok(`Deleted IDB database: ${db_info.name}`);
|
||||
resolve();
|
||||
};
|
||||
req.onerror = () => {
|
||||
log(`ERROR deleting IDB database: ${db_info.name}`);
|
||||
has_error = true;
|
||||
resolve(); // continue anyway
|
||||
};
|
||||
req.onblocked = () => {
|
||||
log_warn(`IDB delete blocked (open connections): ${db_info.name} — will proceed`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback: delete known Aether databases by name
|
||||
log_warn('indexedDB.databases() not available — deleting known Aether databases by name.');
|
||||
const known_dbs = [
|
||||
'ae_core_db', 'ae_events_db', 'ae_journals_db',
|
||||
'ae_archives_db', 'ae_posts_db', 'ae_idaa_db',
|
||||
'ae_sponsorships_db', 'ae_reports_db',
|
||||
];
|
||||
for (const name of known_dbs) {
|
||||
await new Promise<void>((resolve) => {
|
||||
const req = indexedDB.deleteDatabase(name);
|
||||
req.onsuccess = () => { log_ok(`Deleted IDB database: ${name}`); resolve(); };
|
||||
req.onerror = () => { resolve(); }; // not present = silent
|
||||
req.onblocked = () => { log_warn(`IDB delete blocked: ${name}`); resolve(); };
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
has_error = true;
|
||||
log(`ERROR clearing IndexedDB: ${err.message}`);
|
||||
}
|
||||
|
||||
// 4. Clear localStorage
|
||||
try {
|
||||
const local_count = localStorage.length;
|
||||
localStorage.clear();
|
||||
log_ok(`Cleared localStorage (${local_count} item(s)).`);
|
||||
} catch (err: any) {
|
||||
has_error = true;
|
||||
log(`ERROR clearing localStorage: ${err.message}`);
|
||||
}
|
||||
|
||||
// 5. Clear sessionStorage
|
||||
try {
|
||||
const session_count = sessionStorage.length;
|
||||
sessionStorage.clear();
|
||||
log_ok(`Cleared sessionStorage (${session_count} item(s)).`);
|
||||
} catch (err: any) {
|
||||
has_error = true;
|
||||
log(`ERROR clearing sessionStorage: ${err.message}`);
|
||||
}
|
||||
|
||||
log('─── Reset complete. Reloading in 3 seconds... ───');
|
||||
done = true;
|
||||
|
||||
// Auto-reload after a short pause so the user can read the log
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
nukeServiceWorkers();
|
||||
nuke_everything();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-2xl p-8 font-mono">
|
||||
<h1 class="mb-4 text-2xl font-bold text-red-600">
|
||||
Service Worker Reset Tool
|
||||
</h1>
|
||||
<p class="mb-4">
|
||||
Attempting to unregister all service workers and clear caches to fix the
|
||||
TypeError loop.
|
||||
<h1 class="mb-2 text-2xl font-bold text-red-600">Full Storage Reset</h1>
|
||||
<p class="mb-1 text-sm text-gray-600">
|
||||
Clears <strong>everything</strong> for this domain: service workers, SW cache, IndexedDB,
|
||||
localStorage, sessionStorage — then reloads.
|
||||
</p>
|
||||
<p class="mb-4 text-sm text-gray-500">
|
||||
Use this if you are completely stuck on an old version of the app and nothing else has
|
||||
worked.
|
||||
</p>
|
||||
|
||||
<div class="min-h-[200px] rounded border border-gray-300 bg-gray-100 p-4">
|
||||
<div
|
||||
class="min-h-60 rounded border border-gray-300 bg-gray-950 p-4 text-green-400 text-sm">
|
||||
{#each status as line, i (i)}
|
||||
<div class="border-b border-gray-200 py-1 last:border-0">
|
||||
<div
|
||||
class="py-0.5 {line.includes('ERROR')
|
||||
? 'text-red-400'
|
||||
: line.includes('⚠')
|
||||
? 'text-yellow-400'
|
||||
: ''}">
|
||||
{line}
|
||||
</div>
|
||||
{/each}
|
||||
{#if !done}
|
||||
<div class="mt-2 animate-pulse text-gray-500">Working...</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if done}
|
||||
<div class="mt-4 flex items-center gap-4">
|
||||
<p class="text-sm {has_error ? 'text-yellow-600' : 'text-green-700'} font-semibold">
|
||||
{has_error
|
||||
? 'Reset finished with some errors. Reloading...'
|
||||
: 'Reset complete! Reloading to home page...'}
|
||||
</p>
|
||||
<button
|
||||
class="mt-4 rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
|
||||
onclick={() => window.location.reload()}>
|
||||
Reload Page
|
||||
class="ml-auto rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
|
||||
onclick={() => (window.location.href = '/')}>
|
||||
Reload Now
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user