|
|
|
|
@ -1,11 +1,14 @@
@@ -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<string, string[]>({ max: 10000 }) |
|
|
|
|
|
|
|
|
|
export function isNsfwEvent(event: Event) { |
|
|
|
|
return event.tags.some( |
|
|
|
|
([tagName, tagValue]) => |
|
|
|
|
@ -16,21 +19,15 @@ export function isNsfwEvent(event: Event) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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] |
|
|
|
|
} |
|
|
|
|
|