feat(channels): status filter + header full-history link + stable priority
- header: per-user "N without full history" count (channels_deep_pending), clickable with a hint -> opens the channel manager filtered to those. - channel manager: status filter chips (All / Needs full history / Fully synced / Hidden); the header link deep-links to "Needs full history". - fix: priority up/down is now an optimistic in-place cache update (no refetch / re-sort), so the list no longer jumps to the top and loses your scroll position; the new order applies on the next page load.
This commit is contained in:
parent
c949630b89
commit
aa38e972fd
4 changed files with 92 additions and 10 deletions
|
|
@ -18,12 +18,25 @@ import { notify } from "../lib/notifications";
|
|||
import Tooltip from "./Tooltip";
|
||||
import Avatar from "./Avatar";
|
||||
|
||||
export type ChannelStatusFilter = "all" | "needs_full" | "fully_synced" | "hidden";
|
||||
|
||||
const STATUS_FILTERS: { id: ChannelStatusFilter; label: string }[] = [
|
||||
{ id: "all", label: "All" },
|
||||
{ id: "needs_full", label: "Needs full history" },
|
||||
{ id: "fully_synced", label: "Fully synced" },
|
||||
{ id: "hidden", label: "Hidden" },
|
||||
];
|
||||
|
||||
export default function Channels({
|
||||
canWrite,
|
||||
onViewChannel,
|
||||
statusFilter,
|
||||
setStatusFilter,
|
||||
}: {
|
||||
canWrite: boolean;
|
||||
onViewChannel: (id: string, name: string) => void;
|
||||
statusFilter: ChannelStatusFilter;
|
||||
setStatusFilter: (f: ChannelStatusFilter) => void;
|
||||
}) {
|
||||
const qc = useQueryClient();
|
||||
const channelsQuery = useQuery({ queryKey: ["channels"], queryFn: api.channels });
|
||||
|
|
@ -54,6 +67,19 @@ export default function Channels({
|
|||
qc.invalidateQueries({ queryKey: ["feed-count"] });
|
||||
},
|
||||
});
|
||||
// Priority is updated optimistically in place and NOT re-sorted/refetched, so the list
|
||||
// doesn't jump (the server list is priority-sorted; refetching would reorder rows and
|
||||
// lose your scroll position). The new order takes effect next time the page loads.
|
||||
const bumpPriority = (id: string, current: number, delta: number) => {
|
||||
const next = current + delta;
|
||||
qc.setQueryData<ManagedChannel[]>(["channels"], (old) =>
|
||||
(old ?? []).map((ch) => (ch.id === id ? { ...ch, priority: next } : ch))
|
||||
);
|
||||
api
|
||||
.updateChannel(id, { priority: next })
|
||||
.catch(() => qc.invalidateQueries({ queryKey: ["channels"] }));
|
||||
};
|
||||
|
||||
const attach = useMutation({
|
||||
mutationFn: (v: { id: string; tagId: number }) => api.attachChannelTag(v.id, v.tagId),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["channels"] }),
|
||||
|
|
@ -103,9 +129,17 @@ export default function Channels({
|
|||
onError: () => notify({ level: "error", message: "Couldn't request full history" }),
|
||||
});
|
||||
|
||||
const channels = (channelsQuery.data ?? []).filter(
|
||||
(c) => !q || (c.title ?? "").toLowerCase().includes(q.toLowerCase())
|
||||
);
|
||||
const channels = (channelsQuery.data ?? [])
|
||||
.filter((c) => !q || (c.title ?? "").toLowerCase().includes(q.toLowerCase()))
|
||||
.filter((c) =>
|
||||
statusFilter === "needs_full"
|
||||
? !c.backfill_done
|
||||
: statusFilter === "fully_synced"
|
||||
? c.backfill_done
|
||||
: statusFilter === "hidden"
|
||||
? c.hidden
|
||||
: true
|
||||
);
|
||||
const s = statusQuery.data;
|
||||
|
||||
return (
|
||||
|
|
@ -179,6 +213,23 @@ export default function Channels({
|
|||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/* Status filter */}
|
||||
<div className="flex flex-wrap items-center gap-1.5 mb-4">
|
||||
{STATUS_FILTERS.map((f) => (
|
||||
<button
|
||||
key={f.id}
|
||||
onClick={() => setStatusFilter(f.id)}
|
||||
className={`text-xs px-2.5 py-1 rounded-full border transition ${
|
||||
statusFilter === f.id
|
||||
? "bg-accent text-accent-fg border-accent"
|
||||
: "bg-card border-border text-muted hover:border-accent"
|
||||
}`}
|
||||
>
|
||||
{f.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted mb-4 leading-relaxed">
|
||||
Set a channel's <b className="text-fg/80">priority</b> to push its videos up when you sort
|
||||
by “Channel priority”, attach your own <b className="text-fg/80">tags</b> to filter the feed,
|
||||
|
|
@ -244,7 +295,7 @@ export default function Channels({
|
|||
unsubscribe.mutate(c.id);
|
||||
}}
|
||||
onView={() => onViewChannel(c.id, c.title ?? "This channel")}
|
||||
onPriority={(d) => patch.mutate({ id: c.id, body: { priority: c.priority + d } })}
|
||||
onPriority={(d) => bumpPriority(c.id, c.priority, d)}
|
||||
onHide={() => patch.mutate({ id: c.id, body: { hidden: !c.hidden } })}
|
||||
onDeep={() =>
|
||||
patch.mutate({ id: c.id, body: { deep_requested: !c.deep_requested } })
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue