mirror of
https://github.com/dergigi/boris.git
synced 2025-12-28 03:54:51 +01:00
- Import EventStore from applesauce-core/dist/event-store/event-store.js - Add TS module declaration shim for deep import typing - No functional changes, fixes Vercel bundling for async-event-store.js
170 lines
5.2 KiB
TypeScript
170 lines
5.2 KiB
TypeScript
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 (
|
|
<Routes>
|
|
<Route
|
|
path="/a/:naddr"
|
|
element={
|
|
<Bookmarks
|
|
relayPool={relayPool}
|
|
onLogout={handleLogout}
|
|
/>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/r/*"
|
|
element={
|
|
<Bookmarks
|
|
relayPool={relayPool}
|
|
onLogout={handleLogout}
|
|
/>
|
|
}
|
|
/>
|
|
<Route path="/" element={<Navigate to={`/a/${DEFAULT_ARTICLE}`} replace />} />
|
|
</Routes>
|
|
)
|
|
}
|
|
|
|
function App() {
|
|
const [eventStore, setEventStore] = useState<EventStore | null>(null)
|
|
const [accountManager, setAccountManager] = useState<AccountManager | null>(null)
|
|
const [relayPool, setRelayPool] = useState<RelayPool | null>(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 (
|
|
<div className="loading">
|
|
<FontAwesomeIcon icon={faSpinner} spin />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<EventStoreProvider eventStore={eventStore}>
|
|
<AccountsProvider manager={accountManager}>
|
|
<BrowserRouter>
|
|
<div className="app">
|
|
<AppRoutes relayPool={relayPool} showToast={showToast} />
|
|
</div>
|
|
</BrowserRouter>
|
|
{toastMessage && (
|
|
<Toast
|
|
message={toastMessage}
|
|
type={toastType}
|
|
onClose={clearToast}
|
|
/>
|
|
)}
|
|
</AccountsProvider>
|
|
</EventStoreProvider>
|
|
)
|
|
}
|
|
|
|
export default App
|