diff --git a/src/components/ParentNotePreview/index.tsx b/src/components/ParentNotePreview/index.tsx index 4fa6e76a..d9694f9a 100644 --- a/src/components/ParentNotePreview/index.tsx +++ b/src/components/ParentNotePreview/index.tsx @@ -1,5 +1,6 @@ import { Skeleton } from '@/components/ui/skeleton' import { useFetchEvent } from '@/hooks' +import { isSupportedKind } from '@/lib/event' import { cn } from '@/lib/utils' import { useMuteList } from '@/providers/MuteListProvider' import { useMemo } from 'react' @@ -68,6 +69,8 @@ export default function ParentNotePreview({ {event && } {isMuted ? (
[{t('This user has been muted')}]
+ ) : !isSupportedKind(event.kind) ? ( +
[{t('Cannot handle event of kind k', { k: event.kind })}]
) : ( )} diff --git a/src/components/PostEditor/Uploader.tsx b/src/components/PostEditor/Uploader.tsx index be42bd09..4a731306 100644 --- a/src/components/PostEditor/Uploader.tsx +++ b/src/components/PostEditor/Uploader.tsx @@ -24,7 +24,6 @@ export default function Uploader({ try { for (const file of event.target.files) { const result = await mediaUpload.upload(file) - console.log('File uploaded successfully', result) onUploadSuccess(result) } } catch (error) { diff --git a/src/components/ReplyNoteList/index.tsx b/src/components/ReplyNoteList/index.tsx index 570eb349..88ebd5e1 100644 --- a/src/components/ReplyNoteList/index.tsx +++ b/src/components/ReplyNoteList/index.tsx @@ -1,8 +1,11 @@ import { BIG_RELAY_URLS, ExtendedKind } from '@/constants' import { + getEventCoordinate, getParentEventTag, + getRootAddressableEventTag, getRootEventHexId, getRootEventTag, + isReplaceable, isReplyNoteEvent } from '@/lib/event' import { generateEventIdFromETag, tagNameEquals } from '@/lib/tag' @@ -16,7 +19,10 @@ import { useTranslation } from 'react-i18next' import { LoadingBar } from '../LoadingBar' import ReplyNote, { ReplyNoteSkeleton } from '../ReplyNote' -type TRootInfo = { type: 'event'; id: string; pubkey: string } | { type: 'I'; id: string } +type TRootInfo = + | { type: 'E'; id: string; pubkey: string } + | { type: 'A'; id: string; eventId: string; pubkey: string; relay?: string } + | { type: 'I'; id: string } const LIMIT = 100 const SHOW_COUNT = 10 @@ -30,7 +36,8 @@ export default function ReplyNoteList({ index, event }: { index?: number; event: const replies = useMemo(() => { const replyIdSet = new Set() const replyEvents: NEvent[] = [] - let parentEventIds = [event.id] + const currentEventId = isReplaceable(event.kind) ? getEventCoordinate(event) : event.id + let parentEventIds = [currentEventId] while (parentEventIds.length > 0) { const events = parentEventIds.flatMap((id) => repliesMap.get(id)?.events || []) events.forEach((evt) => { @@ -52,22 +59,36 @@ export default function ReplyNoteList({ index, event }: { index?: number; event: useEffect(() => { const fetchRootEvent = async () => { - let root: TRootInfo = { type: 'event', id: event.id, pubkey: event.pubkey } + let root: TRootInfo = isReplaceable(event.kind) + ? { + type: 'A', + id: getEventCoordinate(event), + eventId: event.id, + pubkey: event.pubkey, + relay: client.getEventHint(event.id) + } + : { type: 'E', id: event.id, pubkey: event.pubkey } const rootEventTag = getRootEventTag(event) if (rootEventTag) { const [, rootEventHexId, , , rootEventPubkey] = rootEventTag if (rootEventHexId && rootEventPubkey) { - root = { type: 'event', id: rootEventHexId, pubkey: rootEventPubkey } + root = { type: 'E', id: rootEventHexId, pubkey: rootEventPubkey } } else { const rootEventId = generateEventIdFromETag(rootEventTag) if (rootEventId) { const rootEvent = await client.fetchEvent(rootEventId) if (rootEvent) { - root = { type: 'event', id: rootEvent.id, pubkey: rootEvent.pubkey } + root = { type: 'E', id: rootEvent.id, pubkey: rootEvent.pubkey } } } } } else if (event.kind === ExtendedKind.COMMENT) { + const rootATag = getRootAddressableEventTag(event) + if (rootATag) { + const [, coordinate, relay] = rootATag + const [, pubkey] = coordinate.split(':') + root = { type: 'A', id: coordinate, eventId: event.id, pubkey, relay } + } const rootITag = event.tags.find(tagNameEquals('I')) if (rootITag) { root = { type: 'I', id: rootITag[1] } @@ -110,13 +131,18 @@ export default function ReplyNoteList({ index, event }: { index?: number; event: (rootInfo as { pubkey?: string }).pubkey ?? event.pubkey ) const relayUrls = relayList.read.concat(BIG_RELAY_URLS) - const seenOn = client.getSeenEventRelayUrls(rootInfo.id) + const seenOn = + rootInfo.type === 'E' + ? client.getSeenEventRelayUrls(rootInfo.id) + : rootInfo.type === 'A' + ? client.getCurrentRelayUrls() + : [] relayUrls.unshift(...seenOn) const filters: (Omit & { limit: number })[] = [] - if (rootInfo.type === 'event') { + if (rootInfo.type === 'E') { filters.push({ '#e': [rootInfo.id], kinds: [kinds.ShortTextNote], @@ -129,6 +155,15 @@ export default function ReplyNoteList({ index, event }: { index?: number; event: limit: LIMIT }) } + } else if (rootInfo.type === 'A') { + filters.push({ + '#A': [rootInfo.id], + kinds: [ExtendedKind.COMMENT], + limit: LIMIT + }) + if (rootInfo.relay) { + relayUrls.push(rootInfo.relay) + } } else { filters.push({ '#I': [rootInfo.id], diff --git a/src/lib/draft-event.ts b/src/lib/draft-event.ts index c90bf3c2..95ecefe9 100644 --- a/src/lib/draft-event.ts +++ b/src/lib/draft-event.ts @@ -187,10 +187,12 @@ export async function createCommentDraftEvent( const { quoteEventIds, rootEventId, + rootCoordinateTag, rootKind, rootPubkey, rootUrl, parentEventId, + parentCoordinate, parentKind, parentPubkey } = await extractCommentMentions(content, parentEvent) @@ -207,7 +209,9 @@ export async function createCommentDraftEvent( tags.push(...mentions.filter((pubkey) => pubkey !== parentPubkey).map((pubkey) => ['p', pubkey])) - if (rootEventId) { + if (rootCoordinateTag) { + tags.push(rootCoordinateTag) + } else if (rootEventId) { tags.push( rootPubkey ? ['E', rootEventId, client.getEventHint(rootEventId), rootPubkey] @@ -225,7 +229,9 @@ export async function createCommentDraftEvent( } tags.push( ...[ - ['e', parentEventId, client.getEventHint(parentEventId), parentPubkey], + parentCoordinate + ? ['a', parentCoordinate, client.getEventHint(parentEventId)] + : ['e', parentEventId, client.getEventHint(parentEventId), parentPubkey], ['k', parentKind.toString()], ['p', parentPubkey] ] diff --git a/src/lib/event.ts b/src/lib/event.ts index 02c21508..cc67c188 100644 --- a/src/lib/event.ts +++ b/src/lib/event.ts @@ -7,6 +7,7 @@ import { getAmountFromInvoice, getLightningAddressFromProfile } from './lightnin import { formatPubkey, pubkeyToNpub } from './pubkey' import { extractImageInfoFromTag, + generateEventIdFromATag, generateEventIdFromETag, isReplyETag, isRootETag, @@ -27,7 +28,7 @@ export function isNsfwEvent(event: Event) { export function isReplyNoteEvent(event: Event) { if (event.kind === ExtendedKind.COMMENT) { - return !!getParentEventTag(event) + return !!getParentEventTag(event) || !!getParentAddressableEventTag(event) } if (event.kind !== kinds.ShortTextNote) return false @@ -88,16 +89,27 @@ export function getParentEventTag(event?: Event) { return tag } +export function getParentAddressableEventTag(event?: Event) { + if (!event || event.kind !== ExtendedKind.COMMENT) return undefined + + return event.tags.find(tagNameEquals('a')) ?? event.tags.find(tagNameEquals('A')) +} + export function getParentEventHexId(event?: Event) { const tag = getParentEventTag(event) return tag?.[1] } export function getParentEventId(event?: Event) { - const tag = getParentEventTag(event) - if (!tag) return undefined + const eTag = getParentEventTag(event) + if (!eTag) { + const aTag = getParentAddressableEventTag(event) + if (!aTag) return undefined - return generateEventIdFromETag(tag) + return generateEventIdFromATag(aTag) + } + + return generateEventIdFromETag(eTag) } export function getRootEventTag(event?: Event) { @@ -117,6 +129,12 @@ export function getRootEventTag(event?: Event) { return tag } +export function getRootAddressableEventTag(event?: Event) { + if (!event || event.kind !== ExtendedKind.COMMENT) return undefined + + return event.tags.find(tagNameEquals('A')) +} + export function getRootEventHexId(event?: Event) { const tag = getRootEventTag(event) return tag?.[1] @@ -124,7 +142,12 @@ export function getRootEventHexId(event?: Event) { export function getRootEventId(event?: Event) { const tag = getRootEventTag(event) - if (!tag) return undefined + if (!tag) { + const aTag = getRootAddressableEventTag(event) + if (!aTag) return undefined + + return generateEventIdFromATag(aTag) + } return generateEventIdFromETag(tag) } @@ -356,6 +379,13 @@ export async function extractRelatedEventIds(content: string, parentEvent?: Even export async function extractCommentMentions(content: string, parentEvent: Event) { const quoteEventIds: string[] = [] + const parentEventIsReplaceable = isReplaceable(parentEvent.kind) + const rootCoordinateTag = + parentEvent.kind === ExtendedKind.COMMENT + ? parentEvent.tags.find(tagNameEquals('A')) + : isReplaceable(parentEvent.kind) + ? ['A', getEventCoordinate(parentEvent), client.getEventHint(parentEvent.id)] + : undefined const rootEventId = parentEvent.kind === ExtendedKind.COMMENT ? parentEvent.tags.find(tagNameEquals('E'))?.[1] @@ -374,6 +404,7 @@ export async function extractCommentMentions(content: string, parentEvent: Event : undefined const parentEventId = parentEvent.id + const parentCoordinate = parentEventIsReplaceable ? getEventCoordinate(parentEvent) : undefined const parentKind = parentEvent.kind const parentPubkey = parentEvent.pubkey @@ -399,10 +430,12 @@ export async function extractCommentMentions(content: string, parentEvent: Event return { quoteEventIds, rootEventId, + rootCoordinateTag, rootKind, rootPubkey, rootUrl, parentEventId, + parentCoordinate, parentKind, parentPubkey } diff --git a/src/pages/secondary/NotePage/index.tsx b/src/pages/secondary/NotePage/index.tsx index ded17040..ccf72ab9 100644 --- a/src/pages/secondary/NotePage/index.tsx +++ b/src/pages/secondary/NotePage/index.tsx @@ -11,7 +11,7 @@ import { Skeleton } from '@/components/ui/skeleton' import { ExtendedKind } from '@/constants' import { useFetchEvent } from '@/hooks' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' -import { getParentEventId, getRootEventId, isPictureEvent } from '@/lib/event' +import { getParentEventId, getRootEventId, isPictureEvent, isSupportedKind } from '@/lib/event' import { toNote, toNoteList } from '@/lib/link' import { tagNameEquals } from '@/lib/tag' import { useMuteList } from '@/providers/MuteListProvider' @@ -158,6 +158,21 @@ function ParentNote({ eventId }: { eventId?: string }) { ) } + if (!isSupportedKind(event.kind)) { + return ( +
+ push(toNote(eventId))} + > + +
[{t('Cannot handle event of kind k', { k: event.kind })}]
+
+
+
+ ) + } + return (