feat(inbox): add navigation links to watched and scheduler notices
Extend the reload-safe meta+link pattern (introduced for "Subscribed") to more inbox entries, so every notice that can point somewhere does: - A "Marked watched" activity notice now offers "Find in feed" (jumping to the feed filtered to Watched + that channel), alongside Unwatch — matching what the "Hidden" notice already does. VideoWatchedMeta carries the channel, and locate() handles both hidden and watched. - A scheduler job-completion notice (System) now offers "Open Scheduler", jumping to the dashboard. Maintenance notices reference already-removed videos, so they stay informational with nothing to navigate to.
This commit is contained in:
parent
a004fa4107
commit
c2b877c14e
6 changed files with 38 additions and 7 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useSyncExternalStore } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { Bell, Check, CheckCheck, ExternalLink, Eye, RotateCcw, Search, Trash2, Tv, X } from "lucide-react";
|
||||
import { Activity, Bell, Check, CheckCheck, ExternalLink, Eye, RotateCcw, Search, Trash2, Tv, X } from "lucide-react";
|
||||
import { api, type AppNotification, type FeedFilters } from "../lib/api";
|
||||
import { useLiveQuery } from "../lib/useLiveQuery";
|
||||
import {
|
||||
|
|
@ -94,11 +94,12 @@ export default function NotificationsPanel({
|
|||
clearMut.mutate();
|
||||
}
|
||||
|
||||
// "Find in feed" / undo, ported from the old bell.
|
||||
function locateHidden(meta: VideoHiddenMeta) {
|
||||
// "Find in feed" / undo, ported from the old bell. Works for both hidden and watched
|
||||
// notices — jump to the feed filtered to that state and the video's channel.
|
||||
function locate(meta: VideoHiddenMeta | VideoWatchedMeta) {
|
||||
setFilters({
|
||||
...filters,
|
||||
show: "hidden",
|
||||
show: meta.kind === "video-hidden" ? "hidden" : "watched",
|
||||
channelId: meta.channelId || undefined,
|
||||
channelName: meta.channelName || undefined,
|
||||
});
|
||||
|
|
@ -168,6 +169,7 @@ export default function NotificationsPanel({
|
|||
t={t}
|
||||
onRead={() => readMut.mutate(n.id)}
|
||||
onDismiss={() => dismissMut.mutate(n.id)}
|
||||
onOpenScheduler={() => setPage("scheduler")}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -184,7 +186,7 @@ export default function NotificationsPanel({
|
|||
key={n.id}
|
||||
n={n}
|
||||
t={t}
|
||||
onFind={locateHidden}
|
||||
onFind={locate}
|
||||
onRevert={revertState}
|
||||
onFocusChannel={onFocusChannel}
|
||||
onRemove={() => removeClient(n.id)}
|
||||
|
|
@ -204,11 +206,13 @@ function NotificationRow({
|
|||
t,
|
||||
onRead,
|
||||
onDismiss,
|
||||
onOpenScheduler,
|
||||
}: {
|
||||
n: AppNotification;
|
||||
t: (k: string, o?: any) => string;
|
||||
onRead: () => void;
|
||||
onDismiss: () => void;
|
||||
onOpenScheduler: () => void;
|
||||
}) {
|
||||
// The maintenance producer ships the affected videos in `data.videos`; list a few titles.
|
||||
const videos: { id: string; title: string | null }[] = Array.isArray(n.data?.videos)
|
||||
|
|
@ -261,6 +265,15 @@ function NotificationRow({
|
|||
)}
|
||||
</ul>
|
||||
)}
|
||||
{isScheduler && (
|
||||
<button
|
||||
onClick={onOpenScheduler}
|
||||
className="flex items-center gap-1 text-accent text-sm font-semibold hover:underline mt-1.5"
|
||||
>
|
||||
<Activity className="w-3.5 h-3.5" />
|
||||
{t("inbox.openScheduler")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
{!n.read && (
|
||||
|
|
@ -296,7 +309,7 @@ function ClientActivityRow({
|
|||
}: {
|
||||
n: ClientNotif;
|
||||
t: (k: string, o?: any) => string;
|
||||
onFind: (meta: VideoHiddenMeta) => void;
|
||||
onFind: (meta: VideoHiddenMeta | VideoWatchedMeta) => void;
|
||||
onRevert: (meta: VideoHiddenMeta | VideoWatchedMeta) => void;
|
||||
onFocusChannel: (name: string) => void;
|
||||
onRemove: () => void;
|
||||
|
|
@ -342,6 +355,13 @@ function ClientActivityRow({
|
|||
</div>
|
||||
) : watched ? (
|
||||
<div className="flex items-center gap-3 mt-1">
|
||||
<button
|
||||
onClick={() => onFind(watched)}
|
||||
className="flex items-center gap-1 text-accent text-sm font-semibold hover:underline"
|
||||
>
|
||||
<Search className="w-3.5 h-3.5" />
|
||||
{t("notifications.findInFeed")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onRevert(watched)}
|
||||
className="flex items-center gap-1 text-accent text-sm font-semibold hover:underline"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue