import type { QueryClient } from "@tanstack/react-query"; import { api, type Playlist, type VideoStatus } from "../../lib/api"; import { markVideo } from "../../lib/progressCache"; // A video's user state, its Watch-later mark and its playlist items, written to the server and — // once it has the change — into the lists that are never refetched (`markVideo`: every related // list, every live search). These are the ONE way to make those writes, and // `videoWrites.contract.test.ts` holds it: four writes that called the api directly (the // Notification Center's undo, the feed's clear, the feed's and Home's Watch-later toggles) left a // related list hiding a video the user had un-hidden, or showing it unsaved after a save. A // playlist item is one of them because the built-in Watch later list IS the mark (`saved`): ticked // in Add to playlist or taken out on the Playlists page, it left the bookmark as it was. Each // returns the server's answer, so a caller keeps its own optimistic layer, notices and rollback. export function writeVideoState(qc: QueryClient, id: string, status: VideoStatus) { return api.setState(id, status).then((r) => { markVideo(qc, id, { status }); return r; }); } /** Forget where the user left it: the resume point goes, the status stays. */ export function clearVideoProgress(qc: QueryClient, id: string) { return api.clearProgress(id).then((r) => { markVideo(qc, id, { position_seconds: 0 }); return r; }); } export function writeWatchLater(qc: QueryClient, id: string, saved: boolean) { return (saved ? api.watchLaterAdd(id) : api.watchLaterRemove(id)).then((r) => { markVideo(qc, id, { saved }); return r; }); } /** The list a playlist item write goes to: on the built-in Watch later list (`kind`) the item IS * the video's Watch-later mark. A kind the caller cannot tell (undefined) marks nothing. */ type ListRef = { id: number; kind: Playlist["kind"] | undefined }; const markIfWatchLater = (qc: QueryClient, list: ListRef, id: string, saved: boolean) => { if (list.kind === "watch_later") markVideo(qc, id, { saved }); }; export function addPlaylistItem(qc: QueryClient, list: ListRef, id: string) { return api.addToPlaylist(list.id, id).then((r) => { markIfWatchLater(qc, list, id, true); return r; }); } export function removePlaylistItem(qc: QueryClient, list: ListRef, id: string) { return api.removeFromPlaylist(list.id, id).then((r) => { markIfWatchLater(qc, list, id, false); return r; }); } /** Take videos out of a playlist, in one request. Marks what the server says went. */ export function removePlaylistItems(qc: QueryClient, list: ListRef, ids: string[]) { return api.removeManyFromPlaylist(list.id, ids).then((r) => { for (const id of r.removed) markIfWatchLater(qc, list, id, false); return r; }); } /** Put removed videos back at their places, in one request. */ export function restorePlaylistItems( qc: QueryClient, list: ListRef, ids: string[], positions: number[], ) { return api.restoreToPlaylist(list.id, ids, positions).then((r) => { for (const id of ids) markIfWatchLater(qc, list, id, true); return r; }); } /** Move videos from one playlist to another, in one request (see `api.moveManyToPlaylist`). Marks * what the server says it moved: a video a concurrent edit already took out is in neither answer. */ export function movePlaylistItems(qc: QueryClient, from: ListRef, ids: string[], to: ListRef) { return api.moveManyToPlaylist(from.id, ids, to.id).then((r) => { for (const id of [...r.moved, ...r.already_there]) { markIfWatchLater(qc, from, id, false); markIfWatchLater(qc, to, id, true); } return r; }); } /** Take a move back: every video returns to `from`, and only the ones that landed in `to` leave it; * it held the others before the move. */ export function undoPlaylistMove( qc: QueryClient, from: ListRef, to: ListRef, back: { moved: string[]; already_there: string[]; positions: number[] }, ) { return api .undoPlaylistMove(from.id, to.id, back.moved, back.already_there, back.positions) .then((r) => { for (const id of back.moved) markIfWatchLater(qc, to, id, false); for (const id of [...back.moved, ...back.already_there]) markIfWatchLater(qc, from, id, true); return r; }); }