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

@ -547,8 +547,9 @@ const sendMessage = async () => {
console.log('🔍 ChatComponent: Nostr key status:', keyStatus)
// 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: Has Nostr keys:', nostrChat.hasNostrKeys.value)
await sendNostrMessage(selectedPeer.value.pubkey, messageInput.value)
messageInput.value = ''

View file

@ -87,66 +87,45 @@ export function useNostrChat() {
// Computed - use relay hub's connection status and auth system
const isConnected = computed(() => relayHub.isConnected.value)
const isLoggedIn = computed(() => auth.isAuthenticated.value)
// Get current user from auth system
const currentUser = computed(() => {
const user = auth.currentUser.value
if (!user || !user.pubkey) {
console.log('🔍 useNostrChat: No user or no pubkey available:', user)
if (!user) {
console.log('🔍 useNostrChat: No user available from auth system')
return null
}
console.log('🔍 useNostrChat: User data available:', {
id: user.id,
username: user.username,
console.log('🔍 useNostrChat: Full user data from LNBits:', user)
console.log('🔍 useNostrChat: User pubkey:', user.pubkey)
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,
hasExtensions: user.extensions,
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)
prvkey: user.prvkey
}
})
// 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
@ -162,17 +141,11 @@ export function useNostrChat() {
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 {
hasUser: true,
hasPubkey: true,
hasPrvkey: false,
message: 'User has Nostr public key but private key not available',
hasPubkey: !!user.pubkey,
hasPrvkey: !!user.prvkey,
message: user.pubkey && user.prvkey ? 'User has complete Nostr keypair' : 'User missing Nostr keys',
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) => {
if (!currentUser.value) {
console.warn('No user logged in - cannot subscribe to peer messages')
return null
console.warn('Cannot subscribe to peer: no user logged in')
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
@ -382,51 +360,32 @@ export function useNostrChat() {
throw new Error('Failed to initialize Nostr pool')
}
const myPubkey = currentUser.value.pubkey
// First, load historical messages
await loadHistoricalMessages(peerPubkey, myPubkey)
// Then subscribe to new messages
const relayConfigs = getRelays()
console.log('Subscribing to new messages for peer:', peerPubkey, 'with filters:', [
{
try {
// Subscribe to direct messages (kind 4) from this peer
const filter = {
kinds: [4],
authors: [peerPubkey],
'#p': [myPubkey]
},
{
kinds: [4],
authors: [myPubkey],
'#p': [peerPubkey]
'#p': [currentUser.value.pubkey], // Messages where we are the recipient
authors: [peerPubkey] // Messages from this specific peer
}
])
const unsubscribe = relayHub.subscribe({
id: `peer-${peerPubkey}-${Date.now()}`,
filters: [
{
kinds: [4],
authors: [peerPubkey],
'#p': [myPubkey]
},
{
kinds: [4],
authors: [myPubkey],
'#p': [peerPubkey]
console.log('Subscribing to peer with filter:', filter)
// Use the relay hub to subscribe
const unsubscribe = relayHub.subscribe({
id: `peer-${peerPubkey}`,
filters: [filter],
onEvent: (event) => {
handleIncomingMessage(event, peerPubkey)
}
],
relays: relayConfigs.map(r => r.url),
onEvent: (event: any) => {
console.log('Received live event:', event.id, 'author:', event.pubkey)
handleIncomingMessage(event, peerPubkey)
},
onEose: () => {
console.log('Subscription closed for peer:', peerPubkey)
}
})
})
return unsubscribe
console.log('Successfully subscribed to peer:', peerPubkey)
return unsubscribe
} catch (error) {
console.error('Failed to subscribe to peer:', error)
throw error
}
}
// Subscribe to a peer for notifications only (without loading full message history)
@ -545,52 +504,38 @@ export function useNostrChat() {
// Handle incoming message
const handleIncomingMessage = async (event: any, peerPubkey: string) => {
console.log('=== HANDLE INCOMING MESSAGE START ===')
console.log('Event ID:', event.id, 'Peer:', peerPubkey)
if (processedMessageIds.value.has(event.id)) {
console.log('Message already processed, skipping:', event.id)
if (!currentUser.value || !currentUser.value.prvkey) {
console.warn('Cannot decrypt message: no private key available')
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 {
// Decrypt the message
// For NIP-04 direct messages, always use peerPubkey as the second argument
// 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:', {
eventId: event.id,
isSentByMe,
eventPubkey: event.pubkey,
myPubkey: currentUser.value!.pubkey,
myPubkey: currentUser.value.pubkey,
peerPubkey,
contentLength: event.content.length
})
const decryptedContent = await nip04.decrypt(
currentUser.value!.prvkey,
currentUser.value.prvkey,
peerPubkey, // Always use peerPubkey for shared secret derivation
event.content
)
console.log('Successfully decrypted message:', {
console.log('Message decrypted successfully:', {
eventId: event.id,
contentLength: decryptedContent.length,
contentPreview: decryptedContent.substring(0, 50) + '...'
isSentByMe
})
// Create chat message
const message: ChatMessage = {
id: event.id,
content: decryptedContent,
@ -599,74 +544,29 @@ export function useNostrChat() {
pubkey: event.pubkey
}
// Add message to the appropriate conversation
// Always use peerPubkey as the conversation key for both sent and received messages
const conversationKey = peerPubkey
if (!messages.value.has(conversationKey)) {
messages.value.set(conversationKey, [])
// Add to messages
if (!messages.value.has(peerPubkey)) {
messages.value.set(peerPubkey, [])
}
messages.value.get(peerPubkey)!.push(message)
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)
// Mark as unread if not sent by us
if (!isSentByMe) {
const unreadData = getUnreadData(peerPubkey)
console.log(`Processing unread message logic for ${peerPubkey}:`, {
messageId: event.id,
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.`)
// For now, just update the unread count
// TODO: Implement proper unread message tracking
const currentCount = unreadCounts.value.get(peerPubkey) || 0
updateUnreadCount(peerPubkey, currentCount + 1)
}
// Update latest message timestamp
updateLatestMessageTimestamp(peerPubkey, event.created_at)
// Trigger callback if set
if (onMessageAdded.value) {
onMessageAdded.value(conversationKey)
onMessageAdded.value(peerPubkey)
}
console.log('Messages for conversation:', messages.value.get(conversationKey)?.map(m => ({
id: m.id,
sent: m.sent,
content: m.content.substring(0, 30) + '...',
timestamp: m.created_at
})))
console.log('Message processed and added to chat:', message)
} catch (error) {
console.error('Failed to decrypt message:', error)
}
@ -674,12 +574,24 @@ export function useNostrChat() {
// Send message to a peer
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) {
console.log('🔍 sendMessage: currentUser.value is null/undefined')
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
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.')
}
@ -877,7 +789,7 @@ export function useNostrChat() {
// State
isConnected: readonly(isConnected),
messages: readonly(messages),
isLoggedIn: readonly(isLoggedIn),
isLoggedIn: readonly(isAuthenticated),
peers: readonly(peers),
// Reactive computed properties

View file

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