fix(player): related marks survive, Restart restarts, and refused pages offer nothing to click
- Every state and Watch-later write goes through features/video/videoWrites, which marks the never-refetched lists (related lists, live searches) once the server has it; a contract test holds it. The Notification Center's undo, the feed's reset and the Watch-later toggles skipped it before. - Confirmed marks are also kept apart (qk.videoMarks) and laid over the related rows: a Show more page landing later rebuilt them from the pages it read when it started, and dropped a mark made meanwhile. - Restart on a related video starts at 0 (the session's stored position won before), and on the one already playing it seeks. - The watched toggle remembers each video's mark: Back showed the opened video's launch-time state after a related one was marked. - The help sheet leaves out the player's keys while a layer above it has them waiting. - After a refusal a retry cannot answer, neither Show more (related) nor Load more (live search) is offered; a failed live-search page keeps the results already read and says why under them. - The related cap message counts requests, as the cap does.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// The whole app source: a state write can be made from any page.
|
||||
const root = new URL("../../", import.meta.url);
|
||||
const sources = () =>
|
||||
readdirSync(root, { recursive: true, encoding: "utf8" })
|
||||
.map((f) => f.replace(/\\/g, "/"))
|
||||
.filter((f) => /\.tsx?$/.test(f) && !f.includes(".test."))
|
||||
.map((f) => ({ f, src: readFileSync(new URL(f, root), "utf8") }));
|
||||
|
||||
// The api's own definitions of these writes, and the one module that may call them.
|
||||
const ALLOWED = new Set([
|
||||
"lib/api/feed.ts",
|
||||
"lib/api/playlists.ts",
|
||||
"features/video/videoWrites.ts",
|
||||
]);
|
||||
|
||||
// A related list and a live search are never refetched, so a state or Watch-later write that does
|
||||
// not reach them (`markVideo`) leaves them showing the old mark for the session. videoWrites makes
|
||||
// those writes and the mark together; a direct api call skips the mark.
|
||||
describe("a video's state and Watch-later writes", () => {
|
||||
it("go through features/video/videoWrites, never the api directly", () => {
|
||||
const offenders = sources()
|
||||
.filter(({ f }) => !ALLOWED.has(f))
|
||||
// Only a call on the api object counts (React's own `setState` is not ours); the dot may sit
|
||||
// on the next line of a chain.
|
||||
.filter(({ src }) =>
|
||||
/\bapi\s*\.\s*(setState|clearState|watchLaterAdd|watchLaterRemove)\(/.test(src),
|
||||
)
|
||||
.map(({ f }) => f);
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user