import { useState, useEffect } from 'react' import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faSpinner } from '@fortawesome/free-solid-svg-icons' import { EventStoreProvider, AccountsProvider, Hooks } from 'applesauce-react' import { EventStore } from 'applesauce-core/dist/event-store/event-store.js' import { AccountManager } from 'applesauce-accounts' import { registerCommonAccountTypes } from 'applesauce-accounts/accounts' import { RelayPool } from 'applesauce-relay' import { createAddressLoader } from 'applesauce-loaders/loaders' import Bookmarks from './components/Bookmarks' import Toast from './components/Toast' import { useToast } from './hooks/useToast' import { RELAYS } from './config/relays' const DEFAULT_ARTICLE = import.meta.env.VITE_DEFAULT_ARTICLE_NADDR || 'naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew' // AppRoutes component that has access to hooks function AppRoutes({ relayPool, showToast }: { relayPool: RelayPool showToast: (message: string) => void }) { const accountManager = Hooks.useAccountManager() const handleLogout = () => { accountManager.setActive(undefined as never) localStorage.removeItem('active') showToast('Logged out successfully') } return ( } /> } /> } /> ) } function App() { const [eventStore, setEventStore] = useState(null) const [accountManager, setAccountManager] = useState(null) const [relayPool, setRelayPool] = useState(null) const { toastMessage, toastType, showToast, clearToast } = useToast() useEffect(() => { const initializeApp = async () => { // Initialize event store, account manager, and relay pool const store = new EventStore() const accounts = new AccountManager() // Register common account types (needed for deserialization) registerCommonAccountTypes(accounts) // Load persisted accounts from localStorage try { const json = JSON.parse(localStorage.getItem('accounts') || '[]') await accounts.fromJSON(json) console.log('Loaded', accounts.accounts.length, 'accounts from storage') // Load active account from storage const activeId = localStorage.getItem('active') if (activeId && accounts.getAccount(activeId)) { accounts.setActive(activeId) console.log('Restored active account:', activeId) } } catch (err) { console.error('Failed to load accounts from storage:', err) } // Subscribe to accounts changes and persist to localStorage const accountsSub = accounts.accounts$.subscribe(() => { localStorage.setItem('accounts', JSON.stringify(accounts.toJSON())) }) // Subscribe to active account changes and persist to localStorage const activeSub = accounts.active$.subscribe((account) => { if (account) { localStorage.setItem('active', account.id) } else { localStorage.removeItem('active') } }) const pool = new RelayPool() // Create a relay group for better event deduplication and management pool.group(RELAYS) console.log('Created relay group with', RELAYS.length, 'relays (including local)') console.log('Relay URLs:', RELAYS) // Attach address/replaceable loaders so ProfileModel can fetch profiles const addressLoader = createAddressLoader(pool, { eventStore: store, lookupRelays: RELAYS }) store.addressableLoader = addressLoader store.replaceableLoader = addressLoader setEventStore(store) setAccountManager(accounts) setRelayPool(pool) // Cleanup function return () => { accountsSub.unsubscribe() activeSub.unsubscribe() } } let cleanup: (() => void) | undefined initializeApp().then((fn) => { cleanup = fn }) return () => { if (cleanup) cleanup() } }, []) if (!eventStore || !accountManager || !relayPool) { return (
) } return (
{toastMessage && ( )}
) } export default App