mirror of
https://github.com/dergigi/boris.git
synced 2026-01-19 14:54:30 +01:00
- Create contactsController similar to bookmarkController - Manage friends/contacts list in one place across the app - Auto-load contacts on login, cache results per pubkey - Stream partial contacts as they arrive - Update App.tsx to subscribe to contacts controller - Update Debug.tsx to use centralized contacts instead of fetching directly - Reset contacts on logout - Contacts won't reload unnecessarily (cached by pubkey) - Debug 'Load Friends' button forces reload to show streaming behavior
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import { RelayPool } from 'applesauce-relay'
|
|
import { fetchContacts } from './contactService'
|
|
|
|
type ContactsCallback = (contacts: Set<string>) => void
|
|
type LoadingCallback = (loading: boolean) => void
|
|
|
|
/**
|
|
* Shared contacts/friends controller
|
|
* Manages the user's follow list centrally, similar to bookmarkController
|
|
*/
|
|
class ContactsController {
|
|
private contactsListeners: ContactsCallback[] = []
|
|
private loadingListeners: LoadingCallback[] = []
|
|
|
|
private currentContacts: Set<string> = new Set()
|
|
private lastLoadedPubkey: string | null = null
|
|
|
|
onContacts(cb: ContactsCallback): () => void {
|
|
this.contactsListeners.push(cb)
|
|
return () => {
|
|
this.contactsListeners = this.contactsListeners.filter(l => l !== cb)
|
|
}
|
|
}
|
|
|
|
onLoading(cb: LoadingCallback): () => void {
|
|
this.loadingListeners.push(cb)
|
|
return () => {
|
|
this.loadingListeners = this.loadingListeners.filter(l => l !== cb)
|
|
}
|
|
}
|
|
|
|
private setLoading(loading: boolean): void {
|
|
this.loadingListeners.forEach(cb => cb(loading))
|
|
}
|
|
|
|
private emitContacts(contacts: Set<string>): void {
|
|
this.contactsListeners.forEach(cb => cb(contacts))
|
|
}
|
|
|
|
/**
|
|
* Get current contacts without triggering a reload
|
|
*/
|
|
getContacts(): Set<string> {
|
|
return new Set(this.currentContacts)
|
|
}
|
|
|
|
/**
|
|
* Check if contacts are loaded for a specific pubkey
|
|
*/
|
|
isLoadedFor(pubkey: string): boolean {
|
|
return this.lastLoadedPubkey === pubkey && this.currentContacts.size > 0
|
|
}
|
|
|
|
/**
|
|
* Reset state (for logout or manual refresh)
|
|
*/
|
|
reset(): void {
|
|
this.currentContacts.clear()
|
|
this.lastLoadedPubkey = null
|
|
this.emitContacts(this.currentContacts)
|
|
}
|
|
|
|
/**
|
|
* Load contacts for a user
|
|
* Streams partial results and caches the final list
|
|
*/
|
|
async start(options: {
|
|
relayPool: RelayPool
|
|
pubkey: string
|
|
force?: boolean
|
|
}): Promise<void> {
|
|
const { relayPool, pubkey, force = false } = options
|
|
|
|
// Skip if already loaded for this pubkey (unless forced)
|
|
if (!force && this.isLoadedFor(pubkey)) {
|
|
console.log('[contacts] ✅ Already loaded for', pubkey.slice(0, 8))
|
|
this.emitContacts(this.currentContacts)
|
|
return
|
|
}
|
|
|
|
this.setLoading(true)
|
|
console.log('[contacts] 🔍 Loading contacts for', pubkey.slice(0, 8))
|
|
|
|
try {
|
|
const contacts = await fetchContacts(
|
|
relayPool,
|
|
pubkey,
|
|
(partial) => {
|
|
// Stream partial updates
|
|
this.currentContacts = new Set(partial)
|
|
this.emitContacts(this.currentContacts)
|
|
console.log('[contacts] 📥 Partial contacts:', partial.size)
|
|
}
|
|
)
|
|
|
|
// Store final result
|
|
this.currentContacts = new Set(contacts)
|
|
this.lastLoadedPubkey = pubkey
|
|
this.emitContacts(this.currentContacts)
|
|
|
|
console.log('[contacts] ✅ Loaded', contacts.size, 'contacts')
|
|
} catch (error) {
|
|
console.error('[contacts] ❌ Failed to load contacts:', error)
|
|
this.currentContacts.clear()
|
|
this.emitContacts(this.currentContacts)
|
|
} finally {
|
|
this.setLoading(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Singleton instance
|
|
export const contactsController = new ContactsController()
|
|
|