an appview-less Bluesky client using Constellation and PDS Queries reddwarf.app
frontend spa bluesky reddwarf microcosm
1import { atom, createStore, useAtomValue } from "jotai"; 2import { atomWithStorage } from "jotai/utils"; 3import { useEffect } from "react"; 4 5export const store = createStore(); 6 7export const quickAuthAtom = atomWithStorage<string | null>( 8 "quickAuth", 9 null 10); 11 12export const selectedFeedUriAtom = atomWithStorage<string | null>( 13 "selectedFeedUri", 14 null 15); 16 17//export const feedScrollPositionsAtom = atom<Record<string, number>>({}); 18 19export const feedScrollPositionsAtom = atomWithStorage<Record<string, number>>( 20 "feedscrollpositions", 21 {} 22); 23 24type TabRouteScrollState = { 25 activeTab: string; 26 scrollPositions: Record<string, number>; 27}; 28export const notificationsScrollAtom = atom<TabRouteScrollState>({ 29 activeTab: "mentions", 30 scrollPositions: {}, 31}); 32 33export const reusableTabRouteScrollAtom = atom<Record<string, TabRouteScrollState | undefined> | undefined>({}); 34 35export const likedPostsAtom = atomWithStorage<Record<string, string>>( 36 "likedPosts", 37 {} 38); 39 40export const defaultconstellationURL = "constellation.microcosm.blue"; 41export const constellationURLAtom = atomWithStorage<string>( 42 "constellationURL", 43 defaultconstellationURL 44); 45export const defaultslingshotURL = "slingshot.microcosm.blue"; 46export const slingshotURLAtom = atomWithStorage<string>( 47 "slingshotURL", 48 defaultslingshotURL 49); 50export const defaultImgCDN = "cdn.bsky.app"; 51export const imgCDNAtom = atomWithStorage<string>("imgcdnurl", defaultImgCDN); 52export const defaultVideoCDN = "video.bsky.app"; 53export const videoCDNAtom = atomWithStorage<string>( 54 "videocdnurl", 55 defaultVideoCDN 56); 57 58export const defaulthue = 28; 59export const hueAtom = atomWithStorage<number>("hue", defaulthue); 60 61export const isAtTopAtom = atom<boolean>(true); 62 63type ComposerState = 64 | { kind: "closed" } 65 | { kind: "root" } 66 | { kind: "reply"; parent: string } 67 | { kind: "quote"; subject: string }; 68export const composerAtom = atom<ComposerState>({ kind: "closed" }); 69 70//export const agentAtom = atom<Agent | null>(null); 71//export const authedAtom = atom<boolean>(false); 72 73export function useAtomCssVar(atom: typeof hueAtom, cssVar: string) { 74 const value = useAtomValue(atom); 75 76 useEffect(() => { 77 document.documentElement.style.setProperty(cssVar, value.toString()); 78 }, [value, cssVar]); 79 80 useEffect(() => { 81 document.documentElement.style.setProperty(cssVar, value.toString()); 82 }, []); 83} 84 85hueAtom.onMount = (setAtom) => { 86 const stored = localStorage.getItem("hue"); 87 if (stored != null) setAtom(Number(stored)); 88}; 89// export function initAtomToCssVar(atom: typeof hueAtom, cssVar: string) { 90// const initial = store.get(atom); 91// console.log("atom get ", initial); 92// document.documentElement.style.setProperty(cssVar, initial.toString()); 93// }