diff --git a/src/components/PostEditor/PostRelaySelector.tsx b/src/components/PostEditor/PostRelaySelector.tsx index 7c6ebb2..5a5601b 100644 --- a/src/components/PostEditor/PostRelaySelector.tsx +++ b/src/components/PostEditor/PostRelaySelector.tsx @@ -96,7 +96,11 @@ export default function PostRelaySelector({ ...cacheRelayList.originalRelays .filter(relay => (relay.scope === 'both' || relay.scope === 'write') && isLocalNetworkUrl(relay.url)) .map(relay => relay.url) - ].filter(url => isLocalNetworkUrl(url)) + ].filter(url => { + // Filter out invalid/empty URLs + if (!url || typeof url !== 'string' || url.trim() === '' || url === 'ws://' || url === 'wss://') return false + return isLocalNetworkUrl(url) + }) const existingUrls = new Set(userWriteRelays.map(url => normalizeUrl(url) || url)) const newCacheRelays = cacheRelays .map(url => normalizeUrl(url) || url) diff --git a/src/lib/event-metadata.ts b/src/lib/event-metadata.ts index cedfc47..8ccc3eb 100644 --- a/src/lib/event-metadata.ts +++ b/src/lib/event-metadata.ts @@ -22,7 +22,9 @@ export function getRelayListFromEvent(event?: Event | null, blockedRelays?: stri const normalizedBlockedRelays = (blockedRelays || []).map(url => normalizeUrl(url) || url) event.tags.filter(tagNameEquals('r')).forEach(([, url, type]) => { - if (!url || !isWebsocketUrl(url)) return + // Filter out empty, invalid, or malformed URLs + if (!url || typeof url !== 'string' || url.trim() === '' || url === 'ws://' || url === 'wss://') return + if (!isWebsocketUrl(url)) return const normalizedUrl = normalizeUrl(url) if (!normalizedUrl) return diff --git a/src/services/relay-selection.service.ts b/src/services/relay-selection.service.ts index 21d09db..5588bc2 100644 --- a/src/services/relay-selection.service.ts +++ b/src/services/relay-selection.service.ts @@ -127,6 +127,13 @@ class RelaySelectionService { return this.filterBlockedRelays(deduplicatedRelays, context.blockedRelays) } + /** + * Validate that a URL is a valid, non-empty relay URL + */ + private isValidRelayUrl(url: string | undefined | null): url is string { + return !!(url && typeof url === 'string' && url.trim() !== '' && url !== 'ws://' && url !== 'wss://') + } + /** * Get relay list from IndexedDB cache (kind 10002 and 10432 merged) * If not in cache, fetch from relays before returning empty @@ -162,9 +169,15 @@ class RelaySelectionService { if (cacheRelayListEvent) { const cacheRelayList = getRelayListFromEvent(cacheRelayListEvent) + // Filter out invalid/empty URLs before merging + const validCacheRead = cacheRelayList.read.filter(this.isValidRelayUrl) + const validCacheWrite = cacheRelayList.write.filter(this.isValidRelayUrl) + const validRelayRead = relayList.read.filter(this.isValidRelayUrl) + const validRelayWrite = relayList.write.filter(this.isValidRelayUrl) + // Merge read relays - cache relays first, then others - const mergedRead = [...cacheRelayList.read, ...relayList.read] - const mergedWrite = [...cacheRelayList.write, ...relayList.write] + const mergedRead = [...validCacheRead, ...validRelayRead] + const mergedWrite = [...validCacheWrite, ...validRelayWrite] const mergedOriginalRelays = new Map() // Add cache relay original relays first (prioritized)