From d2c5c923a31ff064acf0bab0cc4e50a601fd9ff0 Mon Sep 17 00:00:00 2001 From: codytseng Date: Mon, 17 Nov 2025 21:55:24 +0800 Subject: [PATCH] feat: support displaying highlights in replies --- src/components/Note/Highlight.tsx | 32 ++----------------------- src/components/ReplyNote/Highlight.tsx | 26 ++++++++++++++++++++ src/components/ReplyNote/index.tsx | 14 ++++++++--- src/components/ReplyNoteList/index.tsx | 33 +++++++++++++++++--------- src/lib/event-metadata.ts | 31 ++++++++++++++++++++++++ src/providers/ReplyProvider.tsx | 26 +++++++++++++++----- 6 files changed, 112 insertions(+), 50 deletions(-) create mode 100644 src/components/ReplyNote/Highlight.tsx diff --git a/src/components/Note/Highlight.tsx b/src/components/Note/Highlight.tsx index 6b9ef30..44930ed 100644 --- a/src/components/Note/Highlight.tsx +++ b/src/components/Note/Highlight.tsx @@ -1,5 +1,6 @@ import { useFetchEvent, useTranslatedEvent } from '@/hooks' import { createFakeEvent } from '@/lib/event' +import { getHighlightSourceTag } from '@/lib/event-metadata' import { toNote } from '@/lib/link' import { isValidPubkey } from '@/lib/pubkey' import { generateBech32IdFromATag, generateBech32IdFromETag } from '@/lib/tag' @@ -37,36 +38,7 @@ export default function Highlight({ event, className }: { event: Event; classNam function HighlightSource({ event }: { event: Event }) { const { t } = useTranslation() const { push } = useSecondaryPage() - const sourceTag = useMemo(() => { - let sourceTag: string[] | undefined - for (const tag of event.tags) { - // Highest priority: 'source' tag - if (tag[2] === 'source') { - sourceTag = tag - break - } - - // Give 'e' tags highest priority - if (tag[0] === 'e') { - sourceTag = tag - continue - } - - // Give 'a' tags second priority over 'e' tags - if (tag[0] === 'a' && (!sourceTag || sourceTag[0] !== 'e')) { - sourceTag = tag - continue - } - - // Give 'r' tags lowest priority - if (tag[0] === 'r' && (!sourceTag || sourceTag[0] === 'r')) { - sourceTag = tag - continue - } - } - - return sourceTag - }, [event]) + const sourceTag = useMemo(() => getHighlightSourceTag(event), [event]) const { event: referenceEvent } = useFetchEvent( sourceTag ? sourceTag[0] === 'e' diff --git a/src/components/ReplyNote/Highlight.tsx b/src/components/ReplyNote/Highlight.tsx new file mode 100644 index 0000000..8c4304c --- /dev/null +++ b/src/components/ReplyNote/Highlight.tsx @@ -0,0 +1,26 @@ +import { useTranslatedEvent } from '@/hooks' +import { createFakeEvent } from '@/lib/event' +import { cn } from '@/lib/utils' +import { Event } from 'nostr-tools' +import { useMemo } from 'react' +import Content from '../Content' + +export default function Highlight({ event, className }: { event: Event; className?: string }) { + const translatedEvent = useTranslatedEvent(event.id) + const comment = useMemo( + () => (translatedEvent?.tags ?? event.tags).find((tag) => tag[0] === 'comment')?.[1], + [event, translatedEvent] + ) + + return ( +
+ {comment && } +
+
+
+ {translatedEvent?.content ?? event.content} +
+
+
+ ) +} diff --git a/src/components/ReplyNote/index.tsx b/src/components/ReplyNote/index.tsx index c0b82d1..0e94145 100644 --- a/src/components/ReplyNote/index.tsx +++ b/src/components/ReplyNote/index.tsx @@ -6,7 +6,7 @@ import { toNote } from '@/lib/link' import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useMuteList } from '@/providers/MuteListProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' -import { Event } from 'nostr-tools' +import { Event, kinds } from 'nostr-tools' import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ClientTag from '../ClientTag' @@ -15,11 +15,12 @@ import Content from '../Content' import { FormattedTimestamp } from '../FormattedTimestamp' import Nip05 from '../Nip05' import NoteOptions from '../NoteOptions' -import StuffStats from '../StuffStats' import ParentNotePreview from '../ParentNotePreview' +import StuffStats from '../StuffStats' import TranslateButton from '../TranslateButton' import UserAvatar from '../UserAvatar' import Username from '../Username' +import Highlight from './Highlight' export default function ReplyNote({ event, @@ -51,6 +52,13 @@ export default function ReplyNote({ return true }, [showMuted, mutePubkeySet, event, hideContentMentioningMutedUsers]) + let content: React.ReactNode + if (event.kind === kinds.Highlights) { + content = + } else { + content = + } + return (
)} {show ? ( - + content ) : (