38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
// Browser-side reader for the same-origin GET /api/feed route.
|
|
|
|
export interface FeedPage {
|
|
items: unknown[];
|
|
nextCursor: string | null;
|
|
}
|
|
|
|
export class FeedLoadError extends Error {
|
|
constructor(public readonly signedOut: boolean) {
|
|
super(signedOut ? "sign_in" : "feed_unavailable");
|
|
}
|
|
}
|
|
|
|
export async function fetchFeedPage(
|
|
cursor: string | null,
|
|
fetchImpl: typeof fetch = fetch,
|
|
): Promise<FeedPage> {
|
|
const qs = new URLSearchParams({ limit: "20" });
|
|
if (cursor) qs.set("cursor", cursor);
|
|
let res: Response;
|
|
try {
|
|
res = await fetchImpl(`/api/feed?${qs.toString()}`, { credentials: "same-origin" });
|
|
} catch {
|
|
throw new FeedLoadError(false);
|
|
}
|
|
if (res.status === 401) throw new FeedLoadError(true);
|
|
if (!res.ok) throw new FeedLoadError(false);
|
|
try {
|
|
const body = (await res.json()) as { items?: unknown; nextCursor?: unknown };
|
|
return {
|
|
items: Array.isArray(body.items) ? body.items : [],
|
|
nextCursor: typeof body.nextCursor === "string" && body.nextCursor ? body.nextCursor : null,
|
|
};
|
|
} catch {
|
|
throw new FeedLoadError(false);
|
|
}
|
|
}
|