Desktop: background store reads from the tray (no document.hidden skip, backoff), notifications by kind (rejected, approved, live, halted, crash, reviews) with switches, quiet hours and mute, click-through on next show; tray menu with apps and their states, read now, mute, updates, settings, tooltip summary and attention dot; tray_update command; tests

This commit is contained in:
CRTkafa 2026-10-22 22:24:05 +08:00
parent 5d8a0f5e9a
commit 03ef999dac
27 changed files with 1050 additions and 55 deletions

View file

@ -1,12 +1,12 @@
// Shell: sidebar (studios, collapsible, with their apps; the workspace), top bar (breadcrumbs, last read, refresh),
// the screen, the command palette (Cmd/Ctrl+K), toasts and the first-run welcome guide.
import { useCallback, useEffect, useMemo, useState } from 'react';
import { FIXTURES, onDeepLink, onDeepLinkFolder } from './api';
import { FIXTURES, onDeepLink, onDeepLinkFolder, onTrayRoute } from './api';
import { devSwitch } from './dev/switches';
import { type Key, t } from './i18n';
import { ago, num } from './lib/format';
import { groupByStudio, type UnifiedApp } from './lib/identity';
import { NavCtx, parseStart, type Route, sameRoute, TAB_ICON, TAB_KEY, tabsFor, WORKSPACE, type WorkspaceScreen } from './nav';
import { NavCtx, parseRouteJson, parseStart, type Route, sameRoute, TAB_ICON, TAB_KEY, tabsFor, WORKSPACE, type WorkspaceScreen } from './nav';
import { usePrefs } from './prefs-ctx';
import { Accounts } from './screens/Accounts';
import { AppFolders } from './screens/AppFolders';
@ -67,7 +67,9 @@ export function App({ onTheme }: { onTheme: () => void }) {
const un = onDeepLink(s => { const r = parseStart(s, []); if (r) go(r); });
// `storebound desktop` in an app folder: the host added (after its native dialog) or found that folder
const unFolder = onDeepLinkFolder(id => { d.reload().catch(() => {}).finally(() => go({ screen: 'app', appId: id, tab: 'releases' })); });
return () => { un.then(f => f()); unFolder.then(f => f()); };
// the tray menu (an app, Settings, Check for updates) and the first show after a notification
const unTray = onTrayRoute(s => { const r = parseRouteJson(s); if (r) go(r); });
return () => { un.then(f => f()); unFolder.then(f => f()); unTray.then(f => f()); };
}, [go, d.reload]);
const app = route.screen === 'app' ? d.appByKey(route.appId) : undefined;