Scrapboard.org client
1import { create } from "zustand";
2import { persist } from "zustand/middleware";
3import { InterpretedLabelValueDefinition, ModerationPrefs } from "@atproto/api";
4
5interface ModerationOptsData {
6 moderationPrefs: ModerationPrefs | undefined;
7 labelDefs: Record<string, InterpretedLabelValueDefinition[]> | undefined;
8 lastFetched: number | null;
9 isLoading: boolean;
10 error: string | null;
11}
12
13interface ModerationOptsState extends ModerationOptsData {
14 setModerationOpts: (
15 moderationPrefs: ModerationPrefs,
16 labelDefs: Record<string, InterpretedLabelValueDefinition[]>
17 ) => void;
18 setLoading: (isLoading: boolean) => void;
19 setError: (error: string | null) => void;
20 isStale: () => boolean;
21 shouldRefetch: () => boolean;
22 clear: () => void;
23}
24
25const STALE_TIME = 15 * 60 * 1000; // 15 minutes in milliseconds
26const CACHE_TIME = 30 * 60 * 1000; // 30 minutes in milliseconds
27
28export const useModerationOptsStore = create<ModerationOptsState>()(
29 persist(
30 (set, get) => ({
31 moderationPrefs: undefined,
32 labelDefs: undefined,
33 lastFetched: null,
34 isLoading: false,
35 error: null,
36
37 setModerationOpts: (moderationPrefs, labelDefs) => {
38 set({
39 moderationPrefs,
40 labelDefs,
41 lastFetched: Date.now(),
42 error: null,
43 });
44 },
45
46 setLoading: (isLoading) => set({ isLoading }),
47
48 setError: (error) => set({ error, isLoading: false }),
49
50 isStale: () => {
51 const { lastFetched } = get();
52 if (!lastFetched) return true;
53 return Date.now() - lastFetched > STALE_TIME;
54 },
55
56 shouldRefetch: () => {
57 const { lastFetched, isLoading } = get();
58 if (isLoading) return false;
59 if (!lastFetched) return true;
60 return Date.now() - lastFetched > CACHE_TIME;
61 },
62
63 clear: () =>
64 set({
65 moderationPrefs: undefined,
66 labelDefs: undefined,
67 lastFetched: null,
68 error: null,
69 }),
70 }),
71 {
72 name: "moderation-opts-storage",
73 partialize: (state) => ({
74 moderationPrefs: state.moderationPrefs,
75 labelDefs: state.labelDefs,
76 lastFetched: state.lastFetched,
77 }),
78 // Add storage configuration to handle complex objects
79 storage: {
80 getItem: (name) => {
81 const str = localStorage.getItem(name);
82 if (!str) return null;
83 try {
84 const parsed = JSON.parse(str);
85 return parsed;
86 } catch (error) {
87 console.error(
88 "Failed to parse moderation opts from localStorage:",
89 error
90 );
91 return null;
92 }
93 },
94 setItem: (name, value) => {
95 try {
96 localStorage.setItem(name, JSON.stringify(value));
97 } catch (error) {
98 console.error(
99 "Failed to serialize moderation opts to localStorage:",
100 error
101 );
102 }
103 },
104 removeItem: (name) => localStorage.removeItem(name),
105 },
106 }
107 )
108);
109
110// Utility function to clear moderation options cache (useful for logout)
111export const clearModerationOptsCache = () => {
112 useModerationOptsStore.getState().clear();
113};