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:
@@ -103,3 +103,46 @@ export async function readFirstPage<T>(
|
||||
export function withFirst<T extends { id: string }>(items: T[], item: T): T[] {
|
||||
return items.some((i) => i.id === item.id) ? items : [item, ...items];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `items`, newest first as the API pages them, with `item` where its
|
||||
* `createdAt` puts it: before the first item no newer than it. An item older
|
||||
* than every one loaded is left to a later page, and `items` returned as they
|
||||
* are, as they are when `item` is already there. The list sync hooks put a
|
||||
* new or newly visible dump or playlist in a list with it.
|
||||
*/
|
||||
export function withByDate<T extends { id: string; createdAt: Date }>(
|
||||
items: T[],
|
||||
item: T,
|
||||
): T[] {
|
||||
if (items.some((i) => i.id === item.id)) return items;
|
||||
if (items.length === 0) return [item];
|
||||
const time = item.createdAt.getTime();
|
||||
const idx = items.findIndex((i) => i.createdAt.getTime() <= time);
|
||||
if (idx === -1) return items;
|
||||
return [...items.slice(0, idx), item, ...items.slice(idx)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches pages 1 to `pages` of a list again, and resolves to their items,
|
||||
* each once, and whether more follow. An item added between two pages'
|
||||
* fetches shifts the later page by one, which would list an item twice. A
|
||||
* live list calls it after a reconnection, through `usePagedList`'s `resync`
|
||||
* or, for the search's dumps, directly; it rejects when a page fails.
|
||||
*/
|
||||
export async function refetchPages<T extends { id: string }>(
|
||||
pages: number,
|
||||
fetchPage: (page: number) => Promise<PaginatedData<T>>,
|
||||
): Promise<PaginatedData<T>> {
|
||||
const results = await Promise.all(
|
||||
Array.from({ length: pages }, (_, i) => fetchPage(i + 1)),
|
||||
);
|
||||
const byId = new Map<string, T>();
|
||||
for (const item of results.flatMap((r) => r.items)) {
|
||||
if (!byId.has(item.id)) byId.set(item.id, item);
|
||||
}
|
||||
return {
|
||||
items: [...byId.values()],
|
||||
hasMore: results[results.length - 1].hasMore,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user