fix: review round 1 — row press after a rename, related reset, dead items on removal, honest Prev/Next
- A press that begins while a field in the row is edited only ends the editing (rowOpen latches it at pointerdown); the card layout's row press is now tested. - A related video reset in the player forgets the session's stored position. - Removing the playing video passes over known-dead items and steps the way it went. - Prev/Next off only because of dead items stay focusable and say why (HU/EN). - The docked bar's scroller leaves room for focus rings. - A focus-opened About card is not closed by its own scroll into view. - EN "Clear watch progress" for the video reset, distinct from a page's "Reset view". - The signed-out title read goes to the page yt-dlp read; the retitle writes are conditional. - Tests: the cap of three errors, the bar's own Fullscreen, the group glyph, the backfill's reads.
This commit is contained in:
@@ -10,6 +10,27 @@ const v = (id: string): Video => ({ id }) as Video;
|
||||
const ids = (q: Video[]) => q.map((x) => x.id);
|
||||
|
||||
describe("afterRemoval", () => {
|
||||
it("passes over a follower known dead, on the way it went", () => {
|
||||
const q = [v("a"), v("b"), v("c"), v("d")];
|
||||
const next = afterRemoval(q, 1, "b", new Set(["c"]));
|
||||
expect(next).toMatchObject({ playingId: "d", way: 1 });
|
||||
});
|
||||
|
||||
it("goes back over the dead ones when nothing playable followed", () => {
|
||||
const q = [v("a"), v("b"), v("c")];
|
||||
expect(afterRemoval(q, 2, "c", new Set(["b"]))).toMatchObject({ playingId: "a", way: -1 });
|
||||
// The last one going, nothing dead: the one before it, and that is a step back.
|
||||
expect(afterRemoval(q, 2, "c")).toMatchObject({ playingId: "b", way: -1 });
|
||||
});
|
||||
|
||||
it("with none left that can play, takes the follower all the same", () => {
|
||||
const q = [v("a"), v("b"), v("c")];
|
||||
expect(afterRemoval(q, 0, "a", new Set(["b", "c"]))).toMatchObject({
|
||||
playingId: "b",
|
||||
way: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it("plays the item that followed the one you removed", () => {
|
||||
const q = [v("a"), v("b"), v("c")];
|
||||
const next = afterRemoval(q, 1, "b");
|
||||
|
||||
@@ -12,21 +12,30 @@ export interface AfterRemoval {
|
||||
queue: Video[];
|
||||
/** The item to play next. */
|
||||
playingId: string;
|
||||
/** Which way that item lies from the removed one: what it carries on along if it cannot play. */
|
||||
way: 1 | -1;
|
||||
}
|
||||
|
||||
/** `null` means nothing is left — the caller closes the player.
|
||||
*
|
||||
* The next item is the one that FOLLOWED the removed one (that is what "delete and keep going"
|
||||
* means when you are working down a list), clamped to the end when the last item goes. */
|
||||
* means when you are working down a list), the one before it when the last item goes. A video known
|
||||
* to be unplayable (`dead`, see `stepIndex`) is passed over, not loaded again: the nearest playable
|
||||
* one after, else before. None playable: the follower all the same, where the player shows why. */
|
||||
export function afterRemoval(
|
||||
queue: Video[] | undefined,
|
||||
index: number,
|
||||
victimId: string,
|
||||
dead: ReadonlySet<string> = new Set(),
|
||||
): AfterRemoval | null {
|
||||
const rest = (queue ?? []).filter((v) => v.id !== victimId);
|
||||
if (rest.length === 0) return null;
|
||||
const next = rest[Math.min(Math.max(index, 0), rest.length - 1)]!;
|
||||
return { queue: rest, playingId: next.id };
|
||||
// The removed item's slot: what followed it now stands there (past the end when it was the last).
|
||||
const at = Math.min(Math.max(index, 0), rest.length);
|
||||
const pick = (j: number, way: 1 | -1) => ({ queue: rest, playingId: rest[j]!.id, way });
|
||||
for (let j = at; j < rest.length; j++) if (!dead.has(rest[j]!.id)) return pick(j, 1);
|
||||
for (let j = at - 1; j >= 0; j--) if (!dead.has(rest[j]!.id)) return pick(j, -1);
|
||||
return at < rest.length ? pick(at, 1) : pick(rest.length - 1, -1);
|
||||
}
|
||||
|
||||
/** The nearest item from `index` going `dir` that is not known to be unplayable, or -1 when there is
|
||||
|
||||
Reference in New Issue
Block a user