feat: improve parent note preview
This commit is contained in:
34
src/components/ContentPreview/index.tsx
Normal file
34
src/components/ContentPreview/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { extractEmbeddedNotesFromContent, extractImagesFromContent } from '@/lib/event'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Event } from 'nostr-tools'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { embedded, embeddedNostrNpubRenderer, embeddedNostrProfileRenderer } from '../Embedded'
|
||||||
|
|
||||||
|
export default function ContentPreview({
|
||||||
|
event,
|
||||||
|
className
|
||||||
|
}: {
|
||||||
|
event?: Event
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const content = useMemo(() => {
|
||||||
|
if (!event) return t('Not found')
|
||||||
|
const { contentWithoutEmbeddedNotes, embeddedNotes } = extractEmbeddedNotesFromContent(
|
||||||
|
event.content
|
||||||
|
)
|
||||||
|
const { contentWithoutImages, images } = extractImagesFromContent(contentWithoutEmbeddedNotes)
|
||||||
|
const contents = [contentWithoutImages]
|
||||||
|
if (images?.length) {
|
||||||
|
contents.push(`[${t('image')}]`)
|
||||||
|
}
|
||||||
|
if (embeddedNotes.length) {
|
||||||
|
contents.push(`[${t('note')}]`)
|
||||||
|
}
|
||||||
|
return embedded(contents.join(' '), [embeddedNostrProfileRenderer, embeddedNostrNpubRenderer])
|
||||||
|
}, [event])
|
||||||
|
if (!event) return null
|
||||||
|
|
||||||
|
return <div className={cn('pointer-events-none', className)}>{content}</div>
|
||||||
|
}
|
||||||
@@ -35,8 +35,8 @@ function EmbeddedNoteSkeleton({ className }: { className?: string }) {
|
|||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Skeleton className="w-7 h-7 rounded-full" />
|
<Skeleton className="w-16 h-7 rounded-full" />
|
||||||
<Skeleton className="h-3 w-16 my-1" />
|
<Skeleton className="h-3 w-12 my-1" />
|
||||||
</div>
|
</div>
|
||||||
<Skeleton className="w-full h-4 my-1 mt-2" />
|
<Skeleton className="w-full h-4 my-1 mt-2" />
|
||||||
<Skeleton className="w-2/3 h-4 my-1" />
|
<Skeleton className="w-2/3 h-4 my-1" />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
import { getUsingClient } from '@/lib/event'
|
import { useFetchEvent } from '@/hooks'
|
||||||
|
import { getParentEventId, getUsingClient } from '@/lib/event'
|
||||||
import { toNote } from '@/lib/link'
|
import { toNote } from '@/lib/link'
|
||||||
import { Event } from 'nostr-tools'
|
import { Event } from 'nostr-tools'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
@@ -12,21 +13,27 @@ import Username from '../Username'
|
|||||||
|
|
||||||
export default function Note({
|
export default function Note({
|
||||||
event,
|
event,
|
||||||
parentEvent,
|
|
||||||
size = 'normal',
|
size = 'normal',
|
||||||
className,
|
className,
|
||||||
|
hideParentNotePreview = false,
|
||||||
hideStats = false,
|
hideStats = false,
|
||||||
fetchNoteStats = false
|
fetchNoteStats = false
|
||||||
}: {
|
}: {
|
||||||
event: Event
|
event: Event
|
||||||
parentEvent?: Event
|
|
||||||
size?: 'normal' | 'small'
|
size?: 'normal' | 'small'
|
||||||
className?: string
|
className?: string
|
||||||
|
hideParentNotePreview?: boolean
|
||||||
hideStats?: boolean
|
hideStats?: boolean
|
||||||
fetchNoteStats?: boolean
|
fetchNoteStats?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { push } = useSecondaryPage()
|
const { push } = useSecondaryPage()
|
||||||
|
const parentEventId = useMemo(
|
||||||
|
() => (hideParentNotePreview ? undefined : getParentEventId(event)),
|
||||||
|
[event, hideParentNotePreview]
|
||||||
|
)
|
||||||
|
const { event: parentEvent, isFetching } = useFetchEvent(parentEventId)
|
||||||
const usingClient = useMemo(() => getUsingClient(event), [event])
|
const usingClient = useMemo(() => getUsingClient(event), [event])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
@@ -49,13 +56,14 @@ export default function Note({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{parentEvent && (
|
{parentEventId && (
|
||||||
<ParentNotePreview
|
<ParentNotePreview
|
||||||
event={parentEvent}
|
event={parentEvent}
|
||||||
|
isFetching={isFetching}
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
push(toNote(parentEvent))
|
push(toNote(parentEventId))
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { useFetchEvent } from '@/hooks'
|
|
||||||
import { getParentEventId, getRootEventId } from '@/lib/event'
|
|
||||||
import { toNote } from '@/lib/link'
|
import { toNote } from '@/lib/link'
|
||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
import { Event } from 'nostr-tools'
|
import { Event } from 'nostr-tools'
|
||||||
@@ -19,8 +17,6 @@ export default function MainNoteCard({
|
|||||||
embedded?: boolean
|
embedded?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { push } = useSecondaryPage()
|
const { push } = useSecondaryPage()
|
||||||
const { event: rootEvent } = useFetchEvent(getRootEventId(event))
|
|
||||||
const { event: parentEvent } = useFetchEvent(getParentEventId(event))
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={className}
|
||||||
@@ -33,12 +29,7 @@ export default function MainNoteCard({
|
|||||||
className={`clickable text-left ${embedded ? 'p-2 sm:p-3 border rounded-lg' : 'px-4 py-3'}`}
|
className={`clickable text-left ${embedded ? 'p-2 sm:p-3 border rounded-lg' : 'px-4 py-3'}`}
|
||||||
>
|
>
|
||||||
<RepostDescription reposter={reposter} />
|
<RepostDescription reposter={reposter} />
|
||||||
<Note
|
<Note size={embedded ? 'small' : 'normal'} event={event} hideStats={embedded} />
|
||||||
size={embedded ? 'small' : 'normal'}
|
|
||||||
event={event}
|
|
||||||
parentEvent={parentEvent ?? rootEvent}
|
|
||||||
hideStats={embedded}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{!embedded && <Separator />}
|
{!embedded && <Separator />}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -335,13 +335,23 @@ function LoadingSkeleton({ isPictures }: { isPictures: boolean }) {
|
|||||||
<div className="px-4 py-3">
|
<div className="px-4 py-3">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Skeleton className="w-10 h-10 rounded-full" />
|
<Skeleton className="w-10 h-10 rounded-full" />
|
||||||
<div className="space-y-1">
|
<div className={`flex-1 w-0`}>
|
||||||
<Skeleton className="w-10 h-4" />
|
<div className="py-1">
|
||||||
<Skeleton className="w-20 h-3" />
|
<Skeleton className="h-4 w-16" />
|
||||||
|
</div>
|
||||||
|
<div className="py-0.5">
|
||||||
|
<Skeleton className="h-3 w-12" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="pt-2">
|
||||||
|
<div className="my-1">
|
||||||
|
<Skeleton className="w-full h-4 my-1 mt-2" />
|
||||||
|
</div>
|
||||||
|
<div className="my-1">
|
||||||
|
<Skeleton className="w-2/3 h-4 my-1" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Skeleton className="w-full h-5 mt-2" />
|
|
||||||
<Skeleton className="w-2/3 h-5 mt-2" />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { BIG_RELAY_URLS, COMMENT_EVENT_KIND, PICTURE_EVENT_KIND } from '@/constants'
|
import { BIG_RELAY_URLS, COMMENT_EVENT_KIND, PICTURE_EVENT_KIND } from '@/constants'
|
||||||
import { useFetchEvent } from '@/hooks'
|
import { useFetchEvent } from '@/hooks'
|
||||||
import { extractEmbeddedNotesFromContent, extractImagesFromContent } from '@/lib/event'
|
|
||||||
import { toNote } from '@/lib/link'
|
import { toNote } from '@/lib/link'
|
||||||
import { tagNameEquals } from '@/lib/tag'
|
import { tagNameEquals } from '@/lib/tag'
|
||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
@@ -22,7 +21,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import PullToRefresh from 'react-simple-pull-to-refresh'
|
import PullToRefresh from 'react-simple-pull-to-refresh'
|
||||||
import { embedded, embeddedNostrNpubRenderer, embeddedNostrProfileRenderer } from '../Embedded'
|
import ContentPreview from '../ContentPreview'
|
||||||
import { FormattedTimestamp } from '../FormattedTimestamp'
|
import { FormattedTimestamp } from '../FormattedTimestamp'
|
||||||
import UserAvatar from '../UserAvatar'
|
import UserAvatar from '../UserAvatar'
|
||||||
|
|
||||||
@@ -230,7 +229,7 @@ function ReactionNotification({ notification }: { notification: Event }) {
|
|||||||
<UserAvatar userId={notification.pubkey} size="small" />
|
<UserAvatar userId={notification.pubkey} size="small" />
|
||||||
<Heart size={24} className="text-red-400" />
|
<Heart size={24} className="text-red-400" />
|
||||||
<div>{notification.content === '+' ? <ThumbsUp size={14} /> : notification.content}</div>
|
<div>{notification.content === '+' ? <ThumbsUp size={14} /> : notification.content}</div>
|
||||||
<ContentPreview event={event} />
|
<ContentPreview className="truncate flex-1 w-0" event={event} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">
|
||||||
<FormattedTimestamp timestamp={notification.created_at} short />
|
<FormattedTimestamp timestamp={notification.created_at} short />
|
||||||
@@ -248,7 +247,7 @@ function ReplyNotification({ notification }: { notification: Event }) {
|
|||||||
>
|
>
|
||||||
<UserAvatar userId={notification.pubkey} size="small" />
|
<UserAvatar userId={notification.pubkey} size="small" />
|
||||||
<MessageCircle size={24} className="text-blue-400" />
|
<MessageCircle size={24} className="text-blue-400" />
|
||||||
<ContentPreview event={notification} />
|
<ContentPreview className="truncate flex-1 w-0" event={notification} />
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">
|
||||||
<FormattedTimestamp timestamp={notification.created_at} short />
|
<FormattedTimestamp timestamp={notification.created_at} short />
|
||||||
</div>
|
</div>
|
||||||
@@ -278,7 +277,7 @@ function RepostNotification({ notification }: { notification: Event }) {
|
|||||||
>
|
>
|
||||||
<UserAvatar userId={notification.pubkey} size="small" />
|
<UserAvatar userId={notification.pubkey} size="small" />
|
||||||
<Repeat size={24} className="text-green-400" />
|
<Repeat size={24} className="text-green-400" />
|
||||||
<ContentPreview event={event} />
|
<ContentPreview className="truncate flex-1 w-0" event={event} />
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">
|
||||||
<FormattedTimestamp timestamp={notification.created_at} short />
|
<FormattedTimestamp timestamp={notification.created_at} short />
|
||||||
</div>
|
</div>
|
||||||
@@ -307,26 +306,10 @@ function CommentNotification({ notification }: { notification: Event }) {
|
|||||||
>
|
>
|
||||||
<UserAvatar userId={notification.pubkey} size="small" />
|
<UserAvatar userId={notification.pubkey} size="small" />
|
||||||
<MessageCircle size={24} className="text-blue-400" />
|
<MessageCircle size={24} className="text-blue-400" />
|
||||||
<ContentPreview event={notification} />
|
<ContentPreview className="truncate flex-1 w-0" event={notification} />
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">
|
||||||
<FormattedTimestamp timestamp={notification.created_at} short />
|
<FormattedTimestamp timestamp={notification.created_at} short />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ContentPreview({ event }: { event?: Event }) {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const content = useMemo(() => {
|
|
||||||
if (!event) return null
|
|
||||||
const { contentWithoutEmbeddedNotes } = extractEmbeddedNotesFromContent(event.content)
|
|
||||||
const { contentWithoutImages, images } = extractImagesFromContent(contentWithoutEmbeddedNotes)
|
|
||||||
return embedded(contentWithoutImages + (images?.length ? `[${t('image')}]` : ''), [
|
|
||||||
embeddedNostrProfileRenderer,
|
|
||||||
embeddedNostrNpubRenderer
|
|
||||||
])
|
|
||||||
}, [event])
|
|
||||||
if (!event) return null
|
|
||||||
|
|
||||||
return <div className="truncate flex-1 w-0">{content}</div>
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,37 +1,62 @@
|
|||||||
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
import { Event } from 'nostr-tools'
|
import { Event } from 'nostr-tools'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import ContentPreview from '../ContentPreview'
|
||||||
import UserAvatar from '../UserAvatar'
|
import UserAvatar from '../UserAvatar'
|
||||||
|
|
||||||
export default function ParentNotePreview({
|
export default function ParentNotePreview({
|
||||||
event,
|
event,
|
||||||
|
isFetching = false,
|
||||||
className,
|
className,
|
||||||
onClick
|
onClick
|
||||||
}: {
|
}: {
|
||||||
event: Event
|
event?: Event
|
||||||
|
isFetching?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
|
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { mutePubkeys } = useMuteList()
|
const { mutePubkeys } = useMuteList()
|
||||||
const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event])
|
const isMuted = useMemo(
|
||||||
|
() => (event ? mutePubkeys.includes(event.pubkey) : false),
|
||||||
|
[mutePubkeys, event]
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex gap-1 items-center text-sm rounded-full px-2 bg-muted w-44 max-w-full text-muted-foreground hover:text-foreground cursor-pointer',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div className="shrink-0">{t('reply to')}</div>
|
||||||
|
<Skeleton className="w-4 h-4 rounded-full" />
|
||||||
|
<div className="py-1 flex-1">
|
||||||
|
<Skeleton className="h-3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex space-x-1 items-center text-sm rounded-full px-2 bg-muted w-fit max-w-full text-muted-foreground hover:text-foreground cursor-pointer',
|
'flex gap-1 items-center text-sm rounded-full px-2 bg-muted w-fit max-w-full text-muted-foreground hover:text-foreground cursor-pointer',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<div className="shrink-0">{t('reply to')}</div>
|
<div className="shrink-0">{t('reply to')}</div>
|
||||||
<UserAvatar className="shrink-0" userId={event.pubkey} size="tiny" />
|
{event && <UserAvatar className="shrink-0" userId={event.pubkey} size="tiny" />}
|
||||||
{isMuted ? (
|
{isMuted ? (
|
||||||
<div className="truncate">{t('[muted]')}</div>
|
<div className="truncate">{t('[muted]')}</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="truncate">{event.content}</div>
|
<ContentPreview className="truncate" event={event} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -64,7 +64,13 @@ export function SimpleUsername({
|
|||||||
skeletonClassName?: string
|
skeletonClassName?: string
|
||||||
}) {
|
}) {
|
||||||
const { profile } = useFetchProfile(userId)
|
const { profile } = useFetchProfile(userId)
|
||||||
if (!profile) return <Skeleton className={cn('w-16 my-1', skeletonClassName)} />
|
if (!profile) {
|
||||||
|
return (
|
||||||
|
<div className="py-1">
|
||||||
|
<Skeleton className={cn('w-16', skeletonClassName)} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const { username } = profile
|
const { username } = profile
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export default {
|
|||||||
'switch to dark theme': 'switch to dark theme',
|
'switch to dark theme': 'switch to dark theme',
|
||||||
'switch to system theme': 'switch to system theme',
|
'switch to system theme': 'switch to system theme',
|
||||||
Note: 'Note',
|
Note: 'Note',
|
||||||
|
note: 'note',
|
||||||
"username's following": "{{username}}'s following",
|
"username's following": "{{username}}'s following",
|
||||||
"username's used relays": "{{username}}'s used relays",
|
"username's used relays": "{{username}}'s used relays",
|
||||||
"username's muted": "{{username}}'s muted",
|
"username's muted": "{{username}}'s muted",
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export default {
|
|||||||
'switch to dark theme': '切换到深色主题',
|
'switch to dark theme': '切换到深色主题',
|
||||||
'switch to system theme': '切换到系统主题',
|
'switch to system theme': '切换到系统主题',
|
||||||
Note: '笔记',
|
Note: '笔记',
|
||||||
|
note: '笔记',
|
||||||
"username's following": '{{username}} 的关注',
|
"username's following": '{{username}} 的关注',
|
||||||
"username's used relays": '{{username}} 使用的服务器',
|
"username's used relays": '{{username}} 使用的服务器',
|
||||||
"username's muted": '{{username}} 屏蔽的用户',
|
"username's muted": '{{username}} 屏蔽的用户',
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
|
import ContentPreview from '@/components/ContentPreview'
|
||||||
import Nip22ReplyNoteList from '@/components/Nip22ReplyNoteList'
|
import Nip22ReplyNoteList from '@/components/Nip22ReplyNoteList'
|
||||||
import Note from '@/components/Note'
|
import Note from '@/components/Note'
|
||||||
import PictureNote from '@/components/PictureNote'
|
import PictureNote from '@/components/PictureNote'
|
||||||
import ReplyNoteList from '@/components/ReplyNoteList'
|
import ReplyNoteList from '@/components/ReplyNoteList'
|
||||||
import UserAvatar from '@/components/UserAvatar'
|
import UserAvatar from '@/components/UserAvatar'
|
||||||
import Username from '@/components/Username'
|
import { SimpleUsername } from '@/components/Username'
|
||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
@@ -27,7 +28,25 @@ const NotePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref
|
|||||||
return (
|
return (
|
||||||
<SecondaryPageLayout ref={ref} index={index} title={t('Note')}>
|
<SecondaryPageLayout ref={ref} index={index} title={t('Note')}>
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<Skeleton className="w-10 h-10 rounded-full" />
|
<div className="flex items-center space-x-2">
|
||||||
|
<Skeleton className="w-10 h-10 rounded-full" />
|
||||||
|
<div className={`flex-1 w-0`}>
|
||||||
|
<div className="py-1">
|
||||||
|
<Skeleton className="h-4 w-16" />
|
||||||
|
</div>
|
||||||
|
<div className="py-0.5">
|
||||||
|
<Skeleton className="h-3 w-12" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="pt-2">
|
||||||
|
<div className="my-1">
|
||||||
|
<Skeleton className="w-full h-4 my-1 mt-2" />
|
||||||
|
</div>
|
||||||
|
<div className="my-1">
|
||||||
|
<Skeleton className="w-2/3 h-4 my-1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SecondaryPageLayout>
|
</SecondaryPageLayout>
|
||||||
)
|
)
|
||||||
@@ -55,7 +74,7 @@ const NotePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref
|
|||||||
<ParentNote key={`root-note-${event.id}`} eventId={rootEventId} />
|
<ParentNote key={`root-note-${event.id}`} eventId={rootEventId} />
|
||||||
)}
|
)}
|
||||||
<ParentNote key={`parent-note-${event.id}`} eventId={parentEventId} />
|
<ParentNote key={`parent-note-${event.id}`} eventId={parentEventId} />
|
||||||
<Note key={`note-${event.id}`} event={event} fetchNoteStats />
|
<Note key={`note-${event.id}`} event={event} fetchNoteStats hideParentNotePreview />
|
||||||
</div>
|
</div>
|
||||||
<Separator className="mb-2 mt-4" />
|
<Separator className="mb-2 mt-4" />
|
||||||
{event.kind === kinds.ShortTextNote ? (
|
{event.kind === kinds.ShortTextNote ? (
|
||||||
@@ -74,23 +93,52 @@ NotePage.displayName = 'NotePage'
|
|||||||
export default NotePage
|
export default NotePage
|
||||||
|
|
||||||
function ParentNote({ eventId }: { eventId?: string }) {
|
function ParentNote({ eventId }: { eventId?: string }) {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { push } = useSecondaryPage()
|
const { push } = useSecondaryPage()
|
||||||
const { event } = useFetchEvent(eventId)
|
const { event, isFetching } = useFetchEvent(eventId)
|
||||||
if (!event) return null
|
if (!eventId) return null
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Card
|
||||||
|
className="flex space-x-1 p-1 items-center clickable text-sm text-muted-foreground hover:text-foreground"
|
||||||
|
onClick={() => push(toNote(eventId))}
|
||||||
|
>
|
||||||
|
<Skeleton className="shrink w-4 h-4 rounded-full" />
|
||||||
|
<div className="py-1 flex-1">
|
||||||
|
<Skeleton className="h-3" />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<div className="ml-5 w-px h-2 bg-border" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Card className="flex p-1 items-center justify-center text-sm text-muted-foreground">
|
||||||
|
{t('Not found')}
|
||||||
|
</Card>
|
||||||
|
<div className="ml-5 w-px h-2 bg-border" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Card
|
<Card
|
||||||
className="flex space-x-1 p-1 items-center clickable text-sm text-muted-foreground hover:text-foreground"
|
className="flex space-x-1 p-1 items-center clickable text-sm text-muted-foreground hover:text-foreground"
|
||||||
onClick={() => push(toNote(event))}
|
onClick={() => push(toNote(eventId))}
|
||||||
>
|
>
|
||||||
<UserAvatar userId={event.pubkey} size="tiny" className="shrink-0" />
|
<UserAvatar userId={event.pubkey} size="tiny" className="shrink-0" />
|
||||||
<Username
|
<SimpleUsername
|
||||||
userId={event.pubkey}
|
userId={event.pubkey}
|
||||||
className="font-semibold"
|
className="font-semibold truncate shrink-0"
|
||||||
skeletonClassName="h-4 shrink-0"
|
skeletonClassName="h-3 shrink-0"
|
||||||
/>
|
/>
|
||||||
<div className="truncate">{event.content}</div>
|
<ContentPreview className="truncate" event={event} />
|
||||||
</Card>
|
</Card>
|
||||||
<div className="ml-5 w-px h-2 bg-border" />
|
<div className="ml-5 w-px h-2 bg-border" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user