Scrapboard.org client
1import { create } from "zustand";
2import { persist } from "zustand/middleware";
3import * as z from "zod";
4
5export const Board = z.looseObject({
6 name: z.string(),
7 description: z.string(),
8});
9
10export type Board = z.infer<typeof Board>;
11
12type BoardsState = {
13 boards: Record<string, Record<string, Board>>;
14 isLoading: boolean;
15 setLoading: (value: boolean) => void;
16 setBoard: (did: string, rkey: string, board: Board) => void;
17 removeBoard: (did: string, rkey: string) => void;
18 getBoards: (did: string) => Record<string, Board> | undefined;
19 getBoardsAsEntries: (did: string) => [string, Board][] | undefined;
20 getAllBoards: () => Record<string, Record<string, Board>>;
21 clearBoards: (did?: string) => void;
22};
23
24export const useBoardsStore = create<BoardsState>()(
25 persist(
26 (set, get) => ({
27 boards: {},
28 isLoading: true,
29
30 setLoading: (value) => set(() => ({ isLoading: value })),
31
32 setBoard: (did, rkey, board) =>
33 set((state) => ({
34 boards: {
35 ...state.boards,
36 [did]: {
37 ...state.boards[did],
38 [rkey]: board,
39 },
40 },
41 })),
42
43 removeBoard: (did, rkey) =>
44 set((state) => {
45 const userBoards = state.boards[did];
46 if (!userBoards || !userBoards[rkey]) return state;
47
48 const { [rkey]: removed, ...rest } = userBoards;
49 return {
50 boards: {
51 ...state.boards,
52 [did]: rest,
53 },
54 };
55 }),
56
57 getBoards: (did) => get().boards[did],
58
59 getBoardsAsEntries: (did) => {
60 const boards = get().boards[did];
61 return boards ? Object.entries(boards) : undefined;
62 },
63
64 getAllBoards: () => get().boards,
65
66 clearBoards: (did) =>
67 set((state) => {
68 if (did) {
69 const { [did]: removed, ...rest } = state.boards;
70 return { boards: rest };
71 } else {
72 return { boards: {} };
73 }
74 }),
75 }),
76 {
77 name: "boards",
78 partialize: (state) => ({
79 boards: state.boards,
80 }),
81 version: 2,
82 // No need for custom storage anymore!
83 }
84 )
85);