v4: what comes into a live list goes where its date puts it — a dump edited into a category stayed out of the index's feed of it until a reload, and a dump or a playlist made public went on top of the others' lists as if just posted. The index's feed now takes a dump edited into its scope, and orders the dumps that arrive by date, as the server pages them; a created playlist goes by date in the profile's and /playlists' lists, a new one first as before. Both use withByDate (utils/feed/pagedList.ts), which leaves an item older than every one loaded to a later page. The search runs again after a reconnection, for every page of dumps loaded, with its users and playlists; it shares refetchPages with usePagedList's resync. injectDump's doc said a private dump is never broadcast, while its poster gets it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -264,6 +264,44 @@ describe("Index feeds", () => {
|
||||
expect(screen.queryByText("A song")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("a dump edited into the category comes in where its date puts it", async () => {
|
||||
const at = (minutes: number) => new Date(Date.now() - minutes * 60_000);
|
||||
const tagged = (id: string, title: string, minutes: number) =>
|
||||
makeDump({ id, title, categoryIds: ["c-music"], createdAt: at(minutes) });
|
||||
route(
|
||||
"GET /api/dumps",
|
||||
() => page([tagged("n", "Newer", 1), tagged("o", "Older", 30)]),
|
||||
);
|
||||
const { socket } = setup("/music/new");
|
||||
await screen.findByText("Older");
|
||||
socket.receive({
|
||||
type: "dump_updated",
|
||||
dump: wire(tagged("m", "Now music", 10)),
|
||||
});
|
||||
expect(titles()).toEqual(["Newer", "Now music", "Older"]);
|
||||
});
|
||||
|
||||
test("a dump made public long after it was posted goes by its date, not on top", async () => {
|
||||
const at = (minutes: number) => new Date(Date.now() - minutes * 60_000);
|
||||
route("GET /api/dumps", () =>
|
||||
page([
|
||||
makeDump({ id: "n", title: "Newer", createdAt: at(1) }),
|
||||
makeDump({ id: "o", title: "Older", createdAt: at(30) }),
|
||||
]));
|
||||
const { socket } = setup("/new");
|
||||
await screen.findByText("Older");
|
||||
// To everyone but its poster, it arrives as created.
|
||||
socket.receive({
|
||||
type: "dump_created",
|
||||
dump: wire(makeDump({ id: "m", title: "Unveiled", createdAt: at(10) })),
|
||||
});
|
||||
socket.receive({
|
||||
type: "dump_created",
|
||||
dump: wire(makeDump({ id: "a", title: "Ancient", createdAt: at(600) })),
|
||||
});
|
||||
expect(titles()).toEqual(["Newer", "Unveiled", "Older", "Ancient"]);
|
||||
});
|
||||
|
||||
test("an unknown category is a 404", async () => {
|
||||
route("GET /api/dumps", () => fail("nope", 404));
|
||||
setup("/nonsense/new");
|
||||
|
||||
@@ -238,4 +238,50 @@ describe("Search", () => {
|
||||
await user.click(screen.getByRole("button", { name: /Playlists/ }));
|
||||
expect(screen.queryByText("Cats")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("a reconnect runs the search again, for every page loaded", async () => {
|
||||
route(
|
||||
"GET /api/search",
|
||||
(req) =>
|
||||
req.url.searchParams.get("page") === "2"
|
||||
? results({ dumps: [makeDump({ title: "Second page" })] })
|
||||
: results({
|
||||
dumps: [makeDump({ title: "First page" })],
|
||||
hasMore: true,
|
||||
users: [makeUser({ username: "catlover" })],
|
||||
}),
|
||||
);
|
||||
open("/search?q=cat");
|
||||
const socket = FakeWebSocket.last;
|
||||
socket.welcome();
|
||||
await screen.findByText("First page");
|
||||
const sentinel = FakeIntersectionObserver.instances.findLast((o) =>
|
||||
o.elements.size > 0
|
||||
)!;
|
||||
act(() => sentinel.report(true));
|
||||
await screen.findByText("Second page");
|
||||
|
||||
// Gone while the socket was down: an edit, a deletion, a new user.
|
||||
route(
|
||||
"GET /api/search",
|
||||
(req) =>
|
||||
req.url.searchParams.get("page") === "2"
|
||||
? results({ dumps: [] })
|
||||
: results({
|
||||
dumps: [makeDump({ title: "First page, edited" })],
|
||||
hasMore: true,
|
||||
users: [
|
||||
makeUser({ username: "catlover" }),
|
||||
makeUser({ username: "cat2" }),
|
||||
],
|
||||
}),
|
||||
);
|
||||
socket.drop();
|
||||
await act(() => new Promise((r) => setTimeout(r, 600)));
|
||||
FakeWebSocket.last.welcome();
|
||||
expect(await screen.findByText("First page, edited")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Second page")).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Users (2)" }))
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user