import type { RecordComment } from "./recordCommentsApi"; export interface RecordCommentsState { pid: number; comments: RecordComment[]; draft: string; loading: boolean; posting: boolean; error: string; } export type RecordCommentsAction = | { type: "record"; pid: number } | { type: "loaded"; pid: number; comments: RecordComment[] } | { type: "load-failed"; pid: number; error: string } | { type: "draft"; pid: number; draft: string } | { type: "posting"; pid: number } | { type: "posted"; pid: number; comment: RecordComment } | { type: "post-failed"; pid: number; error: string } | { type: "deleted"; pid: number; commentId: string } | { type: "delete-failed"; pid: number; error: string }; export function initialRecordCommentsState(pid: number): RecordCommentsState { return { pid, comments: [], draft: "", loading: true, posting: false, error: "", }; } /** * Every async completion carries the pid it started against. A completion for * a record that is no longer rendered is ignored wholesale; it can never add, * remove, or error a different record's comment feed. */ export function recordCommentsReducer( state: RecordCommentsState, action: RecordCommentsAction ): RecordCommentsState { if (action.type === "record") return initialRecordCommentsState(action.pid); if (action.pid !== state.pid) return state; switch (action.type) { case "loaded": return { ...state, comments: action.comments, loading: false, error: "" }; case "load-failed": return { ...state, loading: false, error: action.error }; case "draft": return { ...state, draft: action.draft }; case "posting": return { ...state, posting: true, error: "" }; case "posted": return { ...state, comments: [...state.comments, action.comment], draft: "", posting: false, }; case "post-failed": return { ...state, posting: false, error: action.error }; case "deleted": return { ...state, comments: state.comments.filter((comment) => comment.id !== action.commentId), }; case "delete-failed": return { ...state, error: action.error }; } }