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:
parent
855a003962
commit
2dec184c42
5 changed files with 160 additions and 209 deletions
37
src/App.vue
37
src/App.vue
|
|
@ -8,28 +8,39 @@ import { Toaster } from '@/components/ui/sonner'
|
||||||
import 'vue-sonner/style.css'
|
import 'vue-sonner/style.css'
|
||||||
import { auth } from '@/composables/useAuth'
|
import { auth } from '@/composables/useAuth'
|
||||||
import { useMarketPreloader } from '@/composables/useMarketPreloader'
|
import { useMarketPreloader } from '@/composables/useMarketPreloader'
|
||||||
import { useNostrChatPreloader } from '@/composables/useNostrChatPreloader'
|
import { nostrChat } from '@/composables/useNostrChat'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const showLoginDialog = ref(false)
|
const showLoginDialog = ref(false)
|
||||||
|
|
||||||
// Initialize preloaders
|
// Initialize preloader
|
||||||
const marketPreloader = useMarketPreloader()
|
const marketPreloader = useMarketPreloader()
|
||||||
const chatPreloader = useNostrChatPreloader()
|
|
||||||
|
|
||||||
// Hide navbar on login page
|
// Hide navbar on login page
|
||||||
const showNavbar = computed(() => {
|
const showNavbar = computed(() => {
|
||||||
return route.path !== '/login'
|
return route.path !== '/login'
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleLoginSuccess() {
|
async function handleLoginSuccess() {
|
||||||
showLoginDialog.value = false
|
showLoginDialog.value = false
|
||||||
toast.success('Welcome back!')
|
toast.success('Welcome back!')
|
||||||
|
|
||||||
// Trigger preloading after successful login
|
// Trigger preloading after successful login
|
||||||
marketPreloader.preloadMarket()
|
marketPreloader.preloadMarket()
|
||||||
chatPreloader.preloadChat()
|
|
||||||
|
// Connect to chat
|
||||||
|
if (!nostrChat.isConnected.value) {
|
||||||
|
try {
|
||||||
|
await nostrChat.connect()
|
||||||
|
|
||||||
|
// Load peers and subscribe to all for notifications
|
||||||
|
const peers = await nostrChat.loadPeers()
|
||||||
|
await nostrChat.subscribeToAllPeersForNotifications(peers)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize chat:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -42,15 +53,23 @@ onMounted(async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Watch for authentication changes and trigger preloading
|
// Watch for authentication changes and trigger preloading
|
||||||
watch(() => auth.isAuthenticated.value, (isAuthenticated) => {
|
watch(() => auth.isAuthenticated.value, async (isAuthenticated) => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
if (!marketPreloader.isPreloaded.value) {
|
if (!marketPreloader.isPreloaded.value) {
|
||||||
console.log('User authenticated, triggering market preload...')
|
console.log('User authenticated, triggering market preload...')
|
||||||
marketPreloader.preloadMarket()
|
marketPreloader.preloadMarket()
|
||||||
}
|
}
|
||||||
if (!chatPreloader.isPreloaded.value) {
|
if (!nostrChat.isConnected.value) {
|
||||||
console.log('User authenticated, triggering chat preload...')
|
console.log('User authenticated, connecting to chat...')
|
||||||
chatPreloader.preloadChat()
|
try {
|
||||||
|
await nostrChat.connect()
|
||||||
|
|
||||||
|
// Load peers and subscribe to all for notifications
|
||||||
|
const peers = await nostrChat.loadPeers()
|
||||||
|
await nostrChat.subscribeToAllPeersForNotifications(peers)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize chat:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import ProfileDialog from '@/components/auth/ProfileDialog.vue'
|
||||||
import CurrencyDisplay from '@/components/ui/CurrencyDisplay.vue'
|
import CurrencyDisplay from '@/components/ui/CurrencyDisplay.vue'
|
||||||
import { auth } from '@/composables/useAuth'
|
import { auth } from '@/composables/useAuth'
|
||||||
import { useMarketPreloader } from '@/composables/useMarketPreloader'
|
import { useMarketPreloader } from '@/composables/useMarketPreloader'
|
||||||
import { useNostrChatPreloader } from '@/composables/useNostrChatPreloader'
|
import { nostrChat } from '@/composables/useNostrChat'
|
||||||
|
|
||||||
interface NavigationItem {
|
interface NavigationItem {
|
||||||
name: string
|
name: string
|
||||||
|
|
@ -27,7 +27,6 @@ const isOpen = ref(false)
|
||||||
const showLoginDialog = ref(false)
|
const showLoginDialog = ref(false)
|
||||||
const showProfileDialog = ref(false)
|
const showProfileDialog = ref(false)
|
||||||
const marketPreloader = useMarketPreloader()
|
const marketPreloader = useMarketPreloader()
|
||||||
const chatPreloader = useNostrChatPreloader()
|
|
||||||
|
|
||||||
const navigation = computed<NavigationItem[]>(() => [
|
const navigation = computed<NavigationItem[]>(() => [
|
||||||
{ name: t('nav.home'), href: '/' },
|
{ name: t('nav.home'), href: '/' },
|
||||||
|
|
@ -45,9 +44,9 @@ const totalBalance = computed(() => {
|
||||||
}, 0)
|
}, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Compute total unread messages
|
// Compute total unread messages (reactive)
|
||||||
const totalUnreadMessages = computed(() => {
|
const totalUnreadMessages = computed(() => {
|
||||||
return chatPreloader.getTotalUnreadCount()
|
return nostrChat.totalUnreadCount.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleMenu = () => {
|
const toggleMenu = () => {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@
|
||||||
Disconnected
|
Disconnected
|
||||||
</Badge>
|
</Badge>
|
||||||
<!-- Total unread count -->
|
<!-- Total unread count -->
|
||||||
<Badge v-if="getTotalUnreadCount() > 0" variant="destructive" class="text-xs">
|
<Badge v-if="totalUnreadCount > 0" variant="destructive" class="text-xs">
|
||||||
{{ getTotalUnreadCount() }} unread
|
{{ totalUnreadCount }} unread
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<Button @click="refreshPeers" :disabled="isLoading" size="sm">
|
<Button @click="refreshPeers" :disabled="isLoading" size="sm">
|
||||||
|
|
@ -198,8 +198,8 @@
|
||||||
Disconnected
|
Disconnected
|
||||||
</Badge>
|
</Badge>
|
||||||
<!-- Total unread count -->
|
<!-- Total unread count -->
|
||||||
<Badge v-if="getTotalUnreadCount() > 0" variant="destructive" class="text-xs">
|
<Badge v-if="totalUnreadCount > 0" variant="destructive" class="text-xs">
|
||||||
{{ getTotalUnreadCount() }} unread
|
{{ totalUnreadCount }} unread
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<Button @click="refreshPeers" :disabled="isLoading" size="sm">
|
<Button @click="refreshPeers" :disabled="isLoading" size="sm">
|
||||||
|
|
@ -375,8 +375,8 @@ import { Input } from '@/components/ui/input'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
||||||
import { useNostrChat } from '@/composables/useNostrChat'
|
import { nostrChat } from '@/composables/useNostrChat'
|
||||||
import { useNostrChatPreloader } from '@/composables/useNostrChatPreloader'
|
|
||||||
import { useFuzzySearch } from '@/composables/useFuzzySearch'
|
import { useFuzzySearch } from '@/composables/useFuzzySearch'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
|
@ -386,15 +386,12 @@ interface Peer {
|
||||||
pubkey: string
|
pubkey: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize preloader and chat
|
|
||||||
const chatPreloader = useNostrChatPreloader()
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const peers = computed(() => chatPreloader.peers.value)
|
const peers = computed(() => nostrChat.peers.value)
|
||||||
const selectedPeer = ref<Peer | null>(null)
|
const selectedPeer = ref<Peer | null>(null)
|
||||||
const messageInput = ref('')
|
const messageInput = ref('')
|
||||||
|
|
||||||
const isLoading = computed(() => chatPreloader.isPreloading.value)
|
const isLoading = ref(false)
|
||||||
const showChat = ref(false)
|
const showChat = ref(false)
|
||||||
const messagesScrollArea = ref<HTMLElement | null>(null)
|
const messagesScrollArea = ref<HTMLElement | null>(null)
|
||||||
const messagesContainer = ref<HTMLElement | null>(null)
|
const messagesContainer = ref<HTMLElement | null>(null)
|
||||||
|
|
@ -403,7 +400,7 @@ const scrollTarget = ref<HTMLElement | null>(null)
|
||||||
// Mobile detection
|
// Mobile detection
|
||||||
const isMobile = ref(false)
|
const isMobile = ref(false)
|
||||||
|
|
||||||
// Nostr chat composable
|
// Nostr chat composable (singleton)
|
||||||
const {
|
const {
|
||||||
isConnected,
|
isConnected,
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -414,9 +411,9 @@ const {
|
||||||
onMessageAdded,
|
onMessageAdded,
|
||||||
markMessagesAsRead,
|
markMessagesAsRead,
|
||||||
getUnreadCount,
|
getUnreadCount,
|
||||||
getTotalUnreadCount,
|
totalUnreadCount,
|
||||||
getLatestMessageTimestamp
|
getLatestMessageTimestamp
|
||||||
} = useNostrChat()
|
} = nostrChat
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const currentMessages = computed(() => {
|
const currentMessages = computed(() => {
|
||||||
|
|
@ -505,7 +502,14 @@ const goBackToPeers = () => {
|
||||||
|
|
||||||
const refreshPeers = async () => {
|
const refreshPeers = async () => {
|
||||||
console.log('Refreshing peers and chat data...')
|
console.log('Refreshing peers and chat data...')
|
||||||
await chatPreloader.preloadChat()
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
await nostrChat.loadPeers()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to refresh peers:', error)
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectPeer = async (peer: Peer) => {
|
const selectPeer = async (peer: Peer) => {
|
||||||
|
|
@ -600,22 +604,22 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Chat component mounted - checking if preloader has data...')
|
console.log('Chat component mounted - checking connection state...')
|
||||||
|
|
||||||
// If chat is already preloaded, we're good to go
|
// If not connected, connect
|
||||||
if (chatPreloader.isPreloaded.value) {
|
if (!isConnected.value) {
|
||||||
console.log('Chat data was preloaded, connecting to chat...')
|
console.log('Not connected, connecting to chat...')
|
||||||
await connect()
|
|
||||||
console.log('Chat connected successfully')
|
|
||||||
} else if (!chatPreloader.isPreloading.value) {
|
|
||||||
// If not preloaded and not currently preloading, trigger preload
|
|
||||||
console.log('Chat data not preloaded, triggering preload...')
|
|
||||||
await chatPreloader.preloadChat()
|
|
||||||
await connect()
|
await connect()
|
||||||
} else {
|
} else {
|
||||||
// Currently preloading, just connect
|
console.log('Already connected to chat')
|
||||||
console.log('Chat is currently preloading, just connecting...')
|
}
|
||||||
await connect()
|
|
||||||
|
// If no peers loaded, load them
|
||||||
|
if (peers.value.length === 0) {
|
||||||
|
console.log('No peers loaded, loading peers...')
|
||||||
|
await nostrChat.loadPeers()
|
||||||
|
} else {
|
||||||
|
console.log('Peers already loaded:', peers.value.length)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,9 @@ export function useNostrChat() {
|
||||||
|
|
||||||
// Track latest message timestamp for each peer (for sorting)
|
// Track latest message timestamp for each peer (for sorting)
|
||||||
const latestMessageTimestamps = ref<Map<string, number>>(new Map())
|
const latestMessageTimestamps = ref<Map<string, number>>(new Map())
|
||||||
|
|
||||||
|
// Store peers globally
|
||||||
|
const peers = ref<any[]>([])
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const isLoggedIn = computed(() => !!currentUser.value)
|
const isLoggedIn = computed(() => !!currentUser.value)
|
||||||
|
|
@ -100,6 +103,15 @@ export function useNostrChat() {
|
||||||
return total
|
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
|
// Get latest message timestamp for a peer
|
||||||
const getLatestMessageTimestamp = (peerPubkey: string): number => {
|
const getLatestMessageTimestamp = (peerPubkey: string): number => {
|
||||||
return latestMessageTimestamps.value.get(peerPubkey) || 0
|
return latestMessageTimestamps.value.get(peerPubkey) || 0
|
||||||
|
|
@ -798,11 +810,87 @@ export function useNostrChat() {
|
||||||
messages.value.delete(peerPubkey)
|
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 {
|
return {
|
||||||
// State
|
// State
|
||||||
isConnected: readonly(isConnected),
|
isConnected: readonly(isConnected),
|
||||||
messages: readonly(messages),
|
messages: readonly(messages),
|
||||||
isLoggedIn: readonly(isLoggedIn),
|
isLoggedIn: readonly(isLoggedIn),
|
||||||
|
peers: readonly(peers),
|
||||||
|
|
||||||
|
// Reactive computed properties
|
||||||
|
totalUnreadCount: readonly(totalUnreadCount),
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
connect,
|
connect,
|
||||||
|
|
@ -825,6 +913,13 @@ export function useNostrChat() {
|
||||||
|
|
||||||
// Timestamp methods (for sorting)
|
// Timestamp methods (for sorting)
|
||||||
getLatestMessageTimestamp,
|
getLatestMessageTimestamp,
|
||||||
getAllLatestMessageTimestamps
|
getAllLatestMessageTimestamps,
|
||||||
|
|
||||||
|
// Peer management methods
|
||||||
|
loadPeers,
|
||||||
|
subscribeToAllPeersForNotifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export singleton instance for global state
|
||||||
|
export const nostrChat = useNostrChat()
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
import { ref, readonly } from 'vue'
|
|
||||||
import { useNostrChat } from './useNostrChat'
|
|
||||||
import { getAuthToken } from '@/lib/config/lnbits'
|
|
||||||
import { config } from '@/lib/config'
|
|
||||||
|
|
||||||
export interface Peer {
|
|
||||||
user_id: string
|
|
||||||
username: string
|
|
||||||
pubkey: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useNostrChatPreloader() {
|
|
||||||
const isPreloading = ref(false)
|
|
||||||
const isPreloaded = ref(false)
|
|
||||||
const preloadError = ref<string | null>(null)
|
|
||||||
const peers = ref<Peer[]>([])
|
|
||||||
|
|
||||||
const chat = useNostrChat()
|
|
||||||
|
|
||||||
const preloadChat = async () => {
|
|
||||||
// Don't preload if already done or currently preloading
|
|
||||||
if (isPreloaded.value || isPreloading.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
isPreloading.value = true
|
|
||||||
preloadError.value = null
|
|
||||||
|
|
||||||
console.log('Preloading chat data...')
|
|
||||||
|
|
||||||
// Connect to chat
|
|
||||||
await chat.connect()
|
|
||||||
|
|
||||||
// Load peers
|
|
||||||
await loadPeers()
|
|
||||||
|
|
||||||
// Subscribe to all peers for notifications (without loading full history)
|
|
||||||
if (peers.value.length > 0) {
|
|
||||||
console.log(`Subscribing to ${peers.value.length} peers for notifications`)
|
|
||||||
await subscribeToAllPeersForNotifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
isPreloaded.value = true
|
|
||||||
console.log('Chat data preloaded successfully')
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to preload chat:', error)
|
|
||||||
preloadError.value = error instanceof Error ? error.message : 'Failed to preload chat'
|
|
||||||
// Don't throw error, let the UI handle it gracefully
|
|
||||||
} finally {
|
|
||||||
isPreloading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadPeers = async () => {
|
|
||||||
try {
|
|
||||||
const authToken = getAuthToken()
|
|
||||||
if (!authToken) {
|
|
||||||
console.warn('No authentication token found - cannot load peers')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
const errorText = await response.text()
|
|
||||||
console.error('Peers API Error:', response.status, errorText)
|
|
||||||
throw new Error(`Failed to load peers: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseText = await response.text()
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(responseText)
|
|
||||||
peers.value = data.map((peer: any) => ({
|
|
||||||
user_id: peer.user_id,
|
|
||||||
username: peer.username,
|
|
||||||
pubkey: peer.pubkey
|
|
||||||
}))
|
|
||||||
|
|
||||||
console.log(`Loaded ${peers.value.length} peers for chat preloader`)
|
|
||||||
|
|
||||||
} catch (parseError) {
|
|
||||||
console.error('JSON Parse Error for peers:', parseError)
|
|
||||||
throw new Error('Invalid JSON response from peers API')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load peers in preloader:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to all peers for notifications (without loading full message history)
|
|
||||||
const subscribeToAllPeersForNotifications = async () => {
|
|
||||||
if (!peers.value.length) {
|
|
||||||
console.log('No peers to subscribe to')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for connection to be established
|
|
||||||
if (!chat.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 (!chat.isConnected.value) {
|
|
||||||
console.warn('Still not connected, skipping peer subscriptions')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Subscribing to ${peers.value.length} peers for notifications`)
|
|
||||||
|
|
||||||
let successCount = 0
|
|
||||||
let errorCount = 0
|
|
||||||
|
|
||||||
for (const peer of peers.value) {
|
|
||||||
try {
|
|
||||||
console.log(`Attempting to subscribe to peer: ${peer.pubkey} (${peer.username})`)
|
|
||||||
// Subscribe to peer for notifications only (don't load full history)
|
|
||||||
const subscription = await chat.subscribeToPeerForNotifications(peer.pubkey)
|
|
||||||
if (subscription) {
|
|
||||||
console.log(`Successfully subscribed to notifications for peer: ${peer.pubkey}`)
|
|
||||||
successCount++
|
|
||||||
} else {
|
|
||||||
console.warn(`Failed to create subscription for peer: ${peer.pubkey}`)
|
|
||||||
errorCount++
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Failed to subscribe to peer ${peer.pubkey}:`, error)
|
|
||||||
errorCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Chat preloader subscription summary: ${successCount} successful, ${errorCount} failed`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetPreload = () => {
|
|
||||||
isPreloaded.value = false
|
|
||||||
preloadError.value = null
|
|
||||||
peers.value = []
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isPreloading: readonly(isPreloading),
|
|
||||||
isPreloaded: readonly(isPreloaded),
|
|
||||||
preloadError: readonly(preloadError),
|
|
||||||
peers: readonly(peers),
|
|
||||||
preloadChat,
|
|
||||||
resetPreload,
|
|
||||||
|
|
||||||
// Expose chat composable methods for global access
|
|
||||||
getTotalUnreadCount: chat.getTotalUnreadCount,
|
|
||||||
getUnreadCount: chat.getUnreadCount,
|
|
||||||
getAllUnreadCounts: chat.getAllUnreadCounts,
|
|
||||||
markMessagesAsRead: chat.markMessagesAsRead,
|
|
||||||
clearAllUnreadCounts: chat.clearAllUnreadCounts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue