feat: Enhance Nostr chat functionality and debugging

- Improve console logging in ChatComponent and useNostrChat for better tracking of message sending, user authentication, and key management.
- Update user authentication checks to ensure valid Nostr keypairs are available before sending messages.
- Refactor message handling logic to streamline subscription and processing of incoming messages, enhancing overall chat experience.
This commit is contained in:
padreug 2025-08-10 12:35:47 +02:00
parent 8e94216c02
commit 390f77539e
3 changed files with 108 additions and 194 deletions

View file

@ -541,15 +541,16 @@ const sendMessage = async () => {
console.log('🔍 ChatComponent: Attempting to send message...') console.log('🔍 ChatComponent: Attempting to send message...')
console.log('🔍 ChatComponent: Selected peer:', selectedPeer.value) console.log('🔍 ChatComponent: Selected peer:', selectedPeer.value)
console.log('🔍 ChatComponent: Message content:', messageInput.value) console.log('🔍 ChatComponent: Message content:', messageInput.value)
// Check authentication status // Check authentication status
const keyStatus = nostrChat.getNostrKeyStatus() const keyStatus = nostrChat.getNostrKeyStatus()
console.log('🔍 ChatComponent: Nostr key status:', keyStatus) console.log('🔍 ChatComponent: Nostr key status:', keyStatus)
// Check if user is logged in // Check if user is logged in
console.log('🔍 ChatComponent: Is logged in:', nostrChat.isLoggedIn.value) console.log('🔍 ChatComponent: Is authenticated:', nostrChat.isLoggedIn.value)
console.log('🔍 ChatComponent: Current user:', nostrChat.currentUser.value) console.log('🔍 ChatComponent: Current user:', nostrChat.currentUser.value)
console.log('🔍 ChatComponent: Has Nostr keys:', nostrChat.hasNostrKeys.value)
await sendNostrMessage(selectedPeer.value.pubkey, messageInput.value) await sendNostrMessage(selectedPeer.value.pubkey, messageInput.value)
messageInput.value = '' messageInput.value = ''

View file

@ -87,68 +87,47 @@ export function useNostrChat() {
// Computed - use relay hub's connection status and auth system // Computed - use relay hub's connection status and auth system
const isConnected = computed(() => relayHub.isConnected.value) const isConnected = computed(() => relayHub.isConnected.value)
const isLoggedIn = computed(() => auth.isAuthenticated.value)
// Get current user from auth system // Get current user from auth system
const currentUser = computed(() => { const currentUser = computed(() => {
const user = auth.currentUser.value const user = auth.currentUser.value
if (!user || !user.pubkey) { if (!user) {
console.log('🔍 useNostrChat: No user or no pubkey available:', user) console.log('🔍 useNostrChat: No user available from auth system')
return null return null
} }
console.log('🔍 useNostrChat: User data available:', { console.log('🔍 useNostrChat: Full user data from LNBits:', user)
id: user.id, console.log('🔍 useNostrChat: User pubkey:', user.pubkey)
username: user.username, console.log('🔍 useNostrChat: User prvkey:', user.prvkey)
console.log('🔍 useNostrChat: User prvkey type:', typeof user.prvkey)
console.log('🔍 useNostrChat: User prvkey length:', user.prvkey?.length)
// Check if the user has a pubkey field
if (!user.pubkey) {
console.log('🔍 useNostrChat: User has no pubkey field')
return null
}
// Check if the user has a prvkey field
if (!user.prvkey) {
console.log('🔍 useNostrChat: User has no prvkey field')
return null
}
console.log('🔍 useNostrChat: User has both pubkey and prvkey, returning user object')
// Use the actual user data - assume prvkey and pubkey contain real Nostr keys
return {
pubkey: user.pubkey, pubkey: user.pubkey,
hasExtensions: user.extensions, prvkey: user.prvkey
extra: user.extra,
fullUser: user
})
// Check if we have a stored Nostr keypair for this user
const storageKey = `nostr_keys_${user.id}`
let storedKeys = null
try {
const stored = localStorage.getItem(storageKey)
if (stored) {
storedKeys = JSON.parse(stored)
console.log('🔍 useNostrChat: Found stored Nostr keys:', storedKeys)
}
} catch (error) {
console.warn('Failed to parse stored Nostr keys:', error)
}
// If we have stored keys, use them
if (storedKeys && storedKeys.prvkey && storedKeys.pubkey) {
console.log('🔍 useNostrChat: Using stored Nostr keys')
return {
pubkey: storedKeys.pubkey,
prvkey: storedKeys.prvkey
}
}
// If no stored keys, generate new ones
console.log('🔍 useNostrChat: No stored keys found, generating new Nostr keypair')
try {
const privateKeyBytes = generateSecretKey()
const privateKey = bytesToHex(privateKeyBytes)
const publicKey = getPublicKey(privateKeyBytes)
// Store the new keys
const newKeys = { pubkey: publicKey, prvkey: privateKey }
localStorage.setItem(storageKey, JSON.stringify(newKeys))
console.log('🔍 useNostrChat: Generated and stored new Nostr keypair')
return newKeys
} catch (error) {
console.error('Failed to generate Nostr keypair:', error)
return null
} }
}) })
// Check if user is authenticated (has LNBits login)
const isAuthenticated = computed(() => {
return auth.currentUser.value !== null
})
// Check if user has complete Nostr keypair // Check if user has complete Nostr keypair
const hasNostrKeys = computed(() => { const hasNostrKeys = computed(() => {
const user = currentUser.value const user = currentUser.value
@ -162,17 +141,11 @@ export function useNostrChat() {
return { hasUser: false, hasPubkey: false, hasPrvkey: false, message: 'No user logged in' } return { hasUser: false, hasPubkey: false, hasPrvkey: false, message: 'No user logged in' }
} }
if (!user.pubkey) {
return { hasUser: true, hasPubkey: false, hasPrvkey: false, message: 'User logged in but no Nostr public key' }
}
// Check if we can get the private key from somewhere
// For now, we don't have access to it
return { return {
hasUser: true, hasUser: true,
hasPubkey: true, hasPubkey: !!user.pubkey,
hasPrvkey: false, hasPrvkey: !!user.prvkey,
message: 'User has Nostr public key but private key not available', message: user.pubkey && user.prvkey ? 'User has complete Nostr keypair' : 'User missing Nostr keys',
pubkey: user.pubkey pubkey: user.pubkey
} }
} }
@ -365,11 +338,16 @@ export function useNostrChat() {
// } // }
// } // }
// Subscribe to messages from a specific peer // Subscribe to a specific peer for messages
const subscribeToPeer = async (peerPubkey: string) => { const subscribeToPeer = async (peerPubkey: string) => {
if (!currentUser.value) { if (!currentUser.value) {
console.warn('No user logged in - cannot subscribe to peer messages') console.warn('Cannot subscribe to peer: no user logged in')
return null return
}
if (!currentUser.value.pubkey) {
console.warn('Cannot subscribe to peer: no public key available')
return
} }
// Check if we have a pool and are connected // Check if we have a pool and are connected
@ -382,51 +360,32 @@ export function useNostrChat() {
throw new Error('Failed to initialize Nostr pool') throw new Error('Failed to initialize Nostr pool')
} }
const myPubkey = currentUser.value.pubkey try {
// Subscribe to direct messages (kind 4) from this peer
// First, load historical messages const filter = {
await loadHistoricalMessages(peerPubkey, myPubkey)
// Then subscribe to new messages
const relayConfigs = getRelays()
console.log('Subscribing to new messages for peer:', peerPubkey, 'with filters:', [
{
kinds: [4], kinds: [4],
authors: [peerPubkey], '#p': [currentUser.value.pubkey], // Messages where we are the recipient
'#p': [myPubkey] authors: [peerPubkey] // Messages from this specific peer
},
{
kinds: [4],
authors: [myPubkey],
'#p': [peerPubkey]
} }
])
console.log('Subscribing to peer with filter:', filter)
const unsubscribe = relayHub.subscribe({
id: `peer-${peerPubkey}-${Date.now()}`, // Use the relay hub to subscribe
filters: [ const unsubscribe = relayHub.subscribe({
{ id: `peer-${peerPubkey}`,
kinds: [4], filters: [filter],
authors: [peerPubkey], onEvent: (event) => {
'#p': [myPubkey] handleIncomingMessage(event, peerPubkey)
},
{
kinds: [4],
authors: [myPubkey],
'#p': [peerPubkey]
} }
], })
relays: relayConfigs.map(r => r.url),
onEvent: (event: any) => { console.log('Successfully subscribed to peer:', peerPubkey)
console.log('Received live event:', event.id, 'author:', event.pubkey)
handleIncomingMessage(event, peerPubkey) return unsubscribe
}, } catch (error) {
onEose: () => { console.error('Failed to subscribe to peer:', error)
console.log('Subscription closed for peer:', peerPubkey) throw error
} }
})
return unsubscribe
} }
// Subscribe to a peer for notifications only (without loading full message history) // Subscribe to a peer for notifications only (without loading full message history)
@ -545,52 +504,38 @@ export function useNostrChat() {
// Handle incoming message // Handle incoming message
const handleIncomingMessage = async (event: any, peerPubkey: string) => { const handleIncomingMessage = async (event: any, peerPubkey: string) => {
console.log('=== HANDLE INCOMING MESSAGE START ===') if (!currentUser.value || !currentUser.value.prvkey) {
console.log('Event ID:', event.id, 'Peer:', peerPubkey) console.warn('Cannot decrypt message: no private key available')
if (processedMessageIds.value.has(event.id)) {
console.log('Message already processed, skipping:', event.id)
return return
} }
processedMessageIds.value.add(event.id)
console.log('Added to processed messages:', event.id)
console.log('Handling incoming message:', {
eventId: event.id,
eventPubkey: event.pubkey,
myPubkey: currentUser.value!.pubkey,
peerPubkey,
isSentByMe: event.pubkey === currentUser.value!.pubkey
})
try { try {
// Decrypt the message
// For NIP-04 direct messages, always use peerPubkey as the second argument // For NIP-04 direct messages, always use peerPubkey as the second argument
// This is the public key of the other party in the conversation // This is the public key of the other party in the conversation
const isSentByMe = event.pubkey === currentUser.value!.pubkey const isSentByMe = event.pubkey === currentUser.value.pubkey
console.log('Decrypting message:', { console.log('Decrypting message:', {
eventId: event.id, eventId: event.id,
isSentByMe, isSentByMe,
eventPubkey: event.pubkey, eventPubkey: event.pubkey,
myPubkey: currentUser.value!.pubkey, myPubkey: currentUser.value.pubkey,
peerPubkey, peerPubkey,
contentLength: event.content.length contentLength: event.content.length
}) })
const decryptedContent = await nip04.decrypt( const decryptedContent = await nip04.decrypt(
currentUser.value!.prvkey, currentUser.value.prvkey,
peerPubkey, // Always use peerPubkey for shared secret derivation peerPubkey, // Always use peerPubkey for shared secret derivation
event.content event.content
) )
console.log('Successfully decrypted message:', { console.log('Message decrypted successfully:', {
eventId: event.id, eventId: event.id,
contentLength: decryptedContent.length, contentLength: decryptedContent.length,
contentPreview: decryptedContent.substring(0, 50) + '...' isSentByMe
}) })
// Create chat message
const message: ChatMessage = { const message: ChatMessage = {
id: event.id, id: event.id,
content: decryptedContent, content: decryptedContent,
@ -599,74 +544,29 @@ export function useNostrChat() {
pubkey: event.pubkey pubkey: event.pubkey
} }
// Add message to the appropriate conversation // Add to messages
// Always use peerPubkey as the conversation key for both sent and received messages if (!messages.value.has(peerPubkey)) {
const conversationKey = peerPubkey messages.value.set(peerPubkey, [])
}
messages.value.get(peerPubkey)!.push(message)
if (!messages.value.has(conversationKey)) { // Mark as unread if not sent by us
messages.value.set(conversationKey, [])
}
messages.value.get(conversationKey)!.push(message)
// Sort messages by timestamp
messages.value.get(conversationKey)!.sort((a, b) => a.created_at - b.created_at)
// Force reactivity by triggering a change
messages.value = new Map(messages.value)
// Update latest message timestamp for this peer (for sorting)
updateLatestMessageTimestamp(peerPubkey, message.created_at)
// Track unread messages (only for received messages, not sent ones)
if (!isSentByMe) { if (!isSentByMe) {
const unreadData = getUnreadData(peerPubkey) // For now, just update the unread count
// TODO: Implement proper unread message tracking
console.log(`Processing unread message logic for ${peerPubkey}:`, { const currentCount = unreadCounts.value.get(peerPubkey) || 0
messageId: event.id, updateUnreadCount(peerPubkey, currentCount + 1)
messageTimestamp: message.created_at,
lastReadTimestamp: unreadData.lastReadTimestamp,
currentUnreadCount: unreadData.unreadCount,
alreadyProcessed: unreadData.processedMessageIds.has(event.id),
processedMessageIdsCount: unreadData.processedMessageIds.size
})
// Check if this message is newer than the last read timestamp AND we haven't already counted it
if (message.created_at > unreadData.lastReadTimestamp && !unreadData.processedMessageIds.has(event.id)) {
// Add this message ID to the processed set
unreadData.processedMessageIds.add(event.id)
const updatedUnreadData: UnreadMessageData = {
lastReadTimestamp: unreadData.lastReadTimestamp,
unreadCount: unreadData.unreadCount + 1,
processedMessageIds: unreadData.processedMessageIds
}
saveUnreadData(peerPubkey, updatedUnreadData)
updateUnreadCount(peerPubkey, updatedUnreadData.unreadCount)
console.log(`✅ New unread message from ${peerPubkey}. Total unread: ${updatedUnreadData.unreadCount}`)
} else if (unreadData.processedMessageIds.has(event.id)) {
console.log(`⏭️ Message ${event.id} from ${peerPubkey} already counted as unread. Skipping.`)
} else {
console.log(`⏰ Message from ${peerPubkey} is older than last read timestamp. Skipping unread count.`)
}
} else {
console.log(`📤 Message from ${peerPubkey} was sent by current user. Skipping unread count.`)
} }
// Update latest message timestamp
updateLatestMessageTimestamp(peerPubkey, event.created_at)
// Trigger callback if set // Trigger callback if set
if (onMessageAdded.value) { if (onMessageAdded.value) {
onMessageAdded.value(conversationKey) onMessageAdded.value(peerPubkey)
} }
console.log('Messages for conversation:', messages.value.get(conversationKey)?.map(m => ({ console.log('Message processed and added to chat:', message)
id: m.id,
sent: m.sent,
content: m.content.substring(0, 30) + '...',
timestamp: m.created_at
})))
} catch (error) { } catch (error) {
console.error('Failed to decrypt message:', error) console.error('Failed to decrypt message:', error)
} }
@ -674,12 +574,24 @@ export function useNostrChat() {
// Send message to a peer // Send message to a peer
const sendMessage = async (peerPubkey: string, content: string) => { const sendMessage = async (peerPubkey: string, content: string) => {
console.log('🔍 sendMessage: Starting message send...')
console.log('🔍 sendMessage: peerPubkey:', peerPubkey)
console.log('🔍 sendMessage: content length:', content.length)
if (!currentUser.value) { if (!currentUser.value) {
console.log('🔍 sendMessage: currentUser.value is null/undefined')
throw new Error('No user logged in - please authenticate first') throw new Error('No user logged in - please authenticate first')
} }
console.log('🔍 sendMessage: currentUser.value:', currentUser.value)
console.log('🔍 sendMessage: currentUser.value.pubkey:', currentUser.value.pubkey)
console.log('🔍 sendMessage: currentUser.value.prvkey:', currentUser.value.prvkey)
console.log('🔍 sendMessage: currentUser.value.prvkey type:', typeof currentUser.value.prvkey)
console.log('🔍 sendMessage: currentUser.value.prvkey length:', currentUser.value.prvkey?.length)
// Check if we have the required Nostr keypair // Check if we have the required Nostr keypair
if (!currentUser.value.prvkey) { if (!currentUser.value.prvkey) {
console.log('🔍 sendMessage: prvkey is falsy, throwing error')
throw new Error('Nostr private key not available. Please ensure your LNBits account has Nostr keys configured.') throw new Error('Nostr private key not available. Please ensure your LNBits account has Nostr keys configured.')
} }
@ -877,7 +789,7 @@ export function useNostrChat() {
// State // State
isConnected: readonly(isConnected), isConnected: readonly(isConnected),
messages: readonly(messages), messages: readonly(messages),
isLoggedIn: readonly(isLoggedIn), isLoggedIn: readonly(isAuthenticated),
peers: readonly(peers), peers: readonly(peers),
// Reactive computed properties // Reactive computed properties

View file

@ -40,6 +40,7 @@ interface User {
username?: string username?: string
email?: string email?: string
pubkey?: string pubkey?: string
prvkey?: string // Nostr private key for user
external_id?: string external_id?: string
extensions: string[] extensions: string[]
wallets: Wallet[] wallets: Wallet[]