// Browser-side comment calls. Each throws CommentError naming the action. import { appendComment, type CommentItem } from "./comment-model"; export type CommentAction = "load" | "post"; export class CommentError extends Error { constructor(public readonly action: CommentAction, public readonly signedOut: boolean) { super(signedOut ? "sign_in" : `comment_${action}_failed`); } } export function commentErrorText(action: CommentAction): string { return action === "load" ? "Could not load comments." : "Could not post comment."; } async function parse(res: Response, action: CommentAction): Promise> { if (res.status === 401) throw new CommentError(action, true); if (!res.ok) throw new CommentError(action, false); try { return (await res.json()) as Record; } catch { throw new CommentError(action, false); } } export async function loadComments(postId: string, fetchImpl: typeof fetch = fetch): Promise { let res: Response; try { res = await fetchImpl(`/api/posts/${encodeURIComponent(postId)}/comments`, { credentials: "same-origin" }); } catch { throw new CommentError("load", false); } const b = await parse(res, "load"); return Array.isArray(b.comments) ? (b.comments as CommentItem[]) : []; } /** Post a comment and return the list as it should now read. Throws and returns nothing on failure. */ export async function postComment( postId: string, text: string, current: CommentItem[], fetchImpl: typeof fetch = fetch, ): Promise { const content = text.trim(); if (!content) return current; let res: Response; try { res = await fetchImpl(`/api/posts/${encodeURIComponent(postId)}/comments`, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content }), }); } catch { throw new CommentError("post", false); } const b = await parse(res, "post"); const made = b.comment as CommentItem | null | undefined; if (made && typeof made === "object") return appendComment(current, made); // The feed did not return the new comment; read the list back. try { return await loadComments(postId, fetchImpl); } catch { return appendComment(current, { key: `local#${current.length}`, id: null, authorName: "You", createdAt: new Date().toISOString(), removed: false, content, }); } }