From 5d211720176e15ae846148514528e96c152e1041 Mon Sep 17 00:00:00 2001 From: codytseng Date: Thu, 6 Feb 2025 22:52:44 +0800 Subject: [PATCH] feat: add hint to e tags --- src/lib/draft-event.ts | 25 ++++++++++++--------- src/lib/url.ts | 41 ++++++++++++++++++++++++++++++++++ src/services/client.service.ts | 6 +++++ 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/lib/draft-event.ts b/src/lib/draft-event.ts index ed20a497..31baa97b 100644 --- a/src/lib/draft-event.ts +++ b/src/lib/draft-event.ts @@ -1,4 +1,5 @@ import { COMMENT_EVENT_KIND, PICTURE_EVENT_KIND } from '@/constants' +import client from '@/services/client.service' import { TDraftEvent, TMailboxRelay, TRelaySet } from '@/types' import dayjs from 'dayjs' import { Event, kinds } from 'nostr-tools' @@ -14,12 +15,14 @@ import { // https://github.com/nostr-protocol/nips/blob/master/25.md export function createReactionDraftEvent(event: Event): TDraftEvent { const tags = event.tags.filter((tag) => tag.length >= 2 && ['e', 'p'].includes(tag[0])) - tags.push(['e', event.id]) + + const hint = client.getEventHint(event.id) + tags.push(['e', event.id, hint, event.pubkey]) tags.push(['p', event.pubkey]) tags.push(['k', event.kind.toString()]) if (isReplaceable(event.kind)) { - tags.push(['a', getEventCoordinate(event)]) + tags.push(hint ? ['a', getEventCoordinate(event), hint] : ['a', getEventCoordinate(event)]) } return { @@ -33,7 +36,7 @@ export function createReactionDraftEvent(event: Event): TDraftEvent { // https://github.com/nostr-protocol/nips/blob/master/18.md export function createRepostDraftEvent(event: Event): TDraftEvent { const tags = [ - ['e', event.id], // TODO: url + ['e', event.id, client.getEventHint(event.id), event.pubkey], ['p', event.pubkey] ] @@ -60,16 +63,16 @@ export async function createShortTextNoteDraftEvent( const tags = pubkeys .map((pubkey) => ['p', pubkey]) - .concat(otherRelatedEventIds.map((eventId) => ['e', eventId])) - .concat(quoteEventIds.map((eventId) => ['q', eventId, '', 'mention'])) + .concat(otherRelatedEventIds.map((eventId) => ['e', eventId, client.getEventHint(eventId)])) + .concat(quoteEventIds.map((eventId) => ['q', eventId, client.getEventHint(eventId)])) .concat(hashtags.map((hashtag) => ['t', hashtag])) if (rootEventId) { - tags.push(['e', rootEventId, '', 'root']) + tags.push(['e', rootEventId, client.getEventHint(rootEventId), 'root']) } if (parentEventId) { - tags.push(['e', parentEventId, '', 'reply']) + tags.push(['e', parentEventId, client.getEventHint(parentEventId), 'reply']) } const { images } = extractImagesFromContent(content) @@ -124,7 +127,7 @@ export async function createPictureNoteDraftEvent( const tags = pictureInfos .map((info) => ['imeta', ...info.tags.map(([n, v]) => `${n} ${v}`)]) .concat(pubkeys.map((pubkey) => ['p', pubkey])) - .concat(quoteEventIds.map((eventId) => ['q', eventId])) + .concat(quoteEventIds.map((eventId) => ['q', eventId, client.getEventHint(eventId)])) .concat(hashtags.map((hashtag) => ['t', hashtag])) if (options.addClientTag) { @@ -165,15 +168,15 @@ export async function createCommentDraftEvent( const hashtags = extractHashtags(content) const tags = [ - ['E', rootEventId], + ['E', rootEventId, client.getEventHint(rootEventId), rootEventPubkey], ['K', rootEventKind.toString()], ['P', rootEventPubkey], - ['e', parentEventId], + ['e', parentEventId, client.getEventHint(parentEventId), parentEventPubkey], ['k', parentEventKind.toString()], ['p', parentEventPubkey] ] .concat(pubkeys.map((pubkey) => ['p', pubkey])) - .concat(quoteEventIds.map((eventId) => ['q', eventId])) + .concat(quoteEventIds.map((eventId) => ['q', eventId, client.getEventHint(eventId)])) .concat(hashtags.map((hashtag) => ['t', hashtag])) const { images } = extractImagesFromContent(content) diff --git a/src/lib/url.ts b/src/lib/url.ts index cb8e2690..fc659825 100644 --- a/src/lib/url.ts +++ b/src/lib/url.ts @@ -31,6 +31,47 @@ export function simplifyUrl(url: string): string { return url.replace('wss://', '').replace('ws://', '').replace(/\/$/, '') } +export function isLocalNetworkUrl(urlString: string): boolean { + try { + const url = new URL(urlString) + const hostname = url.hostname + + // Check if it's localhost + if (hostname === 'localhost' || hostname === '::1') { + return true + } + + // Check if it's an IPv4 local network address + const ipv4Match = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) + if (ipv4Match) { + const [, a, b, c, d] = ipv4Match.map(Number) + return ( + a === 10 || + (a === 172 && b >= 16 && b <= 31) || + (a === 192 && b === 168) || + (a === 127 && b === 0 && c === 0 && d === 1) + ) + } + + // Check if it's an IPv6 address + if (hostname.includes(':')) { + if (hostname === '::1') { + return true // IPv6 loopback address + } + if (hostname.startsWith('fe80:')) { + return true // Link-local address + } + if (hostname.startsWith('fc') || hostname.startsWith('fd')) { + return true // Unique local address (ULA) + } + } + + return false + } catch { + return false // Return false for invalid URLs + } +} + export function isImage(url: string) { try { const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.heic', '.svg'] diff --git a/src/services/client.service.ts b/src/services/client.service.ts index ac29aa11..7886a194 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -2,6 +2,7 @@ import { BIG_RELAY_URLS } from '@/constants' import { getProfileFromProfileEvent, getRelayListFromRelayListEvent } from '@/lib/event' import { formatPubkey, userIdToPubkey } from '@/lib/pubkey' import { extractPubkeysFromEventTags } from '@/lib/tag' +import { isLocalNetworkUrl } from '@/lib/url' import { TDraftEvent, TProfile, TRelayInfo, TRelayList } from '@/types' import { sha256 } from '@noble/hashes/sha2' import DataLoader from 'dataloader' @@ -559,6 +560,11 @@ class ClientService extends EventTarget { return this.getSeenEventRelays(eventId).map((relay) => relay.url) } + getEventHint(eventId: string) { + const relayUrls = this.getSeenEventRelayUrls(eventId) + return relayUrls.find((url) => !isLocalNetworkUrl(url)) ?? '' + } + trackEventSeenOn(eventId: string, relay: AbstractRelay) { let set = this.pool.seenOn.get(eventId) if (!set) {