Show 'N yours / M total' in the header (your subscriptions vs. the whole shared catalog) with a tooltip, backed by a new total_videos field on /sync/my-status. The admin pause button now only appears when there's sync work to pause; Resume still shows whenever sync is paused.
86 lines
3.4 KiB
TypeScript
86 lines
3.4 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import { Database, History, Loader2, Pause, Play } from "lucide-react";
|
|
import { api } from "../lib/api";
|
|
import { formatViews } from "../lib/format";
|
|
import Tooltip from "./Tooltip";
|
|
|
|
// Per-user status (not the global catalog): shows the number of videos available to *this*
|
|
// user, how many of *their* channels are still being fetched, and how many lack full
|
|
// history (clickable -> channel manager filtered to those). The pause control is admin-only.
|
|
export default function SyncStatus({
|
|
isAdmin,
|
|
onGoToFullHistory,
|
|
}: {
|
|
isAdmin: boolean;
|
|
onGoToFullHistory: () => void;
|
|
}) {
|
|
const qc = useQueryClient();
|
|
const { data } = useQuery({
|
|
queryKey: ["my-status"],
|
|
queryFn: api.myStatus,
|
|
refetchInterval: 30_000,
|
|
staleTime: 25_000,
|
|
});
|
|
|
|
const toggle = useMutation({
|
|
mutationFn: () => (data?.paused ? api.resumeSync() : api.pauseSync()),
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["my-status"] }),
|
|
});
|
|
|
|
if (!data) return null;
|
|
|
|
const syncing = data.channels_recent_pending;
|
|
const notFull = data.channels_deep_pending; // your channels without full history yet
|
|
|
|
return (
|
|
<div className="hidden lg:flex items-center gap-2 text-xs text-muted">
|
|
<Tooltip hint="Videos from your subscriptions, against the total in the shared catalog that every user's subscriptions have pulled in.">
|
|
<span className="flex items-center gap-1.5 cursor-default">
|
|
<Database className="w-3.5 h-3.5" />
|
|
<span>
|
|
<span className="text-fg font-medium">{formatViews(data.my_videos)}</span> yours
|
|
</span>
|
|
<span className="opacity-40">/</span>
|
|
<span>{formatViews(data.total_videos)} total</span>
|
|
</span>
|
|
</Tooltip>
|
|
<span className="opacity-40">·</span>
|
|
{data.paused ? (
|
|
<span className="text-accent font-medium">paused</span>
|
|
) : syncing > 0 ? (
|
|
<span className="flex items-center gap-1">
|
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
{syncing} syncing
|
|
</span>
|
|
) : (
|
|
<span>all synced</span>
|
|
)}
|
|
{notFull > 0 && (
|
|
<>
|
|
<span className="opacity-40">·</span>
|
|
<Tooltip hint="Some of your channels only have their recent uploads. Click to open the channel manager (filtered to these) and request their full back-catalog.">
|
|
<button
|
|
onClick={onGoToFullHistory}
|
|
className="flex items-center gap-1 hover:text-fg underline decoration-dotted decoration-muted/40 underline-offset-4 transition cursor-pointer"
|
|
>
|
|
<History className="w-3.5 h-3.5" />
|
|
{notFull} without full history
|
|
</button>
|
|
</Tooltip>
|
|
</>
|
|
)}
|
|
{/* Pause only makes sense when there's work to pause; Resume must always show
|
|
while paused so it can be lifted. Hidden entirely when idle and not paused. */}
|
|
{isAdmin && (data.paused || syncing > 0) && (
|
|
<button
|
|
onClick={() => toggle.mutate()}
|
|
disabled={toggle.isPending}
|
|
title={data.paused ? "Resume background sync" : "Pause background sync"}
|
|
className="ml-1 p-1 rounded-md hover:bg-card hover:text-fg transition"
|
|
>
|
|
{data.paused ? <Play className="w-3.5 h-3.5" /> : <Pause className="w-3.5 h-3.5" />}
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|