refactor: consolidate obscure_email and browser reset into shared utilities
- Remove 6 local copies of obscure_email(); all now use ae_util.obscure_email() (badges list, badge review, badge print, presenter list, presenter detail, session view) - badge review page: add missing ae_util import - e_app_sys_bar: replace inline IDB/storage clear implementations with core_func.clear_idb() and core_func.clear_all_storage(); adds known-names fallback the inline version lacked - fix-sw page: replace 110-line inline nuke with core_func.clear_all_storage(log_callback); step-by-step logging and countdown UI preserved via BrowserResetLogFn callback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ import {
|
||||
User
|
||||
} from '@lucide/svelte';
|
||||
import { ae_loc, ae_sess } from '$lib/stores/ae_stores';
|
||||
import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||
|
||||
import Element_access_type from '$lib/app_components/e_app_access_type.svelte';
|
||||
import Element_sign_in_out from '$lib/app_components/e_app_sign_in_out.svelte';
|
||||
@@ -91,18 +92,11 @@ async function handle_clear_idb_only() {
|
||||
)
|
||||
)
|
||||
return;
|
||||
const db_list = await indexedDB.databases();
|
||||
console.log('[clear_idb] IDB databases found:', db_list.map((d) => d.name));
|
||||
for (const db of db_list) {
|
||||
if (db.name) indexedDB.deleteDatabase(db.name);
|
||||
}
|
||||
await core_func.clear_idb();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// ── Dev: full reset — SW + Cache Storage + IDB + localStorage/sessionStorage ──
|
||||
// SW and Cache Storage MUST be cleared here. Clearing IDB/localStorage alone leaves
|
||||
// the SW serving old JS bundles from its own Cache Storage on the next reload,
|
||||
// which means the user stays stuck on old code. Order: SW → cache → IDB → storage.
|
||||
async function handle_clear_storage_and_idb() {
|
||||
if (
|
||||
!confirm(
|
||||
@@ -110,21 +104,7 @@ async function handle_clear_storage_and_idb() {
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (const reg of registrations) await reg.unregister();
|
||||
}
|
||||
const cache_keys = await caches.keys();
|
||||
for (const key of cache_keys) await caches.delete(key);
|
||||
|
||||
const db_list = await indexedDB.databases();
|
||||
console.log('[clear_all] IDB databases found:', db_list.map((d) => d.name));
|
||||
for (const db of db_list) {
|
||||
if (db.name) indexedDB.deleteDatabase(db.name);
|
||||
}
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
await core_func.clear_all_storage();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +76,6 @@ function build_review_url(): string {
|
||||
return `/events/${$lq__event_badge_obj?.event_id}/badges/${$lq__event_badge_obj?.event_badge_id}/review`;
|
||||
}
|
||||
|
||||
function obscure_email(email: string | null | undefined): string {
|
||||
if (!email) return '';
|
||||
const at = email.indexOf('@');
|
||||
if (at < 0) return email;
|
||||
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
|
||||
}
|
||||
|
||||
// TODO: replace alert with actual email API call when available
|
||||
function send_review_email() {
|
||||
@@ -92,7 +86,7 @@ function send_review_email() {
|
||||
`${badge?.given_name ?? ''} ${badge?.family_name ?? ''}`.trim();
|
||||
const email = is_trusted
|
||||
? (badge?.email ?? '(no email on file)')
|
||||
: obscure_email(badge?.email);
|
||||
: ae_util.obscure_email(badge?.email);
|
||||
const event_name = EVENTS_MODULE_TITLE;
|
||||
alert(
|
||||
`PLACEHOLDER: An email will be sent to ${name} at ${email}. Use that link to review your ${event_name} badge.`
|
||||
|
||||
@@ -24,6 +24,7 @@ import { untrack } from 'svelte';
|
||||
import { liveQuery } from 'dexie';
|
||||
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { db_events } from '$lib/ae_events/db_events';
|
||||
import { EVENTS_MODULE_TITLE } from '$lib/stores/ae_events_stores';
|
||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||
@@ -158,12 +159,6 @@ async function copy_review_link() {
|
||||
}
|
||||
}
|
||||
|
||||
function obscure_email(email: string | null | undefined): string {
|
||||
if (!email) return '';
|
||||
const at = email.indexOf('@');
|
||||
if (at < 0) return email;
|
||||
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
|
||||
}
|
||||
|
||||
// TODO: replace alert with actual email API call when available
|
||||
function send_review_email() {
|
||||
@@ -174,7 +169,7 @@ function send_review_email() {
|
||||
`${badge?.given_name ?? ''} ${badge?.family_name ?? ''}`.trim();
|
||||
const email = is_trusted
|
||||
? (badge?.email ?? '(no email on file)')
|
||||
: obscure_email(badge?.email);
|
||||
: ae_util.obscure_email(badge?.email);
|
||||
const event_name = EVENTS_MODULE_TITLE;
|
||||
alert(
|
||||
`PLACEHOLDER: An email will be sent to ${name} at ${email}. Use that link to review your ${event_name} badge.`
|
||||
|
||||
@@ -52,17 +52,6 @@ let is_manager = $derived($ae_loc.manager_access === true);
|
||||
let is_public = $derived($ae_loc.public_access === true); // public passcode or higher — may print first prints
|
||||
let is_edit_mode = $derived($ae_loc.edit_mode === true);
|
||||
|
||||
/**
|
||||
* Obscures an email address for display to non-trusted users.
|
||||
* e.g. john.doe@example.com → joh***@example.com
|
||||
*/
|
||||
function obscure_email(email: string | null | undefined): string {
|
||||
if (!email) return '';
|
||||
const at = email.indexOf('@');
|
||||
if (at < 0) return email;
|
||||
const visible = email.slice(0, Math.min(3, at));
|
||||
return `${visible}***${email.slice(at)}`;
|
||||
}
|
||||
|
||||
function build_review_url(event_badge_obj: any): string {
|
||||
// TODO: append ?passcode=... when person_passcode is added to the event_badge schema
|
||||
@@ -96,7 +85,7 @@ function do_send_review_email(event_badge_obj: any) {
|
||||
`${event_badge_obj?.given_name ?? ''} ${event_badge_obj?.family_name ?? ''}`.trim();
|
||||
const email = is_trusted
|
||||
? (event_badge_obj?.email ?? '(no email on file)')
|
||||
: obscure_email(event_badge_obj?.email);
|
||||
: ae_util.obscure_email(event_badge_obj?.email);
|
||||
const event_name = EVENTS_MODULE_TITLE;
|
||||
alert(
|
||||
`PLACEHOLDER: An email will be sent to ${name} at ${email}. Use that link to review your ${event_name} badge.`
|
||||
@@ -243,7 +232,7 @@ let visible_badge_obj_li = $derived(
|
||||
>
|
||||
{is_trusted
|
||||
? event_badge_obj.email
|
||||
: obscure_email(event_badge_obj.email)}
|
||||
: ae_util.obscure_email(event_badge_obj.email)}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
@@ -315,7 +304,7 @@ let visible_badge_obj_li = $derived(
|
||||
">
|
||||
{is_trusted
|
||||
? event_badge_obj.email
|
||||
: obscure_email(event_badge_obj.email)}
|
||||
: ae_util.obscure_email(event_badge_obj.email)}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -185,12 +185,6 @@ let presenter_sign_in_url = $derived((() => {
|
||||
|
||||
// *** Functions and Logic
|
||||
|
||||
function obscure_email(email: string | null | undefined): string {
|
||||
if (!email) return '';
|
||||
const at = email.indexOf('@');
|
||||
if (at < 0) return email;
|
||||
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -335,7 +329,7 @@ function obscure_email(email: string | null | undefined): string {
|
||||
}
|
||||
const display_email = $ae_loc.trusted_access
|
||||
? use_email
|
||||
: obscure_email(use_email);
|
||||
: ae_util.obscure_email(use_email);
|
||||
if (!confirm(`This will send the sign in email to ${display_email}`)) {
|
||||
return;
|
||||
}
|
||||
@@ -366,7 +360,7 @@ function obscure_email(email: string | null | undefined): string {
|
||||
});
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 m-0.25 border"
|
||||
title="Email the access link to {$lq__event_presenter_obj?.full_name ?? 'presenter'} ({$ae_loc.trusted_access ? ($lq__event_presenter_obj?.person_primary_email ?? $lq__event_presenter_obj?.email ?? 'no email on file') : obscure_email($lq__event_presenter_obj?.person_primary_email ?? $lq__event_presenter_obj?.email)})">
|
||||
title="Email the access link to {$lq__event_presenter_obj?.full_name ?? 'presenter'} ({$ae_loc.trusted_access ? ($lq__event_presenter_obj?.person_primary_email ?? $lq__event_presenter_obj?.email ?? 'no email on file') : ae_util.obscure_email($lq__event_presenter_obj?.person_primary_email ?? $lq__event_presenter_obj?.email)})">
|
||||
<Mail size="1em" class="" />
|
||||
<Link size="1em" class="" />
|
||||
Email Access Link
|
||||
|
||||
@@ -68,12 +68,6 @@ let ae_tmp: key_val = $state({});
|
||||
|
||||
// *** Functions and Logic
|
||||
|
||||
function obscure_email(email: string | null | undefined): string {
|
||||
if (!email) return '';
|
||||
const at = email.indexOf('@');
|
||||
if (at < 0) return email;
|
||||
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="float-right flex flex-row items-center">
|
||||
@@ -210,7 +204,7 @@ function obscure_email(email: string | null | undefined): string {
|
||||
}
|
||||
const display_email = $ae_loc.trusted_access
|
||||
? use_email
|
||||
: obscure_email(use_email);
|
||||
: ae_util.obscure_email(use_email);
|
||||
if (!confirm(`This will send the access link email to ${event_presenter_obj.full_name ?? 'this presenter'} (${display_email})?`)) {
|
||||
return;
|
||||
}
|
||||
@@ -241,7 +235,7 @@ function obscure_email(email: string | null | undefined): string {
|
||||
}}
|
||||
class="btn preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 my-0.5 border transition-all hover:transition-all"
|
||||
class:btn-sm={display_mode != 'default'}
|
||||
title="Email the access link to {event_presenter_obj.full_name ?? 'presenter'} ({$ae_loc.trusted_access ? (event_presenter_obj.person_primary_email ?? event_presenter_obj.email ?? 'no email on file') : obscure_email(event_presenter_obj.person_primary_email ?? event_presenter_obj.email)})">
|
||||
title="Email the access link to {event_presenter_obj.full_name ?? 'presenter'} ({$ae_loc.trusted_access ? (event_presenter_obj.person_primary_email ?? event_presenter_obj.email ?? 'no email on file') : ae_util.obscure_email(event_presenter_obj.person_primary_email ?? event_presenter_obj.email)})">
|
||||
<Mail size="1em" class="mr-1" />
|
||||
Email Access Link
|
||||
</button>
|
||||
|
||||
@@ -191,19 +191,13 @@ $effect(() => {
|
||||
}
|
||||
});
|
||||
|
||||
function obscure_email(email: string | null | undefined): string {
|
||||
if (!email) return '';
|
||||
const at = email.indexOf('@');
|
||||
if (at < 0) return email;
|
||||
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
|
||||
}
|
||||
|
||||
async function send_poc_email_link() {
|
||||
const sess = $lq__event_session_obj;
|
||||
if (!sess?.poc_person_primary_email) return;
|
||||
const display_email = $ae_loc.trusted_access
|
||||
? sess.poc_person_primary_email
|
||||
: obscure_email(sess.poc_person_primary_email);
|
||||
: ae_util.obscure_email(sess.poc_person_primary_email);
|
||||
if (!confirm(`Send sign-in link to ${sess.poc_person_full_name} (${display_email})?`)) return;
|
||||
poc_email_status = 'sending';
|
||||
try {
|
||||
@@ -498,7 +492,7 @@ async function send_poc_email_link() {
|
||||
type="button"
|
||||
disabled={poc_email_status === 'sending'}
|
||||
onclick={send_poc_email_link}
|
||||
title="Email the sign-in link to {pres_mgmt_loc.current.label__session_poc_name}: {$lq__event_session_obj?.poc_person_full_name} ({$ae_loc.trusted_access ? $lq__event_session_obj?.poc_person_primary_email : obscure_email($lq__event_session_obj?.poc_person_primary_email)})"
|
||||
title="Email the sign-in link to {pres_mgmt_loc.current.label__session_poc_name}: {$lq__event_session_obj?.poc_person_full_name} ({$ae_loc.trusted_access ? $lq__event_session_obj?.poc_person_primary_email : ae_util.obscure_email($lq__event_session_obj?.poc_person_primary_email)})"
|
||||
class="btn btn-sm preset-outlined-secondary-300-700 transition-all duration-200"
|
||||
class:preset-tonal-secondary={poc_email_status === 'idle'}
|
||||
class:preset-tonal-warning={poc_email_status === 'sending'}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { RefreshCw } from '@lucide/svelte';
|
||||
import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||
|
||||
let status: string[] = $state([]);
|
||||
let done = $state(false);
|
||||
@@ -14,122 +15,12 @@ function log(msg: string) {
|
||||
console.log(`[FIX-SW] ${msg}`);
|
||||
}
|
||||
|
||||
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();
|
||||
if (registrations.length === 0) {
|
||||
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})`);
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
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}`);
|
||||
}
|
||||
await core_func.clear_all_storage((msg, level) => {
|
||||
if (level === 'error') has_error = true;
|
||||
const prefix = level === 'ok' ? '✓ ' : level === 'warn' ? '⚠ ' : '';
|
||||
log(prefix + msg);
|
||||
});
|
||||
|
||||
log(`─── Reset complete. Reloading in ${RELOAD_DELAY_S} seconds... ───`);
|
||||
done = true;
|
||||
|
||||
Reference in New Issue
Block a user