feat: display reaction emojis in notifications
This commit is contained in:
@@ -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 && (
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user