learn and share notes on atproto (wip) 馃 malfestio.stormlightlabs.org/
readability solid axum atproto srs
at main 99 lines 2.9 kB view raw
1import { createRoot, createSignal } from "solid-js"; 2import { api } from "./api"; 3import type { Persona, User, UserPreferences } from "./model"; 4 5export type AuthState = { 6 user: User | null; 7 accessJwt: string | null; 8 refreshJwt: string | null; 9 isAuthenticated: boolean; 10}; 11 12function createAuthStore() { 13 const [user, setUser] = createSignal<User | null>( 14 localStorage.getItem("did") 15 ? { did: localStorage.getItem("did")!, handle: localStorage.getItem("handle") || "" } 16 : null, 17 ); 18 const [accessJwt, setAccessJwt] = createSignal(localStorage.getItem("accessJwt")); 19 const [_, setRefreshJwt] = createSignal(localStorage.getItem("refreshJwt")); 20 21 const login = (data: { accessJwt: string; refreshJwt: string; did: string; handle: string }) => { 22 setAccessJwt(data.accessJwt); 23 setRefreshJwt(data.refreshJwt); 24 setUser({ did: data.did, handle: data.handle }); 25 26 localStorage.setItem("accessJwt", data.accessJwt); 27 localStorage.setItem("refreshJwt", data.refreshJwt); 28 localStorage.setItem("did", data.did); 29 localStorage.setItem("handle", data.handle); 30 }; 31 32 const logout = () => { 33 setUser(null); 34 setAccessJwt(null); 35 setRefreshJwt(null); 36 localStorage.clear(); 37 }; 38 39 const [loading, setLoading] = createSignal(true); 40 41 const init = async () => { 42 await new Promise((resolve) => setTimeout(resolve, 1500)); 43 setLoading(false); 44 }; 45 46 init(); 47 48 return { user, accessJwt, isAuthenticated: () => !!accessJwt(), login, logout, loading }; 49} 50 51export const authStore = createRoot(createAuthStore); 52 53function createPrefStore() { 54 const [prefs, setPrefs] = createSignal<UserPreferences | null>(null); 55 const [loading, setLoading] = createSignal(false); 56 57 const fetchPrefs = async () => { 58 if (!authStore.isAuthenticated()) return; 59 setLoading(true); 60 try { 61 const res = await api.getPreferences(); 62 if (res.ok) { 63 setPrefs(await res.json()); 64 } 65 } catch (e) { 66 console.error("Failed to fetch preferences:", e); 67 } finally { 68 setLoading(false); 69 } 70 }; 71 72 const updatePreferences = async ( 73 updates: { 74 persona?: Persona; 75 complete_onboarding?: boolean; 76 density_mode?: "compact" | "comfortable" | "spacious"; 77 }, 78 ) => { 79 try { 80 const res = await api.updatePreferences(updates); 81 if (res.ok) { 82 setPrefs(await res.json()); 83 } 84 } catch (e) { 85 console.error("Failed to update preferences:", e); 86 } 87 }; 88 89 const needsOnboarding = () => { 90 const preferences = prefs(); 91 return preferences !== null && preferences.onboarding_completed_at === null; 92 }; 93 94 const persona = () => prefs()?.persona ?? null; 95 const densityMode = () => prefs()?.density_mode ?? "comfortable"; 96 return { prefs, loading, fetchPrefs, updatePreferences, needsOnboarding, persona, densityMode }; 97} 98 99export const prefStore = createRoot(createPrefStore);