feat(feed): resume progress bar, play/continue/restart, in-progress filter
Video cards show a resume progress bar for started-but-unfinished videos and a hover overlay: Play on every card, Continue + Restart on in-progress ones. The in-app player now resumes from (and checkpoints to) the server position instead of localStorage, accepts an explicit startAt (Restart -> 0), and refreshes the feed on close so the card bar reflects the session. Sidebar gains an 'In progress' show filter.
This commit is contained in:
parent
686c40cbb9
commit
04c971f623
5 changed files with 114 additions and 30 deletions
|
|
@ -16,6 +16,9 @@ function matchesView(status: string, show: string): boolean {
|
|||
case "saved":
|
||||
return status === "saved";
|
||||
case "unwatched":
|
||||
case "in_progress":
|
||||
// (in_progress is further narrowed server-side by resume position; here we only
|
||||
// need to drop a card once it's optimistically marked watched/hidden.)
|
||||
return status !== "watched" && status !== "hidden";
|
||||
default:
|
||||
return status !== "hidden"; // all
|
||||
|
|
@ -36,9 +39,17 @@ export default function Feed({
|
|||
onOpenWizard: () => void;
|
||||
}) {
|
||||
const [overrides, setOverrides] = useState<Record<string, string>>({});
|
||||
const [activeVideo, setActiveVideo] = useState<Video | null>(null);
|
||||
// The open player: which video and where to start (null = resume from saved position).
|
||||
const [activeVideo, setActiveVideo] = useState<{ video: Video; startAt: number | null } | null>(
|
||||
null
|
||||
);
|
||||
const qc = useQueryClient();
|
||||
|
||||
const openVideo = useCallback(
|
||||
(video: Video, startAt: number | null = null) => setActiveVideo({ video, startAt }),
|
||||
[]
|
||||
);
|
||||
|
||||
const query = useInfiniteQuery({
|
||||
queryKey: ["feed", filters],
|
||||
queryFn: ({ pageParam }) => api.feed(filters, pageParam as number, PAGE),
|
||||
|
|
@ -166,7 +177,7 @@ export default function Feed({
|
|||
view="grid"
|
||||
onState={onState}
|
||||
onChannelFilter={onChannelFilter}
|
||||
onOpen={setActiveVideo}
|
||||
onOpen={openVideo}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -179,14 +190,15 @@ export default function Feed({
|
|||
view="list"
|
||||
onState={onState}
|
||||
onChannelFilter={onChannelFilter}
|
||||
onOpen={setActiveVideo}
|
||||
onOpen={openVideo}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{activeVideo && (
|
||||
<PlayerModal
|
||||
video={activeVideo}
|
||||
video={activeVideo.video}
|
||||
startAt={activeVideo.startAt}
|
||||
onClose={() => setActiveVideo(null)}
|
||||
onState={onState}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue