Files
siftlode/frontend/src/lib/useChannelNames.ts
T
peter e9c15baae0 fix(feed): a channel filter's chips name their channels from the first paint
After a reload the chips (filter panel and the row above the feed) read as
raw UC… ids until the subscription list landed. The names seen last time
are now kept per account (useChannelNames) and name the chips at once; the
list then replaces the copy, so a renamed or dropped channel heals. A
channel never named on this browser draws a placeholder bone while the
list loads; a failed list still falls back to the id.
2026-10-03 03:29:54 +02:00

45 lines
1.7 KiB
TypeScript

import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { qk } from "./queryKeys";
import { api, type ManagedChannel } from "./api";
import { LS, readAccount, writeAccount } from "./storage";
type Names = Record<string, string>;
const namesOf = (channels: ManagedChannel[]): Names =>
Object.fromEntries(channels.map((c) => [c.id, c.title ?? c.id]));
const readNames = (): Names => {
const raw = readAccount<unknown>(LS.channelNames, {});
return raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as Names) : {};
};
/**
* Channel id → name, for the chips that name a channel filter (the filter panel's picker and the chip
* row above the feed). The names come from the subscription list, which a reload has not fetched yet:
* for a moment the chips read as raw UC… ids (the owner, 2026-10-03). The names seen last time are
* kept per account and name the chips until the list lands; the list then wins and replaces the copy,
* so a renamed or dropped channel heals on the next load.
*
* `pending` is true while the list loads: a channel this browser has never named then has no name to
* show, and the chip draws a placeholder rather than its id. A failed list leaves the id (the chips
* stay told apart, and each ✕ drops a different one).
*/
export function useChannelNames(enabled = true) {
const query = useQuery({
queryKey: qk.channels(),
queryFn: api.channels,
enabled,
staleTime: 5 * 60_000,
});
const [remembered] = useState(readNames);
useEffect(() => {
if (query.data) writeAccount(LS.channelNames, namesOf(query.data));
}, [query.data]);
return {
query,
names: query.data ? namesOf(query.data) : remembered,
pending: enabled && query.isPending,
};
}