From e7cb7342aa652921341fc46380e92282f14a1a92 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sun, 23 Feb 2025 15:58:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8F=97=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/event.ts | 59 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/lib/event.ts b/src/lib/event.ts index 695e1bc4..5cdd3335 100644 --- a/src/lib/event.ts +++ b/src/lib/event.ts @@ -1,11 +1,14 @@ import { BIG_RELAY_URLS, COMMENT_EVENT_KIND, PICTURE_EVENT_KIND } from '@/constants' import client from '@/services/client.service' import { TImageInfo, TRelayList } from '@/types' +import { LRUCache } from 'lru-cache' import { Event, kinds, nip19 } from 'nostr-tools' import { formatPubkey } from './pubkey' import { extractImageInfoFromTag, isReplyETag, isRootETag, tagNameEquals } from './tag' import { isWebsocketUrl, normalizeHttpUrl, normalizeUrl } from './url' +const EVENT_EMBEDDED_EVENT_IDS_CACHE = new LRUCache({ max: 10000 }) + export function isNsfwEvent(event: Event) { return event.tags.some( ([tagName, tagValue]) => @@ -16,21 +19,15 @@ export function isNsfwEvent(event: Event) { export function isReplyNoteEvent(event: Event) { if (event.kind !== kinds.ShortTextNote) return false - let hasETag = false - let hasMentionMarker = false - for (const [tagName, , , marker] of event.tags) { - if (tagName !== 'e') continue - hasETag = true - - if (!marker) continue - if (marker === 'mention') { - hasMentionMarker = true - continue - } + const mentionsEventIds: string[] = [] + for (const [tagName, eventId, , marker] of event.tags) { + if (tagName !== 'e' || !eventId) continue + mentionsEventIds.push(eventId) if (['root', 'reply'].includes(marker)) return true } - return hasETag && !hasMentionMarker + const embeddedEventIds = extractEmbeddedEventIds(event) + return mentionsEventIds.some((id) => !embeddedEventIds.includes(id)) } export function isCommentEvent(event: Event) { @@ -50,8 +47,14 @@ export function isSupportedKind(kind: number) { } export function getParentEventId(event?: Event) { - if (!event || !isReplyNoteEvent(event)) return undefined - const tag = event.tags.find(isReplyETag) ?? event.tags.find(tagNameEquals('e')) + if (!event) return undefined + let tag = event.tags.find(isReplyETag) + if (!tag) { + const embeddedEventIds = extractEmbeddedEventIds(event) + tag = event.tags.findLast( + ([tagName, tagValue]) => tagName === 'e' && !embeddedEventIds.includes(tagValue) + ) + } if (!tag) return undefined try { @@ -63,8 +66,14 @@ export function getParentEventId(event?: Event) { } export function getRootEventId(event?: Event) { - if (!event || !isReplyNoteEvent(event)) return undefined - const tag = event.tags.find(isRootETag) + if (!event) return undefined + let tag = event.tags.find(isRootETag) + if (!tag) { + const embeddedEventIds = extractEmbeddedEventIds(event) + tag = event.tags.find( + ([tagName, tagValue]) => tagName === 'e' && !embeddedEventIds.includes(tagValue) + ) + } if (!tag) return undefined try { @@ -312,6 +321,24 @@ export function extractEmbeddedNotesFromContent(content: string) { return { embeddedNotes, contentWithoutEmbeddedNotes: c } } +export function extractEmbeddedEventIds(event: Event) { + const cache = EVENT_EMBEDDED_EVENT_IDS_CACHE.get(event.id) + if (cache) return cache + + const embeddedEventIds: string[] = [] + const embeddedNoteRegex = /nostr:(note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g + ;(event.content.match(embeddedNoteRegex) || []).forEach((note) => { + const { type, data } = nip19.decode(note.split(':')[1]) + if (type === 'nevent') { + embeddedEventIds.push(data.id) + } else if (type === 'note') { + embeddedEventIds.push(data) + } + }) + EVENT_EMBEDDED_EVENT_IDS_CACHE.set(event.id, embeddedEventIds) + return embeddedEventIds +} + export function getLatestEvent(events: Event[]) { return events.sort((a, b) => b.created_at - a.created_at)[0] }