File size: 2,222 Bytes
5351cc8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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 };
  }
}