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:
@@ -9,11 +9,11 @@ import { useEffect, useLayoutEffect, useRef } from "react";
|
||||
import type { Dump } from "../../../shared/dump.ts";
|
||||
import { useWS, useWSEvent } from "../ws/useWS.ts";
|
||||
import { insertAt, usePositionMemory } from "../feed/usePositionMemory.ts";
|
||||
import { withByDate } from "../../utils/feed/pagedList.ts";
|
||||
|
||||
/**
|
||||
* Passed to {@link useDumpListSync} by the lists that need more than edits and
|
||||
* deletions. The index's feed adds no dump through it, since it merges
|
||||
* `WSContext`'s `recentDumps` itself.
|
||||
* deletions.
|
||||
*/
|
||||
export interface DumpListSyncOptions {
|
||||
/**
|
||||
@@ -32,23 +32,11 @@ export interface DumpListSyncOptions {
|
||||
* Also adds a dump `belongs` accepts that the list never held, where the
|
||||
* newest-first order puts it among the dumps loaded: a new one first. One
|
||||
* older than all of them is left to the next pages. For the lists sorted by
|
||||
* `createdAt`, as a profile's dumps.
|
||||
* `createdAt`: the index's feed, a profile's dumps, the followed users'.
|
||||
*/
|
||||
addNew?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `list` with `dump` before the first dump posted earlier, or as is
|
||||
* when none was: the dump belongs to a later page.
|
||||
*/
|
||||
function insertByDate(list: Dump[], dump: Dump): Dump[] {
|
||||
if (list.length === 0) return [dump];
|
||||
const idx = list.findIndex((d) =>
|
||||
d.createdAt.getTime() < dump.createdAt.getTime()
|
||||
);
|
||||
return idx === -1 ? list : insertAt(list, dump, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the socket's dump, comment and user events to `items`, the loaded
|
||||
* list, which `setDumps` updates. A deleted dump is removed, an edited one is
|
||||
@@ -96,7 +84,7 @@ export function useDumpListSync(
|
||||
}
|
||||
if (!accepted) return prev;
|
||||
if (oldIdx !== undefined) return insertAt(prev, dump, oldIdx);
|
||||
return addNew ? insertByDate(prev, dump) : prev;
|
||||
return addNew ? withByDate(prev, dump) : prev;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
firstPage,
|
||||
mapItems,
|
||||
type PagedList,
|
||||
refetchPages,
|
||||
restoredList,
|
||||
} from "../../utils/feed/pagedList.ts";
|
||||
import type { FeedCacheEntry } from "./useFeedCache.ts";
|
||||
@@ -114,22 +115,8 @@ export function usePagedList<T extends { id: string }>(
|
||||
const resync = useCallback(() => {
|
||||
const { list, fetchPage } = latestRef.current;
|
||||
if (!list) return;
|
||||
const pages = Array.from({ length: list.page }, (_, i) => i + 1);
|
||||
Promise.all(pages.map((page) => fetchPage(page)))
|
||||
.then((results) => {
|
||||
// An item added between two pages' fetches shifts the later page by
|
||||
// one, which would list an item twice.
|
||||
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);
|
||||
}
|
||||
const items = [...byId.values()];
|
||||
setData((l) => ({
|
||||
...l,
|
||||
items,
|
||||
hasMore: results[results.length - 1].hasMore,
|
||||
}));
|
||||
})
|
||||
refetchPages(list.page, (page) => fetchPage(page))
|
||||
.then(({ items, hasMore }) => setData((l) => ({ ...l, items, hasMore })))
|
||||
.catch(() => {});
|
||||
}, [setData]);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useEffect, useLayoutEffect, useRef } from "react";
|
||||
import type { Playlist } from "../../../shared/playlist.ts";
|
||||
import { useWS, useWSEvent } from "../ws/useWS.ts";
|
||||
import { insertAt, usePositionMemory } from "../feed/usePositionMemory.ts";
|
||||
import { withByDate } from "../../utils/feed/pagedList.ts";
|
||||
|
||||
/** Passed to {@link usePlaylistListSync} by each page, to fit its list. */
|
||||
export interface PlaylistListSyncOptions {
|
||||
@@ -29,12 +30,12 @@ export interface PlaylistListSyncOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the socket's playlist events to `items`, the loaded list, which
|
||||
* `setPlaylists` updates. A created playlist goes first, an updated one
|
||||
* changes in place or leaves when it turns private (unless `isOwner`), one
|
||||
* that left comes back where it was when it returns, a deleted one leaves,
|
||||
* and a playlist's `dumpCount` follows its dumps. A renamed user's playlists
|
||||
* take their new name.
|
||||
* Applies the socket's playlist events to `items`, the loaded list, newest
|
||||
* first, which `setPlaylists` updates. A created playlist goes where its date
|
||||
* puts it, first when new, an updated one changes in place or leaves when it
|
||||
* turns private (unless `isOwner`), one that left comes back where it was
|
||||
* when it returns, a deleted one leaves, and a playlist's `dumpCount` follows
|
||||
* its dumps. A renamed user's playlists take their new name.
|
||||
*/
|
||||
export function usePlaylistListSync(
|
||||
items: Playlist[],
|
||||
@@ -73,7 +74,7 @@ export function usePlaylistListSync(
|
||||
if (oldIdx !== undefined) return insertAt(prev, playlist, oldIdx);
|
||||
// An update never adds a playlist the list doesn't hold.
|
||||
if (ev.type === "updated" || noNewEntries) return prev;
|
||||
return [playlist, ...prev];
|
||||
return withByDate(prev, playlist);
|
||||
}
|
||||
|
||||
if (ev.type === "deleted") {
|
||||
|
||||
Reference in New Issue
Block a user