From f9356e228e166eb433991e44bfc68811014ee918 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Jan 2026 21:53:52 +0000 Subject: [PATCH] feat: Enhanced inbox UI with relay status, conversations list, and debugging This commit adds comprehensive UI improvements and debugging capabilities to the NIP-59 inbox: InboxViewer enhancements: - Added "Decrypted" count stat (4th column) - Added "DM Relays" section showing which relays are being used for gift wraps - Added "Conversations" list displaying decrypted messages with sender, preview, timestamp, and unread count - Added "Debug Info" section with loader state (enabled, auto-decrypt, loading, error count, last sync) - Added proper RxJS subscription to loader state using useEffect Gift-wrap-loader improvements: - Added `relays` field to GiftWrapLoaderState to track which relays are in use - Update state with relay list during sync - Enhanced console logging to show relay URLs Gift-wrap debugging: - Added console.log when creating new conversations to help debug conversation creation This makes it much easier to: - See which DM relays are being used - View decrypted conversations and messages - Debug issues with message decryption and relay connectivity - Track loader state and sync status --- src/components/InboxViewer.tsx | 179 ++++++++++++++++++++++++++++++- src/services/gift-wrap-loader.ts | 11 +- src/services/gift-wrap.ts | 4 + 3 files changed, 191 insertions(+), 3 deletions(-) diff --git a/src/components/InboxViewer.tsx b/src/components/InboxViewer.tsx index c362d20..77507cb 100644 --- a/src/components/InboxViewer.tsx +++ b/src/components/InboxViewer.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useLiveQuery } from "dexie-react-hooks"; import { Mail, @@ -8,15 +8,23 @@ import { Loader2, CheckCircle, XCircle, + MessageSquare, + Radio, + Database, } from "lucide-react"; import { useGrimoire } from "@/core/state"; import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"; import { Button } from "./ui/button"; import { Switch } from "./ui/switch"; import giftWrapLoader from "@/services/gift-wrap-loader"; +import { getConversations } from "@/services/gift-wrap"; +import db from "@/services/db"; import { toast } from "sonner"; import { use$ } from "applesauce-react/hooks"; import accounts from "@/services/accounts"; +import { useProfile } from "@/hooks/useProfile"; +import { getDisplayName } from "@/lib/nostr-utils"; +import { formatDistanceToNow } from "date-fns"; export function InboxViewer() { const { state, setPrivateMessagesEnabled, setAutoDecryptGiftWraps } = @@ -51,6 +59,30 @@ export function InboxViewer() { return giftWrapLoader.getUnreadCount(activeAccount.pubkey); }, [activeAccount?.pubkey]); + // Get conversations + const conversations = useLiveQuery(async () => { + if (!activeAccount?.pubkey) return []; + return getConversations(activeAccount.pubkey); + }, [activeAccount?.pubkey]); + + // Get decrypted messages count + const decryptedCount = useLiveQuery(async () => { + if (!activeAccount?.pubkey) return 0; + return db.decryptedRumors + .where("recipientPubkey") + .equals(activeAccount.pubkey) + .count(); + }, [activeAccount?.pubkey]); + + // Get loader state for relay info + const [loaderState, setLoaderState] = useState(null); + + // Subscribe to loader state + useEffect(() => { + const subscription = giftWrapLoader.state.subscribe(setLoaderState); + return () => subscription.unsubscribe(); + }, []); + const handleTogglePrivateMessages = (enabled: boolean) => { setPrivateMessagesEnabled(enabled); @@ -124,7 +156,7 @@ export function InboxViewer() { {/* Stats */} -
+
@@ -136,6 +168,17 @@ export function InboxViewer() { + + + + Decrypted + + + +
{decryptedCount ?? 0}
+
+
+ @@ -295,6 +338,104 @@ export function InboxViewer() { )} + {/* Relay Status */} + {privateMessagesEnabled && + loaderState?.relays && + loaderState.relays.length > 0 && ( + + +
+ + DM Relays +
+
+ +
+ {loaderState.relays.map((relay: string) => ( +
+
+ {relay} +
+ ))} +
+ + + )} + + {/* Debug Info */} + {privateMessagesEnabled && ( + + +
+ + Debug Info +
+
+ +
+
+ Loader Enabled: + + {loaderState?.enabled ? "Yes" : "No"} + +
+
+ Auto-Decrypt: + + {loaderState?.autoDecrypt ? "Yes" : "No"} + +
+
+ Loading: + + {loaderState?.loading ? "Yes" : "No"} + +
+
+ Error Count: + + {loaderState?.errorCount ?? 0} + +
+
+ Last Sync: + + {loaderState?.lastSync + ? formatDistanceToNow(loaderState.lastSync, { + addSuffix: true, + }) + : "Never"} + +
+
+
+
+ )} + + {/* Conversations List */} + {privateMessagesEnabled && + conversations && + conversations.length > 0 && ( + + +
+ + Conversations +
+
+ +
+ {conversations.map((conv) => ( + + ))} +
+
+
+ )} + {/* Help Text */} {!privateMessagesEnabled && (
@@ -316,3 +457,37 @@ export function InboxViewer() {
); } + +function ConversationItem({ conversation }: { conversation: any }) { + const profile = useProfile(conversation.senderPubkey); + const displayName = getDisplayName(conversation.senderPubkey, profile); + + return ( +
+
+
+
{displayName}
+
+ {formatDistanceToNow(conversation.lastMessageCreatedAt * 1000, { + addSuffix: true, + })} +
+
+
+ {conversation.lastMessagePreview} +
+
+ + {conversation.messageCount} message + {conversation.messageCount === 1 ? "" : "s"} + + {conversation.unreadCount > 0 && ( + + {conversation.unreadCount} new + + )} +
+
+
+ ); +} diff --git a/src/services/gift-wrap-loader.ts b/src/services/gift-wrap-loader.ts index 3a42420..585eeb1 100644 --- a/src/services/gift-wrap-loader.ts +++ b/src/services/gift-wrap-loader.ts @@ -35,6 +35,7 @@ interface GiftWrapLoaderState { recipientPubkey?: string; lastSync?: number; errorCount: number; + relays: string[]; // Relays being used for gift wraps } /** @@ -47,6 +48,7 @@ class GiftWrapLoader { autoDecrypt: false, loading: false, errorCount: 0, + relays: [], }); private subscription?: { unsubscribe: () => void }; @@ -151,9 +153,16 @@ class GiftWrapLoader { } console.log( - `[GiftWrapLoader] Syncing from ${inboxRelays.length} inbox relays`, + `[GiftWrapLoader] Syncing from ${inboxRelays.length} inbox relays:`, + inboxRelays, ); + // Update state with relays being used + this.state$.next({ + ...this.state$.value, + relays: inboxRelays, + }); + // Subscribe to kind 1059 events for this user using timeline loader const filter = { kinds: [1059 as number], diff --git a/src/services/gift-wrap.ts b/src/services/gift-wrap.ts index e4224ea..24fb737 100644 --- a/src/services/gift-wrap.ts +++ b/src/services/gift-wrap.ts @@ -322,6 +322,10 @@ async function updateConversationMetadata( updatedAt: Date.now(), }; await db.conversations.put(conversation); + console.log( + `[GiftWrap] Created new conversation ${conversationId}`, + conversation, + ); } else { // Update existing conversation if this is newer if (rumor.rumorCreatedAt > existing.lastMessageCreatedAt) {