refactor(idaa): migrate clear-caches page to core_func.clear_all_storage
Fixes a bug where indexedDB.deleteDatabase() was not awaited, meaning the page could report success while databases were still being deleted. Also adds the known-names IDB fallback for older Safari (pre-2021), which previously caused the page to silently skip all IDB clearing on those browsers. Removes ~70 lines of duplicate inline logic in favour of core_func.clear_all_storage() with an on_step callback that maps BrowserResetLogFn messages to the existing step-by-step progress UI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { TriangleAlert, CircleCheck, Loader } from '@lucide/svelte';
|
||||
import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||
import type { BrowserResetLogFn } from '$lib/ae_core/ae_core_functions';
|
||||
|
||||
type StepStatus = 'pending' | 'running' | 'done' | 'error';
|
||||
|
||||
@@ -12,101 +14,105 @@ interface ClearStep {
|
||||
|
||||
let steps: ClearStep[] = $state([
|
||||
{ label: 'Service workers', status: 'pending', detail: '' },
|
||||
{ label: 'Service worker caches', status: 'pending', detail: '' },
|
||||
{ label: 'IndexedDB databases', status: 'pending', detail: '' },
|
||||
{ label: 'Local storage', status: 'pending', detail: '' },
|
||||
{ label: 'Session storage', status: 'pending', detail: '' }
|
||||
{ label: 'App file cache', status: 'pending', detail: '' },
|
||||
{ label: 'Saved app data', status: 'pending', detail: '' },
|
||||
{ label: 'Local settings', status: 'pending', detail: '' },
|
||||
{ label: 'Session data', status: 'pending', detail: '' }
|
||||
]);
|
||||
|
||||
let overall_done = $state(false);
|
||||
let had_error = $state(false);
|
||||
|
||||
// Tracks item counts per step — used to build "N databases cleared" summary labels.
|
||||
const ok_counts: number[] = [0, 0, 0, 0, 0];
|
||||
// Tracks which step last received a message so the first message for a new step
|
||||
// transitions it to 'running' before reporting the result.
|
||||
let current_step_idx = -1;
|
||||
|
||||
// Maps a core__browser_reset log message to one of the 5 step indices.
|
||||
function step_index_for(msg: string): number {
|
||||
const m = msg.toLowerCase();
|
||||
if (m.includes('service worker') || (m.includes(' sw') && !m.includes('cache'))) return 0;
|
||||
if (m.includes('cache storage') || m.includes('cache:')) return 1;
|
||||
if (m.includes('idb') || m.includes('indexeddb') || m.includes('database')) return 2;
|
||||
if (m.includes('localstorage')) return 3;
|
||||
if (m.includes('sessionstorage')) return 4;
|
||||
return -1; // general info (e.g. "Starting full storage reset...") — not tied to a step
|
||||
}
|
||||
|
||||
function make_ok_detail(idx: number, msg: string): string {
|
||||
const n = ok_counts[idx];
|
||||
if (idx === 0) return `${n} unregistered`;
|
||||
if (idx === 1) return `${n} cache${n !== 1 ? 's' : ''} cleared`;
|
||||
if (idx === 2) return `${n} database${n !== 1 ? 's' : ''} cleared`;
|
||||
// localStorage / sessionStorage report "Cleared X (N item(s))." — extract the count.
|
||||
const count_match = msg.match(/\((\d+)\s+item/);
|
||||
return count_match ? `${count_match[1]} items cleared` : 'Cleared';
|
||||
}
|
||||
|
||||
const on_step: BrowserResetLogFn = (msg, level = 'info') => {
|
||||
const idx = step_index_for(msg);
|
||||
if (idx < 0) return;
|
||||
|
||||
// First message for this step: advance to it and mark it running.
|
||||
if (idx > current_step_idx) {
|
||||
current_step_idx = idx;
|
||||
steps[idx].status = 'running';
|
||||
}
|
||||
|
||||
if (level === 'error') {
|
||||
steps[idx].status = 'error';
|
||||
steps[idx].detail = msg.replace(/^ERROR\s+/i, '');
|
||||
had_error = true;
|
||||
} else if (level === 'ok') {
|
||||
ok_counts[idx]++;
|
||||
steps[idx].status = 'done';
|
||||
steps[idx].detail = make_ok_detail(idx, msg);
|
||||
} else if (level === 'warn') {
|
||||
// "Nothing found" and "not supported" warnings close the step.
|
||||
// Fallback (indexedDB.databases() unavailable) and "blocked" warnings do NOT
|
||||
// close the step — further 'ok' or 'error' messages will follow.
|
||||
const lower = msg.toLowerCase();
|
||||
const is_nothing_found =
|
||||
lower.includes('no active') ||
|
||||
lower.includes('not supported') ||
|
||||
lower.includes('no cache storage') ||
|
||||
lower.includes('no indexeddb') ||
|
||||
lower.includes('no idb');
|
||||
if (is_nothing_found && steps[idx].status === 'running') {
|
||||
steps[idx].status = 'done';
|
||||
steps[idx].detail = lower.includes('not supported') ? 'Not supported' : 'Nothing to clear';
|
||||
}
|
||||
}
|
||||
// 'info' messages only trigger the 'running' transition above; no further action.
|
||||
};
|
||||
|
||||
async function clear_all_caches() {
|
||||
// Service workers
|
||||
steps[0].status = 'running';
|
||||
try {
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (const reg of registrations) await reg.unregister();
|
||||
steps[0].detail = `${registrations.length} unregistered`;
|
||||
} else {
|
||||
steps[0].detail = 'Not supported';
|
||||
}
|
||||
steps[0].status = 'done';
|
||||
} catch (e) {
|
||||
steps[0].status = 'error';
|
||||
steps[0].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
// core_func.clear_all_storage handles:
|
||||
// SW unregister → Cache Storage → IDB (with known-names fallback) → localStorage → sessionStorage
|
||||
// All IDB deletes are properly awaited (the original inline implementation did not await them).
|
||||
await core_func.clear_all_storage(on_step);
|
||||
|
||||
// Cache Storage (SW asset caches)
|
||||
steps[1].status = 'running';
|
||||
try {
|
||||
if ('caches' in window) {
|
||||
const cache_keys = await caches.keys();
|
||||
for (const key of cache_keys) await caches.delete(key);
|
||||
steps[1].detail = `${cache_keys.length} cache${cache_keys.length !== 1 ? 's' : ''} cleared`;
|
||||
} else {
|
||||
steps[1].detail = 'Not supported';
|
||||
// Any steps that received no matching messages (edge case) are marked done.
|
||||
for (const step of steps) {
|
||||
if (step.status === 'pending' || step.status === 'running') {
|
||||
step.status = 'done';
|
||||
if (!step.detail) step.detail = 'Done';
|
||||
}
|
||||
steps[1].status = 'done';
|
||||
} catch (e) {
|
||||
steps[1].status = 'error';
|
||||
steps[1].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// IDB — enumerate and delete every database on this origin
|
||||
steps[2].status = 'running';
|
||||
try {
|
||||
const db_list = await indexedDB.databases();
|
||||
for (const db of db_list) {
|
||||
if (db.name) indexedDB.deleteDatabase(db.name);
|
||||
}
|
||||
steps[2].status = 'done';
|
||||
steps[2].detail = `${db_list.length} database${db_list.length !== 1 ? 's' : ''} cleared`;
|
||||
} catch (e) {
|
||||
steps[2].status = 'error';
|
||||
steps[2].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// localStorage
|
||||
steps[3].status = 'running';
|
||||
try {
|
||||
localStorage.clear();
|
||||
steps[3].status = 'done';
|
||||
steps[3].detail = 'Cleared';
|
||||
} catch (e) {
|
||||
steps[3].status = 'error';
|
||||
steps[3].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// sessionStorage
|
||||
steps[4].status = 'running';
|
||||
try {
|
||||
sessionStorage.clear();
|
||||
steps[4].status = 'done';
|
||||
steps[4].detail = 'Cleared';
|
||||
} catch (e) {
|
||||
steps[4].status = 'error';
|
||||
steps[4].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
overall_done = true;
|
||||
|
||||
// Notify parent window (Novi page) that the clear is complete.
|
||||
// The Novi page can optionally listen for this to show a confirmation to the member.
|
||||
// Notify the parent Novi page that the clear is complete.
|
||||
// Novi can optionally listen to show a confirmation or trigger a page reload.
|
||||
try {
|
||||
window.parent.postMessage({ ae_cache_cleared: true, had_error }, '*');
|
||||
} catch (_) {
|
||||
// not in an iframe — ignore
|
||||
// Not in an iframe — ignore.
|
||||
}
|
||||
}
|
||||
|
||||
// Run once on mount. Reads no reactive state so $effect never re-fires.
|
||||
// Auto-run once on mount. No reactive state is read, so $effect never re-fires.
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
clear_all_caches();
|
||||
@@ -127,13 +133,13 @@ $effect(() => {
|
||||
Cleared with some errors
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Most caches were cleared. Close this tab and reload the IDAA pages to
|
||||
Most data was cleared. Close this tab and reload the IDAA pages to
|
||||
get the latest version.
|
||||
</p>
|
||||
{:else}
|
||||
<CircleCheck size={40} class="text-success-500" />
|
||||
<div class="text-xl font-semibold">
|
||||
All caches cleared
|
||||
All data cleared
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Close this tab and reload the IDAA pages to get the latest version.
|
||||
@@ -168,13 +174,5 @@ $effect(() => {
|
||||
Meeting List, Archives, or Bulletin Board,
|
||||
please try once more. We apologize for the inconvenience, and thank you for your patience while we work to improve the IDAA experience!
|
||||
</p>
|
||||
|
||||
<!-- <p class="text-sm text-gray-700 dark:text-gray-300">
|
||||
If you were having trouble with the IDAA
|
||||
<a href="https://www.idaa.org/idaa-meetings" style="color:#2563eb;text-decoration:underline;" class="text-blue-600 underline">Meeting List</a>,
|
||||
<a href="https://www.idaa.org/idaa-archives" style="color:#2563eb;text-decoration:underline;" class="text-blue-600 underline">Archives</a>, or
|
||||
<a href="https://www.idaa.org/idaa-bulletin-board" style="color:#2563eb;text-decoration:underline;" class="text-blue-600 underline">Bulletin Board</a>,
|
||||
please try once more. We apologize for the inconvenience, and thank you for your patience while we work to improve the IDAA experience!
|
||||
</p> -->
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user