import { useState, useMemo } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { homeFeed, surpriseMe, getSettings, updateSettings, getChannels, markChannelsSeen } from "../api"; import VideoCard from "../components/VideoCard"; import { scrollToTop } from "../utils/scroll"; const PAGE_SIZE = 25; const FEED_MODES = [ { value: "ranked", label: "For you", hint: "Ranked by your taste" }, { value: "chronological", label: "New", hint: "Everything in date order" }, { value: "rediscover", label: "Rediscover", hint: "Older unwatched videos ranked by your taste" }, { value: "random", label: "Explore", hint: "Random from discovery pool" }, { value: "inbox", label: "Inbox", hint: "New from followed channels since last visit" }, ]; export default function Home() { const qc = useQueryClient(); const [surpriseResults, setSurpriseResults] = useState(null); const [mode, setMode] = useState(() => localStorage.getItem("home-feed-mode") ?? "ranked"); const [page, setPage] = useState(0); const [dismissed, setDismissed] = useState(new Set()); const [shuffleKey, setShuffleKey] = useState(0); const [duration, setDuration] = useState(""); const [viewMode, setViewMode] = useState(() => localStorage.getItem("home-view-mode") ?? "list"); const toggleViewMode = () => { const next = viewMode === "grid" ? "list" : "grid"; localStorage.setItem("home-view-mode", next); setViewMode(next); }; const { data: userSettings } = useQuery({ queryKey: ["settings"], queryFn: () => getSettings().then(r => r.data), staleTime: 60_000, }); const { data: channels = [] } = useQuery({ queryKey: ["channels"], queryFn: () => getChannels().then(r => r.data), staleTime: 60_000, }); const inboxCount = channels.reduce((sum, c) => sum + (c.new_count ?? 0), 0); const markSeenMut = useMutation({ mutationFn: () => markChannelsSeen(), onSuccess: () => qc.invalidateQueries({ queryKey: ["channels"] }), }); const hideWatched = userSettings?.hide_watched_from_feed ?? false; const handleHideWatchedToggle = () => { const next = !hideWatched; updateSettings({ hide_watched_from_feed: next }); qc.setQueryData(["settings"], old => old ? { ...old, hide_watched_from_feed: next } : old); }; const { data: feedData = [], isLoading: loadingFeed } = useQuery({ queryKey: ["home-feed", mode, page, hideWatched, duration, mode === "random" ? shuffleKey : 0], queryFn: () => homeFeed(page, PAGE_SIZE, mode, duration).then((r) => r.data), staleTime: 10 * 60_000, placeholderData: (prev) => prev, }); const surpriseMut = useMutation({ mutationFn: () => surpriseMe().then((r) => r.data), onSuccess: (data) => setSurpriseResults(data), }); const visibleFeed = useMemo( () => feedData.filter(v => !dismissed.has(v.youtube_video_id)), [feedData, dismissed], ); const hasFollowing = channels.length > 0 || feedData.length > 0 || page > 0; const hasNextPage = mode === "ranked" ? feedData.filter(v => !v.is_recommended).length === PAGE_SIZE : feedData.length === PAGE_SIZE; const handleDismiss = (video) => setDismissed(prev => new Set([...prev, video.youtube_video_id])); const handleModeChange = (newMode) => { localStorage.setItem("home-feed-mode", newMode); setMode(newMode); setPage(0); setDismissed(new Set()); }; const handleDurationChange = (d) => { setDuration(prev => prev === d ? "" : d); setPage(0); setDismissed(new Set()); }; const handleReshuffle = () => { setShuffleKey(k => k + 1); setPage(0); setDismissed(new Set()); scrollToTop(); }; return (
Unwatched videos from followed channels since your last visit.
All videos from channels you follow, newest first.
)} {mode === "random" && (Random from your discovery pool — no weighting, no ranking.
{mode === "inbox" ? "You're all caught up — no new videos since your last visit." : "Nothing to show here."}
) : (Follow some channels to get a personalised feed. Or let us pick something.
{surpriseResults && (Want something random?
{surpriseResults && (