replies timeline only, appview-less bluesky client
at main 2.9 kB view raw
1import { writable } from 'svelte/store'; 2import { defaultTheme, type Theme } from './theme'; 3 4export type ApiEndpoints = Record<string, string> & { 5 slingshot: string; 6 spacedust: string; 7 constellation: string; 8 jetstream: string; 9}; 10export type Settings = { 11 endpoints: ApiEndpoints; 12 theme: Theme; 13 socialAppUrl: string; 14}; 15 16export const defaultSettings: Settings = { 17 endpoints: { 18 slingshot: 'https://slingshot.microcosm.blue', 19 spacedust: 'https://spacedust.microcosm.blue', 20 constellation: 'https://constellation.microcosm.blue', 21 jetstream: 'wss://jetstream2.fr.hose.cam' 22 }, 23 theme: defaultTheme, 24 socialAppUrl: 'https://bsky.app' 25}; 26 27const createSettingsStore = () => { 28 // Prevent SSR crash if localStorage is missing 29 const stored = typeof localStorage !== 'undefined' ? localStorage.getItem('settings') : null; 30 31 const initial: Partial<Settings> = stored ? JSON.parse(stored) : defaultSettings; 32 initial.endpoints = { ...defaultSettings.endpoints, ...initial.endpoints }; 33 initial.theme = { ...defaultSettings.theme, ...initial.theme }; 34 initial.socialAppUrl = initial.socialAppUrl ?? defaultSettings.socialAppUrl; 35 36 const { subscribe, set, update } = writable<Settings>(initial as Settings); 37 38 subscribe((settings) => { 39 if (typeof document === 'undefined') return; 40 const theme = settings.theme; 41 document.documentElement.style.setProperty('--nucleus-bg', theme.bg); 42 document.documentElement.style.setProperty('--nucleus-fg', theme.fg); 43 document.documentElement.style.setProperty('--nucleus-accent', theme.accent); 44 document.documentElement.style.setProperty('--nucleus-accent2', theme.accent2); 45 46 const oldMeta = document.querySelector('meta[name="theme-color"]'); 47 if (oldMeta) oldMeta.remove(); 48 49 const metaThemeColor = document.createElement('meta'); 50 metaThemeColor.setAttribute('name', 'theme-color'); 51 metaThemeColor.setAttribute('content', theme.bg); 52 document.head.appendChild(metaThemeColor); 53 }); 54 55 return { 56 subscribe, 57 set: (value: Settings) => { 58 if (typeof localStorage !== 'undefined') 59 localStorage.setItem('settings', JSON.stringify(value)); 60 set(value); 61 }, 62 update: (fn: (value: Settings) => Settings) => { 63 update((value) => { 64 const newValue = fn(value); 65 if (typeof localStorage !== 'undefined') 66 localStorage.setItem('settings', JSON.stringify(newValue)); 67 return newValue; 68 }); 69 }, 70 reset: () => { 71 if (typeof localStorage !== 'undefined') 72 localStorage.setItem('settings', JSON.stringify(defaultSettings)); 73 set(defaultSettings); 74 } 75 }; 76}; 77 78export const settings = createSettingsStore(); 79 80export const needsReload = (current: Settings, other: Settings): boolean => { 81 return ( 82 current.endpoints.slingshot !== other.endpoints.slingshot || 83 current.endpoints.spacedust !== other.endpoints.spacedust || 84 current.endpoints.constellation !== other.endpoints.constellation || 85 current.endpoints.jetstream !== other.endpoints.jetstream 86 ); 87};