Scrapboard.org client
at main 113 lines 3.2 kB view raw
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};