This commit is contained in:
codytseng 2025-07-19 17:24:51 +08:00
parent 28ec943a52
commit 78725c1d14
45 changed files with 698 additions and 766 deletions

View file

@ -1,14 +1,14 @@
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { isDevEnv } from '@/lib/common'
import { isDevEnv } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useTheme } from '@/providers/ThemeProvider'
import { NstartModal } from 'nstart-modal'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import AccountList from '../AccountList'
import NostrConnectLogin from './NostrConnectionLogin'
import GenerateNewAccount from './GenerateNewAccount'
import NostrConnectLogin from './NostrConnectionLogin'
import NpubLogin from './NpubLogin'
import PrivateKeyLogin from './PrivateKeyLogin'

View file

@ -1,5 +1,5 @@
import { useFetchEvent } from '@/hooks'
import { generateEventIdFromETag } from '@/lib/tag'
import { generateBech32IdFromETag } from '@/lib/tag'
import { useNostr } from '@/providers/NostrProvider'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -15,7 +15,7 @@ export default function BookmarkList() {
return (
bookmarkListEvent.tags
.map((tag) => (tag[0] === 'e' ? generateEventIdFromETag(tag) : undefined))
.map((tag) => (tag[0] === 'e' ? generateBech32IdFromETag(tag) : undefined))
.filter(Boolean) as `nevent1${string}`[]
).reverse()
}, [bookmarkListEvent])

View file

@ -3,7 +3,7 @@ import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
import { Drawer, DrawerContent, DrawerOverlay, DrawerTrigger } from '@/components/ui/drawer'
import { Separator } from '@/components/ui/separator'
import { ExtendedKind } from '@/constants'
import { getReplaceableEventIdentifier, getSharableEventId } from '@/lib/event'
import { getReplaceableEventIdentifier, getNoteBech32Id } from '@/lib/event'
import { toChachiChat } from '@/lib/link'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import clientService from '@/services/client.service'
@ -139,7 +139,7 @@ export default function ClientSelect({
<ClientSelectItem
key={clientId}
onClick={() => setOpen(false)}
href={client.getUrl(originalNoteId ?? getSharableEventId(event!))}
href={client.getUrl(originalNoteId ?? getNoteBech32Id(event!))}
name={client.name}
/>
)
@ -150,7 +150,7 @@ export default function ClientSelect({
variant="ghost"
className="w-full py-6 font-semibold"
onClick={() => {
navigator.clipboard.writeText(originalNoteId ?? getSharableEventId(event!))
navigator.clipboard.writeText(originalNoteId ?? getNoteBech32Id(event!))
setOpen(false)
}}
>

View file

@ -11,8 +11,8 @@ import {
EmbeddedWebsocketUrlParser,
parseContent
} from '@/lib/content-parser'
import { extractEmojiInfosFromTags } from '@/lib/event'
import { extractImageInfoFromTag } from '@/lib/tag'
import { getImageInfosFromEvent } from '@/lib/event'
import { getEmojiInfosFromEmojiTags, getImageInfoFromImetaTag } from '@/lib/tag'
import { cn } from '@/lib/utils'
import mediaUpload from '@/services/media-upload.service'
import { TImageInfo } from '@/types'
@ -45,9 +45,7 @@ const Content = memo(({ event, className }: { event: Event; className?: string }
EmbeddedEmojiParser
])
const imageInfos = event.tags
.map((tag) => extractImageInfoFromTag(tag, event.pubkey))
.filter(Boolean) as TImageInfo[]
const imageInfos = getImageInfosFromEvent(event)
const allImages = nodes
.map((node) => {
if (node.type === 'image') {
@ -57,7 +55,7 @@ const Content = memo(({ event, className }: { event: Event; className?: string }
}
const tag = mediaUpload.getImetaTagByUrl(node.data)
return tag
? extractImageInfoFromTag(tag, event.pubkey)
? getImageInfoFromImetaTag(tag, event.pubkey)
: { url: node.data, pubkey: event.pubkey }
}
if (node.type === 'images') {
@ -73,7 +71,7 @@ const Content = memo(({ event, className }: { event: Event; className?: string }
.flat() as TImageInfo[]
let imageIndex = 0
const emojiInfos = extractEmojiInfosFromTags(event.tags)
const emojiInfos = getEmojiInfosFromEmojiTags(event.tags)
const lastNormalUrlNode = nodes.findLast((node) => node.type === 'url')
const lastNormalUrl =

View file

@ -1,4 +1,4 @@
import { getCommunityDefinition } from '@/lib/event'
import { getCommunityDefinitionFromEvent } from '@/lib/event-metadata'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -14,7 +14,7 @@ export default function CommunityDefinitionPreview({
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
}) {
const { t } = useTranslation()
const metadata = useMemo(() => getCommunityDefinition(event), [event])
const metadata = useMemo(() => getCommunityDefinitionFromEvent(event), [event])
return (
<div className={cn('pointer-events-none', className)} onClick={onClick}>

View file

@ -1,4 +1,4 @@
import { getGroupMetadata } from '@/lib/event'
import { getGroupMetadataFromEvent } from '@/lib/event-metadata'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -14,7 +14,7 @@ export default function GroupMetadataPreview({
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
}) {
const { t } = useTranslation()
const metadata = useMemo(() => getGroupMetadata(event), [event])
const metadata = useMemo(() => getGroupMetadataFromEvent(event), [event])
return (
<div className={cn('pointer-events-none', className)} onClick={onClick}>

View file

@ -1,4 +1,4 @@
import { getLiveEventMetadata } from '@/lib/event'
import { getLiveEventMetadataFromEvent } from '@/lib/event-metadata'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -14,7 +14,7 @@ export default function LiveEventPreview({
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
}) {
const { t } = useTranslation()
const metadata = useMemo(() => getLiveEventMetadata(event), [event])
const metadata = useMemo(() => getLiveEventMetadataFromEvent(event), [event])
return (
<div className={cn('pointer-events-none', className)} onClick={onClick}>

View file

@ -1,4 +1,4 @@
import { getLongFormArticleMetadata } from '@/lib/event'
import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -14,7 +14,7 @@ export default function LongFormArticlePreview({
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
}) {
const { t } = useTranslation()
const metadata = useMemo(() => getLongFormArticleMetadata(event), [event])
const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event])
return (
<div className={cn('pointer-events-none', className)} onClick={onClick}>

View file

@ -7,7 +7,7 @@ import {
EmbeddedVideoParser,
parseContent
} from '@/lib/content-parser'
import { extractEmojiInfosFromTags } from '@/lib/event'
import { getEmojiInfosFromEmojiTags } from '@/lib/tag'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -36,7 +36,7 @@ export default function NormalContentPreview({
])
}, [event, translatedEvent])
const emojiInfos = extractEmojiInfosFromTags(event?.tags)
const emojiInfos = getEmojiInfosFromEmojiTags(event?.tags)
return (
<div className={cn('pointer-events-none', className)} onClick={onClick}>

View file

@ -1,5 +1,5 @@
import { Carousel, CarouselApi, CarouselContent, CarouselItem } from '@/components/ui/carousel'
import { isTouchDevice } from '@/lib/common'
import { isTouchDevice } from '@/lib/utils'
import { TImageInfo } from '@/types'
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
import { useEffect, useState } from 'react'

View file

@ -1,4 +1,4 @@
import { getCommunityDefinition } from '@/lib/event'
import { getCommunityDefinitionFromEvent } from '@/lib/event-metadata'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
import ClientSelect from '../ClientSelect'
@ -11,7 +11,7 @@ export default function CommunityDefinition({
event: Event
className?: string
}) {
const metadata = useMemo(() => getCommunityDefinition(event), [event])
const metadata = useMemo(() => getCommunityDefinitionFromEvent(event), [event])
const communityNameComponent = (
<div className="text-xl font-semibold line-clamp-1">{metadata.name}</div>

View file

@ -1,4 +1,4 @@
import { getGroupMetadata } from '@/lib/event'
import { getGroupMetadataFromEvent } from '@/lib/event-metadata'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
import ClientSelect from '../ClientSelect'
@ -13,7 +13,7 @@ export default function GroupMetadata({
originalNoteId?: string
className?: string
}) {
const metadata = useMemo(() => getGroupMetadata(event), [event])
const metadata = useMemo(() => getGroupMetadataFromEvent(event), [event])
const groupNameComponent = (
<div className="text-xl font-semibold line-clamp-1">{metadata.name}</div>

View file

@ -2,7 +2,7 @@ import { useFetchEvent, useTranslatedEvent } from '@/hooks'
import { createFakeEvent } from '@/lib/event'
import { toNjump, toNote } from '@/lib/link'
import { isValidPubkey } from '@/lib/pubkey'
import { generateEventIdFromATag } from '@/lib/tag'
import { generateBech32IdFromATag } from '@/lib/tag'
import { cn } from '@/lib/utils'
import { useSecondaryPage } from '@/PageManager'
import { Event } from 'nostr-tools'
@ -70,7 +70,7 @@ function HighlightSource({ event }: { event: Event }) {
return sourceTag[1]
}
if (sourceTag[0] === 'a') {
return generateEventIdFromATag(sourceTag)
return generateBech32IdFromATag(sourceTag)
}
}, [sourceTag])
const pubkey = useMemo(() => {

View file

@ -1,5 +1,5 @@
import { Badge } from '@/components/ui/badge'
import { getLiveEventMetadata } from '@/lib/event'
import { getLiveEventMetadataFromEvent } from '@/lib/event-metadata'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -8,7 +8,7 @@ import Image from '../Image'
export default function LiveEvent({ event, className }: { event: Event; className?: string }) {
const { isSmallScreen } = useScreenSize()
const metadata = useMemo(() => getLiveEventMetadata(event), [event])
const metadata = useMemo(() => getLiveEventMetadataFromEvent(event), [event])
const liveStatusComponent =
metadata.status &&

View file

@ -1,5 +1,5 @@
import { Badge } from '@/components/ui/badge'
import { getLongFormArticleMetadata } from '@/lib/event'
import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
@ -14,7 +14,7 @@ export default function LongFormArticle({
className?: string
}) {
const { isSmallScreen } = useScreenSize()
const metadata = useMemo(() => getLongFormArticleMetadata(event), [event])
const metadata = useMemo(() => getLongFormArticleMetadataFromEvent(event), [event])
const titleComponent = <div className="text-xl font-semibold line-clamp-2">{metadata.title}</div>

View file

@ -1,8 +1,8 @@
import { useSecondaryPage } from '@/PageManager'
import { ExtendedKind } from '@/constants'
import {
extractImageInfosFromEventTags,
getParentEventId,
getImageInfosFromEvent,
getParentBech32Id,
getUsingClient,
isNsfwEvent,
isPictureEvent
@ -47,11 +47,11 @@ export default function Note({
const { push } = useSecondaryPage()
const { isSmallScreen } = useScreenSize()
const parentEventId = useMemo(
() => (hideParentNotePreview ? undefined : getParentEventId(event)),
() => (hideParentNotePreview ? undefined : getParentBech32Id(event)),
[event, hideParentNotePreview]
)
const imageInfos = useMemo(
() => (isPictureEvent(event) ? extractImageInfosFromEventTags(event) : []),
() => (isPictureEvent(event) ? getImageInfosFromEvent(event) : []),
[event]
)
const usingClient = useMemo(() => getUsingClient(event), [event])

View file

@ -7,7 +7,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { getSharableEventId } from '@/lib/event'
import { getNoteBech32Id } from '@/lib/event'
import { toNjump } from '@/lib/link'
import { pubkeyToNpub } from '@/lib/pubkey'
import { useMuteList } from '@/providers/MuteListProvider'
@ -56,7 +56,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
<Button
onClick={() => {
setIsDrawerOpen(false)
navigator.clipboard.writeText(getSharableEventId(event))
navigator.clipboard.writeText(getNoteBech32Id(event))
}}
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
variant="ghost"
@ -78,7 +78,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
<Button
onClick={() => {
setIsDrawerOpen(false)
navigator.clipboard.writeText(toNjump(getSharableEventId(event)))
navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))
}}
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
variant="ghost"
@ -149,9 +149,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
<DropdownMenu>
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(getSharableEventId(event))}
>
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(getNoteBech32Id(event))}>
<Copy />
{t('Copy event ID')}
</DropdownMenuItem>
@ -162,7 +160,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
{t('Copy user ID')}
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(toNjump(getSharableEventId(event)))}
onClick={() => navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))}
>
<Link />
{t('Copy share link')}

View file

@ -8,7 +8,7 @@ import {
} from '@/components/ui/dropdown-menu'
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
import { createRepostDraftEvent } from '@/lib/draft-event'
import { getSharableEventId } from '@/lib/event'
import { getNoteBech32Id } from '@/lib/event'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
@ -87,7 +87,7 @@ export default function RepostButton({ event }: { event: Event }) {
<PostEditor
open={isPostDialogOpen}
setOpen={setIsPostDialogOpen}
defaultContent={'\nnostr:' + getSharableEventId(event)}
defaultContent={'\nnostr:' + getNoteBech32Id(event)}
/>
)

View file

@ -1,5 +1,5 @@
import { useFetchEvent } from '@/hooks'
import { extractZapInfoFromReceipt } from '@/lib/event'
import { getZapInfoFromEvent } from '@/lib/event-metadata'
import { formatAmount } from '@/lib/lightning'
import { toNote, toProfile } from '@/lib/link'
import { cn } from '@/lib/utils'
@ -24,7 +24,7 @@ export function ZapNotification({
const { push } = useSecondaryPage()
const { pubkey } = useNostr()
const { senderPubkey, eventId, amount, comment } = useMemo(
() => extractZapInfoFromReceipt(notification) ?? ({} as any),
() => getZapInfoFromEvent(notification) ?? ({} as any),
[notification]
)
const { event, isFetching } = useFetchEvent(eventId)

View file

@ -7,7 +7,8 @@ import {
EmbeddedWebsocketUrlParser,
parseContent
} from '@/lib/content-parser'
import { extractEmojiInfosFromTags, extractImageInfosFromEventTags } from '@/lib/event'
import { getImageInfosFromEvent } from '@/lib/event'
import { getEmojiInfosFromEmojiTags } from '@/lib/tag'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { memo, useMemo } from 'react'
@ -22,7 +23,7 @@ import Emoji from '../Emoji'
import { ImageCarousel } from '../ImageCarousel'
const PictureContent = memo(({ event, className }: { event: Event; className?: string }) => {
const images = useMemo(() => extractImageInfosFromEventTags(event), [event])
const images = useMemo(() => getImageInfosFromEvent(event), [event])
const nodes = parseContent(event.content, [
EmbeddedNormalUrlParser,
@ -33,7 +34,7 @@ const PictureContent = memo(({ event, className }: { event: Event; className?: s
EmbeddedEmojiParser
])
const emojiInfos = extractEmojiInfosFromTags(event.tags)
const emojiInfos = getEmojiInfosFromEmojiTags(event.tags)
return (
<div className={cn('text-wrap break-words whitespace-pre-wrap space-y-2', className)}>

View file

@ -1,5 +1,5 @@
import { EmbeddedHashtagParser, EmbeddedMentionParser, parseContent } from '@/lib/content-parser'
import { extractImageInfosFromEventTags } from '@/lib/event'
import { getImageInfosFromEvent } from '@/lib/event'
import { toNote } from '@/lib/link'
import { tagNameEquals } from '@/lib/tag'
import { cn } from '@/lib/utils'
@ -21,7 +21,7 @@ export default function PictureNoteCard({
className?: string
}) {
const { push } = useSecondaryPage()
const images = useMemo(() => extractImageInfosFromEventTags(event), [event])
const images = useMemo(() => getImageInfosFromEvent(event), [event])
const title = useMemo(() => {
const nodes = parseContent(event.tags.find(tagNameEquals('title'))?.[1] ?? event.content, [
EmbeddedMentionParser,

View file

@ -1,14 +1,14 @@
import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { extractMentions } from '@/lib/event'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
import { Check } from 'lucide-react'
import { Event } from 'nostr-tools'
import { Event, nip19 } from 'nostr-tools'
import { HTMLAttributes, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SimpleUserAvatar } from '../UserAvatar'
import { SimpleUsername } from '../Username'
import { useMuteList } from '@/providers/MuteListProvider'
export default function Mentions({
content,
@ -131,3 +131,50 @@ function PopoverCheckboxItem({
</div>
)
}
async function extractMentions(content: string, parentEvent?: Event) {
const parentEventPubkey = parentEvent ? parentEvent.pubkey : undefined
const pubkeys: string[] = []
const relatedPubkeys: string[] = []
const matches = content.match(
/nostr:(npub1[a-z0-9]{58}|nprofile1[a-z0-9]+|note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g
)
const addToSet = (arr: string[], pubkey: string) => {
if (pubkey === parentEventPubkey) return
if (!arr.includes(pubkey)) arr.push(pubkey)
}
for (const m of matches || []) {
try {
const id = m.split(':')[1]
const { type, data } = nip19.decode(id)
if (type === 'nprofile') {
addToSet(pubkeys, data.pubkey)
} else if (type === 'npub') {
addToSet(pubkeys, data)
} else if (['nevent', 'note'].includes(type)) {
const event = await client.fetchEvent(id)
if (event) {
addToSet(pubkeys, event.pubkey)
}
}
} catch (e) {
console.error(e)
}
}
if (parentEvent) {
parentEvent.tags.forEach(([tagName, tagValue]) => {
if (['p', 'P'].includes(tagName) && !!tagValue) {
addToSet(relatedPubkeys, tagValue)
}
})
}
return {
pubkeys,
relatedPubkeys: relatedPubkeys.filter((p) => !pubkeys.includes(p)),
parentEventPubkey
}
}

View file

@ -1,14 +1,14 @@
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
import {
getEventCoordinate,
getParentEventTag,
getRootAddressableEventTag,
getParentETag,
getReplaceableEventCoordinate,
getRootATag,
getRootETag,
getRootEventHexId,
getRootEventTag,
isReplaceable,
isReplaceableEvent,
isReplyNoteEvent
} from '@/lib/event'
import { generateEventIdFromETag, tagNameEquals } from '@/lib/tag'
import { generateBech32IdFromETag, tagNameEquals } from '@/lib/tag'
import { useSecondaryPage } from '@/PageManager'
import { useReply } from '@/providers/ReplyProvider'
import { useUserTrust } from '@/providers/UserTrustProvider'
@ -36,16 +36,18 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
const replies = useMemo(() => {
const replyIdSet = new Set<string>()
const replyEvents: NEvent[] = []
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 || [])
const currentEventKey = isReplaceableEvent(event.kind)
? getReplaceableEventCoordinate(event)
: event.id
let parentEventKeys = [currentEventKey]
while (parentEventKeys.length > 0) {
const events = parentEventKeys.flatMap((id) => repliesMap.get(id)?.events || [])
events.forEach((evt) => {
if (replyIdSet.has(evt.id)) return
replyIdSet.add(evt.id)
replyEvents.push(evt)
})
parentEventIds = events.map((evt) => evt.id)
parentEventKeys = events.map((evt) => evt.id)
}
return replyEvents.sort((a, b) => a.created_at - b.created_at)
}, [event.id, repliesMap])
@ -59,22 +61,22 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
useEffect(() => {
const fetchRootEvent = async () => {
let root: TRootInfo = isReplaceable(event.kind)
let root: TRootInfo = isReplaceableEvent(event.kind)
? {
type: 'A',
id: getEventCoordinate(event),
id: getReplaceableEventCoordinate(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
const rootETag = getRootETag(event)
if (rootETag) {
const [, rootEventHexId, , , rootEventPubkey] = rootETag
if (rootEventHexId && rootEventPubkey) {
root = { type: 'E', id: rootEventHexId, pubkey: rootEventPubkey }
} else {
const rootEventId = generateEventIdFromETag(rootEventTag)
const rootEventId = generateBech32IdFromETag(rootETag)
if (rootEventId) {
const rootEvent = await client.fetchEvent(rootEventId)
if (rootEvent) {
@ -83,7 +85,7 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
}
}
} else if (event.kind === ExtendedKind.COMMENT) {
const rootATag = getRootAddressableEventTag(event)
const rootATag = getRootATag(event)
if (rootATag) {
const [, coordinate, relay] = rootATag
const [, pubkey] = coordinate.split(':')
@ -295,9 +297,9 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
}
}
const parentEventTag = getParentEventTag(reply)
const parentEventOriginalId = parentEventTag?.[1]
const parentEventId = parentEventTag ? generateEventIdFromETag(parentEventTag) : undefined
const parentETag = getParentETag(reply)
const parentEventHexId = parentETag?.[1]
const parentEventId = parentETag ? generateBech32IdFromETag(parentETag) : undefined
return (
<div
ref={(el) => (replyRefs.current[reply.id] = el)}
@ -306,8 +308,8 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
>
<ReplyNote
event={reply}
parentEventId={event.id !== parentEventOriginalId ? parentEventId : undefined}
onClickParent={() => parentEventOriginalId && highlightReply(parentEventOriginalId)}
parentEventId={event.id !== parentEventHexId ? parentEventId : undefined}
onClickParent={() => parentEventHexId && highlightReply(parentEventHexId)}
highlight={highlightReplyId === reply.id}
/>
</div>