Remove Activity and the notification system

This commit is contained in:
2026-10-05 17:25:39 -04:00
parent 193383ddd5
commit b62fb5db30
11 changed files with 32 additions and 216 deletions
+5 -16
View File
@@ -7,11 +7,8 @@
import { startFleetMonitor, hydrateFleet } from '$ui/stores/fleet.js';
import { page } from '$app/stores';
import BrandMark from '$ui/components/BrandMark.svelte';
import Toaster from '$ui/components/Toaster.svelte';
import Confirmation from '$ui/components/Confirmation.svelte';
import NotificationHistory from '$ui/components/NotificationHistory.svelte';
import Dialog from '$ui/components/primitives/Dialog.svelte';
import { notificationHistory, startNotificationHistory } from '$ui/toast.js';
import { resolveConfirmation } from '$ui/confirm.js';
import '$ui/styles/components.css';
import { enableViewTransitions } from '$ui/motion/viewTransition.js';
@@ -28,9 +25,7 @@
let accountOpen = $state(false);
let signingOut = $state(false);
let signOutError = $state('');
let notificationsOpen = $state(false);
let helpOpen = $state(false);
const unread = $derived($notificationHistory.filter(item => !item.read).length);
let menuButton = $state<HTMLButtonElement>();
let accountButton = $state<HTMLButtonElement>();
const accountName = $derived(data.user?.displayName || data.user?.username || 'Account');
@@ -40,10 +35,9 @@
if (event.target instanceof Element && !event.target.closest('.account-menu')) accountOpen = false;
}
function openUtility(panel: 'notifications' | 'help') {
function openHelp() {
if (menuOpen) { menuButton?.focus(); menuOpen = false; }
if (panel === 'notifications') notificationsOpen = true;
else helpOpen = true;
helpOpen = true;
}
beforeNavigate(() => performance.mark('ec:navigation-start'));
@@ -64,18 +58,15 @@
try {
const response = await fetch('/api/auth/logout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' });
if (!response.ok) { const body = await response.json(); throw new Error(body.error ?? 'Could not sign out. Try again.'); }
try { sessionStorage.removeItem('electric-cherry:notifications'); } catch { /* Optional local history. */ }
notificationHistory.set([]);
window.location.assign('/login');
} catch (cause) { signOutError = (cause as Error).message; signingOut = false; }
}
$effect(() => {
if (!sessionReady) return;
const stopNotifications = startNotificationHistory();
const stopPipeline = startPipelineStream();
const stopServers = startServerStream();
return () => { stopNotifications(); stopPipeline?.(); stopServers?.(); };
return () => { stopPipeline?.(); stopServers?.(); };
});
$effect(() => {
@@ -103,7 +94,7 @@
const target = e.target as HTMLElement;
if (target && (['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName) || target.isContentEditable || target.closest('dialog'))) return;
if (e.metaKey || e.ctrlKey || e.altKey) return;
if (e.key === '?') { e.preventDefault(); openUtility('help'); return; }
if (e.key === '?') { e.preventDefault(); openHelp(); return; }
if (!armed && e.key === 'g') {
armed = true;
@@ -215,7 +206,7 @@
{/each}
</div>
<div class="nav-tools" class:expanded={menuOpen}>
<button class="utility" aria-label={`Recent notifications${unread ? `, ${unread} unread` : ''}`} onclick={() => openUtility('notifications')}>Activity{unread ? ` · ${unread}` : ''}</button><button class="utility" aria-label="Keyboard shortcuts" title="Keyboard shortcuts (?)" onclick={() => openUtility('help')}>?</button>
<button class="utility" aria-label="Keyboard shortcuts" title="Keyboard shortcuts (?)" onclick={() => openHelp()}>?</button>
</div>
{/if}
</nav>
@@ -225,9 +216,7 @@
{#if sessionReady && data.user?.role === 'viewer'}<aside class="viewer-note"><strong>Viewer access</strong><span>You can read fleet status and summaries. Operating servers or viewing raw configs, file changes, logs, and telemetry requires an operator account.</span></aside>{/if}
{@render children()}
</main>
<Toaster />
<Confirmation />
<NotificationHistory bind:open={notificationsOpen} />
<Dialog bind:open={helpOpen} title="Keyboard shortcuts" description="Press g, then the destination key. Shortcuts are inactive while typing or using a dialog.">
<dl class="shortcuts">{#each [['g f','Fleet'],['g o','Hosts'],['g s','Scripts'],['g c','Configs'],['g p','Plugins'],['g d','Provisioning'],['g a','Campaigns'],['g w','Maintenance'],['g b','Betas'],...(data.user?.role === 'admin' ? [['g h','Settings'],['g k','Keys']] : []),['g l','Leaderboard'],['g m','Metrics'],['?','This help']] as [keys,label]}<div><dt><kbd>{keys}</kbd></dt><dd>{label}</dd></div>{/each}</dl>
</Dialog>
-1
View File
@@ -24,7 +24,6 @@
try {
const response = await fetch('/api/auth/logout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' });
if (!response.ok) { const body = await response.json(); throw new Error(body.error ?? 'Could not sign out.'); }
try { sessionStorage.removeItem('electric-cherry:notifications'); } catch { /* Optional history. */ }
window.location.assign('/login');
} catch (cause) { error = (cause as Error).message; signingOut = false; }
}
+6 -4
View File
@@ -7,7 +7,6 @@
import { timeFormat } from '$ui/stores/time-zone.js';
import { pollVisible } from '$ui/poll.js';
import { serverTitle } from '$ui/server-labels.js';
import { toast } from '$ui/toast.js';
import { createProvisioningRefresh, type ProvisioningRefreshState } from '$ui/provisioning-refresh.js';
import { elapsedTime, isProvisioningDetail, provisioningOutcome, provisioningReturn } from '$ui/provisioning-presentation.js';
import SectionHeader from '$ui/components/primitives/SectionHeader.svelte';
@@ -30,6 +29,7 @@
});
let now = $state(Date.parse(initial.generatedAt));
let launchOpen = $state(false);
let copyStatus = $state('');
const controller = createProvisioningRefresh<ProvisioningDetail>({ validate: isProvisioningDetail, onChange: value => { view = value; } });
const detail = $derived(view.detail);
const deployment = $derived(detail?.deployment);
@@ -48,6 +48,7 @@
const snapshot = data.detail;
untrack(() => {
launchOpen = false;
copyStatus = '';
now = Date.parse(snapshot.generatedAt);
controller.setOperation(snapshot.deployment.id, snapshot, snapshot.generatedAt);
});
@@ -56,12 +57,13 @@
onDestroy(() => controller.dispose());
function fmt(value: string | null | undefined) { return value ? $timeFormat.dateTime(value) : 'Not recorded'; }
async function copyLink() {
copyStatus = '';
try {
const url = new URL($page.url);
url.search = ''; url.hash = '';
await navigator.clipboard.writeText(url.toString());
toast('Operation link copied.', { variant: 'success' });
} catch { toast('Copy unavailable. Copy this page’s address from your browser.', { variant: 'warning' }); }
copyStatus = 'Operation link copied.';
} catch { copyStatus = 'Copy unavailable. Copy this page’s address from your browser.'; }
}
</script>
@@ -74,7 +76,7 @@
</SectionHeader>
<div class="identity-row">
{#if deployment}<div class="operation-id"><span>Operation</span><CopyValue value={deployment.id} label="operation ID" /></div>{/if}
<div class="identity-links"><a href={returnTo.href}>← {returnTo.label}</a><Button size="sm" variant="ghost" onclick={copyLink}>Copy link</Button></div>
<div class="identity-links"><a href={returnTo.href}>← {returnTo.label}</a><Button size="sm" variant="ghost" onclick={copyLink}>Copy link</Button>{#if copyStatus}<span role="status">{copyStatus}</span>{/if}</div>
</div>
{#if view.loadError}<div class="refresh-error" role="alert"><div><strong>Refresh unavailable</strong><p>{view.loadError} {detail ? 'Showing the last successfully loaded record.' : ''}</p></div><Button size="sm" disabled={view.refreshing || !!view.busy} onclick={() => controller.forceRefresh()}>Retry refresh</Button></div>{/if}