fix(ui): notification UX — outside-click close, toast countdown, hide entry actions
- NotificationCenter closes on outside click (document listener, not an overlay that the blurred header trapped) and no longer needs a second bell click. - Toasts show a level-colored countdown bar and auto-dismiss faster (6s default). - Hidden-video notifications carry structured meta so the center offers an in-app 'Find in feed' (jump to that channel's hidden videos) and a one-click 'Unhide', working even after a reload when the live Undo callback is gone.
This commit is contained in:
parent
42150edb92
commit
a86f829a2d
6 changed files with 173 additions and 71 deletions
|
|
@ -10,13 +10,25 @@ export interface NotifAction {
|
|||
onClick: () => void;
|
||||
}
|
||||
|
||||
// Structured payload that lets the Notification Center offer in-app actions even
|
||||
// after a reload (when live `action` callbacks are gone).
|
||||
export type NotifMeta = {
|
||||
kind: "video-hidden";
|
||||
videoId: string;
|
||||
title: string;
|
||||
channelId: string;
|
||||
channelName: string;
|
||||
};
|
||||
|
||||
export interface Notification {
|
||||
id: number;
|
||||
level: NotifLevel;
|
||||
title?: string;
|
||||
message: string;
|
||||
action?: NotifAction;
|
||||
meta?: NotifMeta;
|
||||
requiresInteraction: boolean;
|
||||
duration?: number; // auto-dismiss ms (undefined when it stays until acted on)
|
||||
ts: number;
|
||||
read: boolean;
|
||||
dismissed: boolean; // transient toast surface closed (still kept in history)
|
||||
|
|
@ -27,12 +39,13 @@ export interface NotifyInput {
|
|||
level?: NotifLevel;
|
||||
title?: string;
|
||||
action?: NotifAction;
|
||||
meta?: NotifMeta;
|
||||
requiresInteraction?: boolean;
|
||||
}
|
||||
|
||||
const HISTORY_KEY = "subfeed.notifications";
|
||||
const MAX_HISTORY = 100;
|
||||
const DEFAULT_TTL = 9000;
|
||||
const DEFAULT_TTL = 6000;
|
||||
const ERROR_TTL = 15000;
|
||||
|
||||
let items: Notification[] = load();
|
||||
|
|
@ -90,6 +103,11 @@ export function notify(input: NotifyInput): number {
|
|||
const id = counter++;
|
||||
const requiresInteraction = input.requiresInteraction ?? false;
|
||||
const level = input.level ?? "info";
|
||||
const duration = requiresInteraction
|
||||
? undefined
|
||||
: level === "error" || level === "fatal"
|
||||
? ERROR_TTL
|
||||
: DEFAULT_TTL;
|
||||
items = [
|
||||
...items,
|
||||
{
|
||||
|
|
@ -98,17 +116,16 @@ export function notify(input: NotifyInput): number {
|
|||
title: input.title,
|
||||
message: input.message,
|
||||
action: input.action,
|
||||
meta: input.meta,
|
||||
requiresInteraction,
|
||||
duration,
|
||||
ts: Date.now(),
|
||||
read: false,
|
||||
dismissed: false,
|
||||
},
|
||||
];
|
||||
emit();
|
||||
if (!requiresInteraction) {
|
||||
const ttl = level === "error" || level === "fatal" ? ERROR_TTL : DEFAULT_TTL;
|
||||
setTimeout(() => dismiss(id), ttl);
|
||||
}
|
||||
if (duration) setTimeout(() => dismiss(id), duration);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue