import { type mastodon } from "masto"; import { Accessor, createEffect, createResource } from "solid-js"; import { createStore } from "solid-js/store"; type TimelineFetchTips = { direction?: "new" | "old"; }; type Timeline = { list(params: { readonly limit?: number; }): mastodon.Paginator; }; export function useTimeline( timeline: Accessor, cfg?: { /** * Use full refresh mode. This mode ignores paging, it will refetch the specified number * of toots at every refetch(). */ fullRefresh?: number; }, ) { let otl: Timeline | undefined; let npager: mastodon.Paginator | undefined; let opager: mastodon.Paginator | undefined; const [snapshot, { refetch }] = createResource< { records: mastodon.v1.Status[]; direction: "new" | "old" | "items"; tlChanged: boolean; }, [Timeline], TimelineFetchTips | undefined >( () => [timeline()] as const, async ([tl], info) => { let tlChanged = false; if (otl !== tl) { console.debug("timeline reset"); npager = opager = undefined; otl = tl; tlChanged = true; } const fullRefresh = cfg?.fullRefresh; if (typeof fullRefresh !== "undefined") { const records = await tl .list({ limit: fullRefresh, }) .next(); return { direction: "items", records: records.value ?? [], end: records.done, tlChanged, }; } const direction = typeof info.refetching !== "boolean" ? (info.refetching?.direction ?? "old") : "old"; if (direction === "old") { if (!opager) { opager = tl.list({}).setDirection("next"); } const next = await opager.next(); return { direction, records: next.value ?? [], end: next.done, tlChanged, }; } else { if (!npager) { npager = tl.list({}).setDirection("prev"); } const next = await npager.next(); const page = next.value ?? []; return { direction, records: page, end: next.done, tlChanged }; } }, ); const [store, setStore] = createStore([] as mastodon.v1.Status[]); createEffect(() => { const shot = snapshot(); if (!shot) return; const { direction, records, tlChanged } = shot; if (tlChanged) { setStore(() => []); } if (direction === "new") { setStore((x) => [...records, ...x]); } else if (direction === "old") { setStore((x) => [...x, ...records]); } else if (direction === "items") { setStore(() => records); } }); return [ store, snapshot, { refetch, mutate: setStore, }, ] as const; }