v3: comments say what the code can't — the documentation pass wanted JSDoc on every export and every field of an exported interface, which filled the code with restatements ("The dump.", "Whether it is loading.", "The props of X.", "Renders the X.") and module docs that told a reader nothing ("The hot and new feeds, and the props every main feed of the index takes."); some were wrong as well as vague. Every comment in api/, shared/ and src/ is rewritten under one rule, now stated in the README's "Comments" section: every module and export still has a JSDoc, and it gives context (who uses it and where), usage (what a caller must know: a precondition, what it leaves to the caller, a side effect) or its place in the chain (where its input comes from, where its output goes); a field, parameter, private function or local gets one only when it adds to its parent's, and context about a whole type goes on the type; a type's doc is phrased from the value's side ("Returned to Y by X when Z", "Passed to Y by X"); full sentences, as short as they can be, no label-colon-list; // goes wherever the code isn't obvious and says why, and the section banners and narration go. Every claim was checked against the code, which turned up some forty comments that were wrong: DumpCard's claimed it was only used by the hot and new feeds, the WebSocket welcome claimed to be a socket's first message (presence comes first), UpdateUserRequest said null removes a description (it keeps it), deletedDumpIds leaves out that it also holds dumps made private, THEME_COLOR claimed to mirror index.html's, and the like. The routers' JSDoc now says where they are mounted and what becomes of a thrown APIException. doc-lint.ts lets a field without a doc through; a module or export without one still fails. No code changes; 204 backend and 628 frontend tests pass, and doc:lint, deno check, deno lint and tsc are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-09-29 14:31:14 +00:00
parent 42e0356d21
commit 0d4e344d77
287 changed files with 5679 additions and 5135 deletions

View File

@@ -1,5 +1,6 @@
/**
* Keeping a list of dumps in step with the socket.
* Keeps a page's list of dumps in step with the dump and comment events that
* `WSContext` receives.
*
* @module
*/
@@ -8,30 +9,30 @@ import { useEffect, useLayoutEffect, useRef } from "react";
import type { Dump } from "../../../shared/dump.ts";
import { useWS } from "../ws/useWS.ts";
/** How {@link useDumpListSync} treats the list. */
/**
* Passed to {@link useDumpListSync} by a profile's list of dumps. The feeds
* pass none.
*/
export interface DumpListSyncOptions {
/** Keep private dumps visible (caller is the owner). */
/** Keeps an edited dump that is now private, for its owner's own list. */
isOwner?: boolean;
/**
* Only re-insert dumps created by this user when they become visible again.
* Leave undefined to never re-insert (caller handles it, or no re-insertion needed).
* Prepends an edited dump missing from the list when `ownerId` posted it.
* Without it, a missing dump is never added.
*/
ownerId?: string;
/**
* When true, don't re-add a dump that isn't in the list (idx === -1).
* Use this when the caller handles re-insertion itself (e.g. with a position map).
* Never adds a missing dump, whatever `ownerId`. A list that restores
* dumps at their old position, with `usePositionAwareSync`, sets it.
*/
skipReinsert?: boolean;
}
/**
* Keeps the dump list set through `setDumps` in sync with real-time WS events.
*
* - a dump deleted → filtered out
* - a dump updated → updated in place, or removed if now private
* - an updated dump not in the list → prepended if `ownerId` matches and it is
* visible
* - a comment created or deleted → the dump's `commentCount` adjusted
* Applies the socket's dump and comment events to the list that `setDumps`
* updates. A deleted dump is removed, an edited one is replaced in place, and
* a new or deleted comment moves its dump's `commentCount`. It doesn't add new
* dumps: the feeds merge `WSContext`'s `recentDumps` themselves.
*/
export function useDumpListSync(
setDumps: (fn: (prev: Dump[]) => Dump[]) => void,
@@ -39,7 +40,7 @@ export function useDumpListSync(
): void {
const { deletedDumpIds, lastDumpEvent, lastCommentEvent } = useWS();
// Keep refs up-to-date so closures in effects are never stale.
// Refs, so that the effects run on new events only, not on new callbacks.
const setDumpsRef = useRef(setDumps);
const optionsRef = useRef(options);
useLayoutEffect(() => {
@@ -63,7 +64,6 @@ export function useDumpListSync(
const idx = prev.findIndex((d) => d.id === dump.id);
if (idx !== -1) {
// Remove if it became private and the viewer can't see private dumps.
if (dump.isPrivate && !isOwner) {
return prev.filter((d) => d.id !== dump.id);
}
@@ -72,7 +72,6 @@ export function useDumpListSync(
return next;
}
// Dump not in list: only re-insert when ownerId is set and matches.
if (skipReinsert || !ownerId) return prev;
if (dump.userId !== ownerId) return prev;
if (dump.isPrivate && !isOwner) return prev;
@@ -80,8 +79,8 @@ export function useDumpListSync(
});
}, [lastDumpEvent]);
// commentCount counts non-deleted comments, so created → +1, deleted → −1.
// Edits don't change the count. Events only fire for public dumps.
// The server sends comment events for public dumps only, so a private
// dump's count stays as fetched.
useEffect(() => {
if (!lastCommentEvent) return;
const { type, dumpId } = lastCommentEvent;