From 9f2e5f1e1fc67d3d16526fe4328aca5966d069e0 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 10 Oct 2025 21:15:27 +0200 Subject: [PATCH] fix relay selector for discussions --- .../PostEditor/PostRelaySelector.tsx | 44 ++++++++++++++- src/services/client.service.ts | 54 ++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/components/PostEditor/PostRelaySelector.tsx b/src/components/PostEditor/PostRelaySelector.tsx index 11027d1..9693d40 100644 --- a/src/components/PostEditor/PostRelaySelector.tsx +++ b/src/components/PostEditor/PostRelaySelector.tsx @@ -8,6 +8,9 @@ import { DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { Separator } from '@/components/ui/separator' +import { ExtendedKind } from '@/constants' +import client from '@/services/client.service' +import { isWebsocketUrl, normalizeUrl } from '@/lib/url' import { simplifyUrl } from '@/lib/url' import { useCurrentRelays } from '@/providers/CurrentRelaysProvider' import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' @@ -92,9 +95,46 @@ export default function PostRelaySelector({ setPostTargetItems(Array.from(new Set(openFrom)).map((url) => ({ type: 'relay', url }))) return } - // Privacy: Default to write relays, never parent event's relays + + // Check if we're replying to a discussion or comment that requires specific relay routing + if (_parentEvent && (_parentEvent.kind === ExtendedKind.DISCUSSION || _parentEvent.kind === ExtendedKind.COMMENT)) { + let relayHint: string | undefined + + if (_parentEvent.kind === ExtendedKind.COMMENT) { + // For kind 1111 (COMMENT): look for 'E' tag which points to the root event + const ETag = _parentEvent.tags.find(tag => tag[0] === 'E') + if (ETag && ETag[2]) { + relayHint = ETag[2] // Relay hint is the 3rd element + } + + // If no 'E' tag, check lowercase 'e' tag for parent event + if (!relayHint) { + const eTag = _parentEvent.tags.find(tag => tag[0] === 'e') + if (eTag && eTag[2]) { + relayHint = eTag[2] + } + } + } else if (_parentEvent.kind === ExtendedKind.DISCUSSION) { + // For kind 11 (DISCUSSION): get relay hint from where it was found + const eventHints = client.getEventHints(_parentEvent.id) + if (eventHints.length > 0) { + relayHint = eventHints[0] + } + } + + // If we found a valid relay hint, use it instead of write relays + if (relayHint && isWebsocketUrl(relayHint)) { + const normalizedRelayHint = normalizeUrl(relayHint) + if (normalizedRelayHint) { + setPostTargetItems([{ type: 'relay', url: normalizedRelayHint }]) + return + } + } + } + + // Default to write relays for all other cases setPostTargetItems([{ type: 'writeRelays' }]) - }, [openFrom]) + }, [openFrom, _parentEvent]) useEffect(() => { const isProtectedEvent = postTargetItems.every((item) => item.type !== 'writeRelays') diff --git a/src/services/client.service.ts b/src/services/client.service.ts index 7f593ce..a546e4f 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -97,7 +97,59 @@ class ClientService extends EventTarget { // Check if this is a discussion thread or reply to a discussion const isDiscussionRelated = event.kind === ExtendedKind.DISCUSSION || - event.tags.some(tag => tag[0] === 'k' && tag[1] === '11') + event.tags.some(tag => tag[0] === 'k' && tag[1] === String(ExtendedKind.DISCUSSION)) + + // Special handling for kind 11 (DISCUSSION) and kind 1111 (COMMENT/NIP-22) + // These should only be published to the relay where the original event was found + // or to the relay hint specified in the event tags + if (event.kind === ExtendedKind.DISCUSSION || event.kind === ExtendedKind.COMMENT) { + let rootEventId: string | undefined + let relayHint: string | undefined + + if (event.kind === ExtendedKind.COMMENT) { + // Kind 1111 (NIP-22 Comment): look for 'E' tag which points to the root event + // Format: ["E", "", "", ""] + const ETag = event.tags.find(tag => tag[0] === 'E') + if (ETag) { + rootEventId = ETag[1] + relayHint = ETag[2] // Relay hint is the 3rd element + } + + // If no 'E' tag, check lowercase 'e' tag for parent event + // This handles cases where we're replying to a reply + if (!rootEventId) { + const eTag = event.tags.find(tag => tag[0] === 'e') + if (eTag) { + rootEventId = eTag[1] + relayHint = eTag[2] + } + } + } else if (event.kind === ExtendedKind.DISCUSSION) { + // Kind 11 (DISCUSSION): this is the root event itself + // For new root events, we can use the specified relays or fall through to normal handling + // But for replies TO kind 11, we should have caught them as kind 1111 above + rootEventId = event.id + } + + // Priority 1: Use relay hint from the tag if present and valid + if (relayHint && isWebsocketUrl(relayHint)) { + const normalizedRelayHint = normalizeUrl(relayHint) + if (normalizedRelayHint) { + relays = [normalizedRelayHint] + return relays + } + } + + // Priority 2: Get relay where the root event was found + if (rootEventId) { + const originalEventRelays = this.getEventHints(rootEventId) + if (originalEventRelays.length > 0) { + // Only publish to the relay(s) where the original event was found + relays = originalEventRelays.slice(0, 1) // Use only the first relay for insular discussions + return relays + } + } + } // Publish to mentioned users' inboxes for all events EXCEPT discussions if (!isDiscussionRelated) {