refactor(web): pushToast store + ToastHost; 8 sites migrated (W3)

This commit is contained in:
2026-05-08 05:45:34 -04:00
parent 970752a153
commit 617477b702
12 changed files with 229 additions and 223 deletions
+14 -31
View File
@@ -16,6 +16,7 @@
regenerateAPIToken
} from '$lib/api/me';
import { errCode } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
const queryClient = useQueryClient();
@@ -40,17 +41,6 @@
$enabledMutation.mutate(!$status.data.enabled);
}
// Toast -------------------------------------------------------------------
let toast = $state<string | null>(null);
let toastTimer: ReturnType<typeof setTimeout> | undefined;
function showToast(msg: string) {
toast = msg;
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(() => { toast = null; }, 4000);
}
// Profile card ------------------------------------------------------------
let profileForm = $state<{ displayName: string; email: string }>({
@@ -66,12 +56,12 @@
display_name: profileForm.displayName,
email: profileForm.email,
});
showToast('Profile saved.');
pushToast('Profile saved.');
} catch (e: unknown) {
const code = errCode(e);
if (code === 'email_taken') showToast('That email is already in use.');
else if (code === 'email_invalid') showToast('Email format is invalid.');
else showToast(`Save failed: ${code}`);
if (code === 'email_taken') pushToast('That email is already in use.', 'error');
else if (code === 'email_invalid') pushToast('Email format is invalid.', 'error');
else pushToast(`Save failed: ${code}`, 'error');
} finally {
profileSaving = false;
}
@@ -87,19 +77,19 @@
async function onSavePassword(e: SubmitEvent) {
e.preventDefault();
if (passwordForm.new !== passwordForm.confirm) {
showToast('New passwords do not match.');
pushToast('New passwords do not match.', 'error');
return;
}
passwordSaving = true;
try {
await changePassword(passwordForm.current, passwordForm.new);
showToast('Password changed.');
pushToast('Password changed.');
passwordForm = { current: '', new: '', confirm: '' };
} catch (e: unknown) {
const code = errCode(e);
if (code === 'wrong_password') showToast('Current password is incorrect.');
else if (code === 'password_too_short') showToast('Password must be at least 8 characters.');
else showToast(`Change failed: ${code}`);
if (code === 'wrong_password') pushToast('Current password is incorrect.', 'error');
else if (code === 'password_too_short') pushToast('Password must be at least 8 characters.', 'error');
else pushToast(`Change failed: ${code}`, 'error');
} finally {
passwordSaving = false;
}
@@ -120,9 +110,9 @@
if (!apiToken) return;
try {
await navigator.clipboard.writeText(apiToken);
showToast('Token copied to clipboard.');
pushToast('Token copied to clipboard.');
} catch {
showToast('Copy failed.');
pushToast('Copy failed.', 'error');
}
}
@@ -139,9 +129,9 @@
try {
const r = await regenerateAPIToken();
apiToken = r.api_token;
showToast('API token regenerated.');
pushToast('API token regenerated.');
} catch (e: unknown) {
showToast(`Regenerate failed: ${errCode(e)}`);
pushToast(`Regenerate failed: ${errCode(e)}`, 'error');
} finally {
tokenSaving = false;
}
@@ -150,13 +140,6 @@
<svelte:head><title>{pageTitle('Settings')}</title></svelte:head>
{#if toast}
<div data-testid="toast" role="status"
class="fixed bottom-4 right-4 z-50 rounded-md border border-border bg-surface px-4 py-2 text-sm text-text-primary shadow">
{toast}
</div>
{/if}
<div class="space-y-6">
<h1 class="text-2xl font-semibold">Settings</h1>