feat: display reaction emojis in notifications

This commit is contained in:
codytseng
2025-03-12 22:53:25 +08:00
parent 9285ecca3d
commit 759cd73af4
3 changed files with 34 additions and 9 deletions

View File

@@ -11,6 +11,7 @@ export default function Image({
className = '', className = '',
classNames = {}, classNames = {},
hideIfError = false, hideIfError = false,
errorPlaceholder = <ImageOff />,
...props ...props
}: HTMLAttributes<HTMLDivElement> & { }: HTMLAttributes<HTMLDivElement> & {
classNames?: { classNames?: {
@@ -20,6 +21,7 @@ export default function Image({
image: TImageInfo image: TImageInfo
alt?: string alt?: string
hideIfError?: boolean hideIfError?: boolean
errorPlaceholder?: React.ReactNode
}) { }) {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [displayBlurHash, setDisplayBlurHash] = useState(true) const [displayBlurHash, setDisplayBlurHash] = useState(true)
@@ -77,7 +79,7 @@ export default function Image({
classNames.errorPlaceholder classNames.errorPlaceholder
)} )}
> >
<ImageOff /> {errorPlaceholder}
</div> </div>
)} )}
{displayBlurHash && blurDataUrl && !hasError && ( {displayBlurHash && blurDataUrl && !hasError && (

View File

@@ -1,7 +1,8 @@
import Image from '@/components/Image'
import { PICTURE_EVENT_KIND } from '@/constants' import { PICTURE_EVENT_KIND } from '@/constants'
import { useFetchEvent } from '@/hooks' import { useFetchEvent } from '@/hooks'
import { toNote } from '@/lib/link' import { toNote } from '@/lib/link'
import { tagNameEquals } from '@/lib/tag' import { extractEmojiFromEventTags, tagNameEquals } from '@/lib/tag'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { useSecondaryPage } from '@/PageManager' import { useSecondaryPage } from '@/PageManager'
import { useNostr } from '@/providers/NostrProvider' import { useNostr } from '@/providers/NostrProvider'
@@ -29,6 +30,29 @@ export function ReactionNotification({
return eTag?.[1] return eTag?.[1]
}, [notification, pubkey]) }, [notification, pubkey])
const { event } = useFetchEvent(eventId) const { event } = useFetchEvent(eventId)
const reaction = useMemo(() => {
if (!notification.content || notification.content === '+') {
return <Heart size={24} className="text-red-400" />
}
const emojiName = /^:([^:]+):$/.exec(notification.content)?.[1]
if (emojiName) {
const emojiUrl = extractEmojiFromEventTags(emojiName, notification.tags)
if (emojiUrl) {
return (
<Image
image={{ url: emojiUrl }}
alt={emojiName}
className="w-6 h-6"
classNames={{ errorPlaceholder: 'bg-transparent' }}
errorPlaceholder={<Heart size={24} className="text-red-400" />}
/>
)
}
}
return notification.content
}, [notification])
if (!event || !eventId || ![kinds.ShortTextNote, PICTURE_EVENT_KIND].includes(event.kind)) { if (!event || !eventId || ![kinds.ShortTextNote, PICTURE_EVENT_KIND].includes(event.kind)) {
return null return null
} }
@@ -40,13 +64,7 @@ export function ReactionNotification({
> >
<div className="flex gap-2 items-center flex-1"> <div className="flex gap-2 items-center flex-1">
<UserAvatar userId={notification.pubkey} size="small" /> <UserAvatar userId={notification.pubkey} size="small" />
<div className="text-xl min-w-6 text-center"> <div className="text-xl min-w-6 text-center">{reaction}</div>
{!notification.content || notification.content === '+' ? (
<Heart size={24} className="text-red-400" />
) : (
notification.content
)}
</div>
<ContentPreview <ContentPreview
className={cn('truncate flex-1 w-0', isNew ? 'font-semibold' : 'text-muted-foreground')} className={cn('truncate flex-1 w-0', isNew ? 'font-semibold' : 'text-muted-foreground')}
event={event} event={event}

View File

@@ -66,6 +66,11 @@ export function extractPubkeysFromEventTags(tags: string[][]) {
) )
} }
export function extractEmojiFromEventTags(emojiName: string, tags: string[][]) {
const emojiTag = tags.find((tag) => tag[0] === 'emoji' && tag[1] === emojiName)
return emojiTag?.[2]
}
export function isSameTag(tag1: string[], tag2: string[]) { export function isSameTag(tag1: string[], tag2: string[]) {
if (tag1.length !== tag2.length) return false if (tag1.length !== tag2.length) return false
for (let i = 0; i < tag1.length; i++) { for (let i = 0; i < tag1.length; i++) {