refactor: Replace Nostr chat preloader with a singleton pattern for improved state management

- Remove the useNostrChatPreloader composable and integrate its functionality into the useNostrChat composable, streamlining chat data handling.
- Update App.vue and ChatComponent to utilize the new singleton instance for managing chat connections and peer subscriptions.
- Enhance Navbar and ChatComponent to reflect changes in unread message tracking and peer management, improving user experience.
- Ensure proper error handling and logging during chat connection and peer loading processes.
This commit is contained in:
padreug 2025-08-09 15:19:52 +02:00
parent 855a003962
commit 2dec184c42
5 changed files with 160 additions and 209 deletions

View file

@ -77,6 +77,9 @@ export function useNostrChat() {
// Track latest message timestamp for each peer (for sorting)
const latestMessageTimestamps = ref<Map<string, number>>(new Map())
// Store peers globally
const peers = ref<any[]>([])
// Computed
const isLoggedIn = computed(() => !!currentUser.value)
@ -100,6 +103,15 @@ export function useNostrChat() {
return total
}
// Reactive computed total unread count
const totalUnreadCount = computed(() => {
let total = 0
for (const count of unreadCounts.value.values()) {
total += count
}
return total
})
// Get latest message timestamp for a peer
const getLatestMessageTimestamp = (peerPubkey: string): number => {
return latestMessageTimestamps.value.get(peerPubkey) || 0
@ -798,11 +810,87 @@ export function useNostrChat() {
messages.value.delete(peerPubkey)
}
// Load peers from API
const loadPeers = async () => {
try {
const authToken = getAuthToken()
if (!authToken) {
throw new Error('No authentication token found')
}
const API_BASE_URL = config.api.baseUrl || 'http://localhost:5006'
const response = await fetch(`${API_BASE_URL}/api/v1/auth/nostr/pubkeys`, {
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Failed to load peers: ${response.status}`)
}
const data = await response.json()
const loadedPeers = data.map((peer: any) => ({
user_id: peer.user_id,
username: peer.username,
pubkey: peer.pubkey
}))
// Store peers in the singleton state
peers.value = loadedPeers
console.log(`Loaded ${loadedPeers.length} peers`)
return loadedPeers
} catch (error) {
console.error('Failed to load peers:', error)
throw error
}
}
// Subscribe to all peers for notifications (without loading full message history)
const subscribeToAllPeersForNotifications = async (peers: any[]) => {
if (!peers.length) {
console.log('No peers to subscribe to')
return
}
// Wait for connection to be established
if (!isConnected.value) {
console.log('Waiting for connection to be established before subscribing to peers')
// Wait a bit for connection to establish
await new Promise(resolve => setTimeout(resolve, 1000))
if (!isConnected.value) {
console.warn('Still not connected, skipping peer subscriptions')
return
}
}
console.log(`Subscribing to ${peers.length} peers for notifications`)
// Subscribe to each peer for notifications
for (const peer of peers) {
try {
await subscribeToPeerForNotifications(peer.pubkey)
} catch (error) {
console.error(`Failed to subscribe to peer ${peer.username} (${peer.pubkey}):`, error)
}
}
console.log(`Successfully subscribed to ${peers.length} peers for notifications`)
}
return {
// State
isConnected: readonly(isConnected),
messages: readonly(messages),
isLoggedIn: readonly(isLoggedIn),
peers: readonly(peers),
// Reactive computed properties
totalUnreadCount: readonly(totalUnreadCount),
// Methods
connect,
@ -825,6 +913,13 @@ export function useNostrChat() {
// Timestamp methods (for sorting)
getLatestMessageTimestamp,
getAllLatestMessageTimestamps
getAllLatestMessageTimestamps,
// Peer management methods
loadPeers,
subscribeToAllPeersForNotifications
}
}
}
// Export singleton instance for global state
export const nostrChat = useNostrChat()