mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-15 17:19:27 +02:00
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
This commit is contained in:
@@ -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<any>(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() {
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
@@ -136,6 +168,17 @@ export function InboxViewer() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
Decrypted
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 pt-0">
|
||||
<div className="text-2xl font-bold">{decryptedCount ?? 0}</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
@@ -295,6 +338,104 @@ export function InboxViewer() {
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Relay Status */}
|
||||
{privateMessagesEnabled &&
|
||||
loaderState?.relays &&
|
||||
loaderState.relays.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Radio className="h-4 w-4" />
|
||||
<CardTitle className="text-base">DM Relays</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 pt-0">
|
||||
<div className="space-y-1">
|
||||
{loaderState.relays.map((relay: string) => (
|
||||
<div
|
||||
key={relay}
|
||||
className="text-sm font-mono text-muted-foreground flex items-center gap-2"
|
||||
>
|
||||
<div className="h-2 w-2 rounded-full bg-green-500" />
|
||||
{relay}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Debug Info */}
|
||||
{privateMessagesEnabled && (
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="h-4 w-4" />
|
||||
<CardTitle className="text-base">Debug Info</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 pt-0 space-y-2">
|
||||
<div className="text-sm space-y-1">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Loader Enabled:</span>
|
||||
<span className="font-mono">
|
||||
{loaderState?.enabled ? "Yes" : "No"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Auto-Decrypt:</span>
|
||||
<span className="font-mono">
|
||||
{loaderState?.autoDecrypt ? "Yes" : "No"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Loading:</span>
|
||||
<span className="font-mono">
|
||||
{loaderState?.loading ? "Yes" : "No"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Error Count:</span>
|
||||
<span className="font-mono">
|
||||
{loaderState?.errorCount ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Last Sync:</span>
|
||||
<span className="font-mono text-xs">
|
||||
{loaderState?.lastSync
|
||||
? formatDistanceToNow(loaderState.lastSync, {
|
||||
addSuffix: true,
|
||||
})
|
||||
: "Never"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Conversations List */}
|
||||
{privateMessagesEnabled &&
|
||||
conversations &&
|
||||
conversations.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<MessageSquare className="h-4 w-4" />
|
||||
<CardTitle className="text-base">Conversations</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 pt-0">
|
||||
<div className="space-y-2">
|
||||
{conversations.map((conv) => (
|
||||
<ConversationItem key={conv.id} conversation={conv} />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Help Text */}
|
||||
{!privateMessagesEnabled && (
|
||||
<div className="text-sm text-muted-foreground space-y-2">
|
||||
@@ -316,3 +457,37 @@ export function InboxViewer() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ConversationItem({ conversation }: { conversation: any }) {
|
||||
const profile = useProfile(conversation.senderPubkey);
|
||||
const displayName = getDisplayName(conversation.senderPubkey, profile);
|
||||
|
||||
return (
|
||||
<div className="flex items-start gap-3 p-3 rounded-lg border hover:bg-accent cursor-pointer">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="font-medium truncate">{displayName}</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{formatDistanceToNow(conversation.lastMessageCreatedAt * 1000, {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground truncate">
|
||||
{conversation.lastMessagePreview}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{conversation.messageCount} message
|
||||
{conversation.messageCount === 1 ? "" : "s"}
|
||||
</span>
|
||||
{conversation.unreadCount > 0 && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded-full bg-primary text-primary-foreground font-medium">
|
||||
{conversation.unreadCount} new
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user