mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-05 02:01:22 +02:00
fix: Fix NIP-17 message sending and display
Two critical fixes for gift-wrapped DMs: 1. Fix publishEvent to accept optional relays parameter - SendWrappedMessage was passing inbox relays but they were ignored - Now gift wraps are correctly published to inbox relays 2. Make NIP-17 adapter a singleton - All components now share the same giftWraps$ state - Sent messages appear immediately as the state is shared - Export nip17Adapter singleton from the module - Update all usages in ChatViewer, InboxViewer, RelaysDropdown, chat-parser
This commit is contained in:
@@ -23,7 +23,7 @@ import type {
|
||||
// import { NipC7Adapter } from "@/lib/chat/adapters/nip-c7-adapter"; // Coming soon
|
||||
import { Nip29Adapter } from "@/lib/chat/adapters/nip-29-adapter";
|
||||
import { Nip53Adapter } from "@/lib/chat/adapters/nip-53-adapter";
|
||||
import { Nip17Adapter } from "@/lib/chat/adapters/nip-17-adapter";
|
||||
import { nip17Adapter } from "@/lib/chat/adapters/nip-17-adapter";
|
||||
import type { ChatProtocolAdapter } from "@/lib/chat/adapters/base-adapter";
|
||||
import type { Message } from "@/types/chat";
|
||||
import type { ChatAction } from "@/types/chat-actions";
|
||||
@@ -960,7 +960,7 @@ function getAdapter(protocol: ChatProtocol): ChatProtocolAdapter {
|
||||
case "nip-29":
|
||||
return new Nip29Adapter();
|
||||
case "nip-17":
|
||||
return new Nip17Adapter();
|
||||
return nip17Adapter;
|
||||
// case "nip-28": // Phase 3 - Public channels (coming soon)
|
||||
// return new Nip28Adapter();
|
||||
case "nip-53":
|
||||
|
||||
@@ -31,7 +31,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
|
||||
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
|
||||
import { Nip17Adapter } from "@/lib/chat/adapters/nip-17-adapter";
|
||||
import { nip17Adapter } from "@/lib/chat/adapters/nip-17-adapter";
|
||||
import { useProfile } from "@/hooks/useProfile";
|
||||
import { getDisplayName } from "@/lib/nostr-utils";
|
||||
|
||||
@@ -258,8 +258,8 @@ export function InboxViewer() {
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [isDecrypting, setIsDecrypting] = useState(false);
|
||||
|
||||
// NIP-17 adapter instance
|
||||
const adapter = useMemo(() => new Nip17Adapter(), []);
|
||||
// NIP-17 adapter singleton instance
|
||||
const adapter = nip17Adapter;
|
||||
|
||||
// Get pending count
|
||||
const pendingCount = use$(() => adapter.getPendingCount$(), [adapter]) ?? 0;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useRelayState } from "@/hooks/useRelayState";
|
||||
import { getConnectionIcon, getAuthIcon } from "@/lib/relay-status-utils";
|
||||
import { normalizeRelayURL } from "@/lib/relay-url";
|
||||
import type { Conversation } from "@/types/chat";
|
||||
import { Nip17Adapter } from "@/lib/chat/adapters/nip-17-adapter";
|
||||
import { nip17Adapter } from "@/lib/chat/adapters/nip-17-adapter";
|
||||
|
||||
interface RelaysDropdownProps {
|
||||
conversation: Conversation;
|
||||
@@ -38,7 +38,6 @@ export function RelaysDropdown({ conversation }: RelaysDropdownProps) {
|
||||
useEffect(() => {
|
||||
if (conversation.protocol !== "nip-17") return;
|
||||
|
||||
const adapter = new Nip17Adapter();
|
||||
const participants = conversation.participants;
|
||||
|
||||
// Initialize with loading state
|
||||
@@ -55,7 +54,7 @@ export function RelaysDropdown({ conversation }: RelaysDropdownProps) {
|
||||
const results = await Promise.all(
|
||||
participants.map(async (p) => {
|
||||
try {
|
||||
const relays = await adapter.getInboxRelays(p.pubkey);
|
||||
const relays = await nip17Adapter.getInboxRelays(p.pubkey);
|
||||
return { pubkey: p.pubkey, relays, loading: false };
|
||||
} catch {
|
||||
return { pubkey: p.pubkey, relays: [], loading: false };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ChatCommandResult, GroupListIdentifier } from "@/types/chat";
|
||||
// import { NipC7Adapter } from "./chat/adapters/nip-c7-adapter";
|
||||
import { Nip17Adapter } from "./chat/adapters/nip-17-adapter";
|
||||
import { nip17Adapter } from "./chat/adapters/nip-17-adapter";
|
||||
import { Nip29Adapter } from "./chat/adapters/nip-29-adapter";
|
||||
import { Nip53Adapter } from "./chat/adapters/nip-53-adapter";
|
||||
import { nip19 } from "nostr-tools";
|
||||
@@ -61,8 +61,9 @@ export function parseChatCommand(args: string[]): ChatCommandResult {
|
||||
}
|
||||
|
||||
// Try each adapter in priority order
|
||||
// NIP-17 uses singleton to share gift wrap state across app
|
||||
const adapters = [
|
||||
new Nip17Adapter(), // NIP-17 - Private DMs (gift wrapped)
|
||||
nip17Adapter, // NIP-17 - Private DMs (gift wrapped) - singleton
|
||||
// new Nip28Adapter(), // NIP-28 - Public channels (coming soon)
|
||||
new Nip29Adapter(), // NIP-29 - Relay groups
|
||||
new Nip53Adapter(), // NIP-53 - Live activity chat
|
||||
|
||||
@@ -781,4 +781,21 @@ export class Nip17Adapter extends ChatProtocolAdapter {
|
||||
defaultValue: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a gift wrap event directly to local state
|
||||
* Used for optimistic updates after sending
|
||||
*/
|
||||
addGiftWrapLocally(giftWrap: NostrEvent): void {
|
||||
const current = this.giftWraps$.value;
|
||||
if (!current.find((g) => g.id === giftWrap.id)) {
|
||||
this.giftWraps$.next([...current, giftWrap]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton instance for shared state across the app
|
||||
* All components should use this to ensure gift wraps are shared
|
||||
*/
|
||||
export const nip17Adapter = new Nip17Adapter();
|
||||
|
||||
@@ -8,30 +8,40 @@ import type { NostrEvent } from "nostr-tools/core";
|
||||
import accountManager from "./accounts";
|
||||
|
||||
/**
|
||||
* Publishes a Nostr event to relays using the author's outbox relays
|
||||
* Publishes a Nostr event to relays
|
||||
* If relays are provided, uses those; otherwise uses author's outbox relays
|
||||
* Falls back to seen relays from the event if no relay list found
|
||||
*
|
||||
* @param event - The signed Nostr event to publish
|
||||
* @param relays - Optional specific relays to publish to (used for gift wraps to inbox relays)
|
||||
*/
|
||||
export async function publishEvent(event: NostrEvent): Promise<void> {
|
||||
// Try to get author's outbox relays from EventStore (kind 10002)
|
||||
let relays = await relayListCache.getOutboxRelays(event.pubkey);
|
||||
export async function publishEvent(
|
||||
event: NostrEvent,
|
||||
relays?: string[],
|
||||
): Promise<void> {
|
||||
let targetRelays = relays;
|
||||
|
||||
// Fallback to relays from the event itself (where it was seen)
|
||||
if (!relays || relays.length === 0) {
|
||||
const seenRelays = getSeenRelays(event);
|
||||
relays = seenRelays ? Array.from(seenRelays) : [];
|
||||
// If no specific relays provided, use author's outbox relays
|
||||
if (!targetRelays || targetRelays.length === 0) {
|
||||
targetRelays =
|
||||
(await relayListCache.getOutboxRelays(event.pubkey)) ?? undefined;
|
||||
|
||||
// Fallback to relays from the event itself (where it was seen)
|
||||
if (!targetRelays || targetRelays.length === 0) {
|
||||
const seenRelays = getSeenRelays(event);
|
||||
targetRelays = seenRelays ? Array.from(seenRelays) : [];
|
||||
}
|
||||
}
|
||||
|
||||
// If still no relays, throw error
|
||||
if (relays.length === 0) {
|
||||
if (targetRelays.length === 0) {
|
||||
throw new Error(
|
||||
"No relays found for publishing. Please configure relay list (kind 10002) or ensure event has relay hints.",
|
||||
);
|
||||
}
|
||||
|
||||
// Publish to relay pool
|
||||
await pool.publish(relays, event);
|
||||
await pool.publish(targetRelays, event);
|
||||
|
||||
// Add to EventStore for immediate local availability
|
||||
eventStore.add(event);
|
||||
|
||||
Reference in New Issue
Block a user