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:
npeter83 2026-06-11 19:46:58 +02:00
parent a105e5c184
commit a419ac2943
6 changed files with 173 additions and 71 deletions

View file

@ -16,13 +16,13 @@ import {
export const LEVEL_STYLE: Record<
NotifLevel,
{ icon: typeof Info; color: string }
{ icon: typeof Info; color: string; bar: string }
> = {
info: { icon: Info, color: "text-accent" },
success: { icon: CheckCircle2, color: "text-emerald-400" },
warning: { icon: AlertTriangle, color: "text-amber-400" },
error: { icon: AlertCircle, color: "text-red-400" },
fatal: { icon: XCircle, color: "text-red-500" },
info: { icon: Info, color: "text-accent", bar: "bg-accent" },
success: { icon: CheckCircle2, color: "text-emerald-400", bar: "bg-emerald-400" },
warning: { icon: AlertTriangle, color: "text-amber-400", bar: "bg-amber-400" },
error: { icon: AlertCircle, color: "text-red-400", bar: "bg-red-400" },
fatal: { icon: XCircle, color: "text-red-500", bar: "bg-red-500" },
};
export default function Toaster() {
@ -31,11 +31,11 @@ export default function Toaster() {
return (
<div className="fixed top-4 right-4 z-50 flex flex-col gap-2 w-80 max-w-[calc(100vw-2rem)]">
{toasts.map((t) => {
const { icon: Icon, color } = LEVEL_STYLE[t.level];
const { icon: Icon, color, bar } = LEVEL_STYLE[t.level];
return (
<div
key={t.id}
className="bg-surface border border-border rounded-xl shadow-2xl px-3 py-3 flex items-start gap-3 animate-[fadeIn_0.15s_ease]"
className="relative overflow-hidden bg-surface border border-border rounded-xl shadow-2xl px-3 py-3 flex items-start gap-3"
>
<Icon className={`w-5 h-5 shrink-0 mt-0.5 ${color}`} />
<div className="min-w-0 flex-1">
@ -60,6 +60,12 @@ export default function Toaster() {
>
<X className="w-4 h-4" />
</button>
{t.duration && (
<div
className={`absolute left-0 bottom-0 h-0.5 w-full origin-left ${bar}`}
style={{ animation: `toastbar ${t.duration}ms linear forwards` }}
/>
)}
</div>
);
})}