v3: added content slugs, fixed real-time updates in client, added @mentions across the app, added new file selector and drop zone

This commit is contained in:
khannurien
2026-03-22 16:06:26 +00:00
parent 39a0cc397e
commit 34e908d1bc
42 changed files with 2170 additions and 628 deletions

View File

@@ -0,0 +1,49 @@
import { useEffect, useLayoutEffect, useRef } from "react";
import type { Dump } from "../model.ts";
import { useWS } from "./useWS.ts";
/**
* Keeps a dump list in sync with real-time WS events:
* - deletedDumpIds: filters out dumps that were deleted or privatised.
* - lastDumpEvent: updates existing dumps in-place; optionally prepends
* new ones when `addFilter` returns true for them.
*
* @param setDumps Updater that patches the caller's dump array.
* @param addFilter Optional predicate: return true to prepend a dump that
* isn't already in the list (e.g. became public).
*/
export function useDumpListSync(
setDumps: (fn: (prev: Dump[]) => Dump[]) => void,
addFilter?: (dump: Dump) => boolean,
): void {
const { deletedDumpIds, lastDumpEvent } = useWS();
// Keep refs up-to-date so closures in effects are never stale.
const setDumpsRef = useRef(setDumps);
const addFilterRef = useRef(addFilter);
useLayoutEffect(() => {
setDumpsRef.current = setDumps;
addFilterRef.current = addFilter;
});
useEffect(() => {
if (deletedDumpIds.size === 0) return;
setDumpsRef.current((prev) => prev.filter((d) => !deletedDumpIds.has(d.id)));
}, [deletedDumpIds]);
useEffect(() => {
if (!lastDumpEvent) return;
setDumpsRef.current((prev) => {
const idx = prev.findIndex((d) => d.id === lastDumpEvent.id);
if (idx !== -1) {
const next = [...prev];
next[idx] = lastDumpEvent;
return next;
}
if (addFilterRef.current?.(lastDumpEvent)) {
return [lastDumpEvent, ...prev];
}
return prev;
});
}, [lastDumpEvent]);
}