feat: collapse long replies and profile bios
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
export default function Collapsible({
|
export default function Collapsible({
|
||||||
alwaysExpand = false,
|
alwaysExpand = false,
|
||||||
children,
|
children,
|
||||||
|
className,
|
||||||
threshold = 1000,
|
threshold = 1000,
|
||||||
collapsedHeight = 600,
|
collapsedHeight = 600,
|
||||||
...props
|
...props
|
||||||
@@ -45,30 +47,30 @@ export default function Collapsible({
|
|||||||
}, [alwaysExpand, shouldCollapse])
|
}, [alwaysExpand, shouldCollapse])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} {...props}>
|
<div
|
||||||
<div
|
className={cn('relative text-left overflow-hidden', className)}
|
||||||
className="relative text-left overflow-hidden"
|
ref={containerRef}
|
||||||
style={{
|
{...props}
|
||||||
maxHeight: !shouldCollapse || expanded ? 'none' : `${collapsedHeight}px`
|
style={{
|
||||||
}}
|
maxHeight: !shouldCollapse || expanded ? 'none' : `${collapsedHeight}px`
|
||||||
>
|
}}
|
||||||
{children}
|
>
|
||||||
{shouldCollapse && !expanded && (
|
{children}
|
||||||
<div className="absolute bottom-0 h-40 w-full bg-gradient-to-b from-transparent to-background/90 flex items-end justify-center pb-4">
|
{shouldCollapse && !expanded && (
|
||||||
<div className="bg-background rounded-md">
|
<div className="absolute bottom-0 h-40 w-full bg-gradient-to-b from-transparent to-background/90 flex items-end justify-center pb-4">
|
||||||
<Button
|
<div className="bg-background rounded-md">
|
||||||
className="bg-foreground hover:bg-foreground/80"
|
<Button
|
||||||
onClick={(e) => {
|
className="bg-foreground hover:bg-foreground/80"
|
||||||
e.stopPropagation()
|
onClick={(e) => {
|
||||||
setExpanded(!expanded)
|
e.stopPropagation()
|
||||||
}}
|
setExpanded(!expanded)
|
||||||
>
|
}}
|
||||||
{t('Show more')}
|
>
|
||||||
</Button>
|
{t('Show more')}
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import NoteStats from '../NoteStats'
|
|||||||
import ParentNotePreview from '../ParentNotePreview'
|
import ParentNotePreview from '../ParentNotePreview'
|
||||||
import UserAvatar from '../UserAvatar'
|
import UserAvatar from '../UserAvatar'
|
||||||
import Username from '../Username'
|
import Username from '../Username'
|
||||||
|
import Collapsible from '../Collapsible'
|
||||||
|
|
||||||
export default function ReplyNote({
|
export default function ReplyNote({
|
||||||
event,
|
event,
|
||||||
@@ -35,49 +36,51 @@ export default function ReplyNote({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex space-x-2 items-start px-4 py-3 border-b transition-colors duration-500 clickable ${highlight ? 'bg-primary/50' : ''}`}
|
className={`pb-3 border-b transition-colors duration-500 clickable ${highlight ? 'bg-primary/50' : ''}`}
|
||||||
onClick={() => push(toNote(event))}
|
onClick={() => push(toNote(event))}
|
||||||
>
|
>
|
||||||
<UserAvatar userId={event.pubkey} size="small" className="shrink-0" />
|
<Collapsible threshold={600} collapsedHeight={400}>
|
||||||
<div className="w-full overflow-hidden">
|
<div className="flex space-x-2 items-start px-4 pt-3">
|
||||||
<div className="flex items-start justify-between gap-2">
|
<UserAvatar userId={event.pubkey} className="shrink-0 h-8 w-8" />
|
||||||
<div className="flex gap-2 items-center flex-1">
|
<div className="w-full overflow-hidden">
|
||||||
<Username
|
<div className="flex items-start justify-between gap-2">
|
||||||
userId={event.pubkey}
|
<div className="flex gap-2 items-center flex-1">
|
||||||
className="text-sm font-semibold text-muted-foreground hover:text-foreground truncate"
|
<Username
|
||||||
skeletonClassName="h-3"
|
userId={event.pubkey}
|
||||||
/>
|
className="text-sm font-semibold text-muted-foreground hover:text-foreground truncate"
|
||||||
<div className="text-xs text-muted-foreground shrink-0">
|
skeletonClassName="h-3"
|
||||||
<FormattedTimestamp timestamp={event.created_at} />
|
/>
|
||||||
|
<div className="text-xs text-muted-foreground shrink-0">
|
||||||
|
<FormattedTimestamp timestamp={event.created_at} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NoteOptions event={event} className="shrink-0 [&_svg]:size-5" />
|
||||||
</div>
|
</div>
|
||||||
|
{parentEventId && (
|
||||||
|
<ParentNotePreview
|
||||||
|
className="mt-2"
|
||||||
|
eventId={parentEventId}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onClickParent()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{show ? (
|
||||||
|
<Content className="mt-2" event={event} />
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="text-muted-foreground font-medium mt-2"
|
||||||
|
onClick={() => setShowMuted(true)}
|
||||||
|
>
|
||||||
|
{t('Temporarily display this reply')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<NoteOptions event={event} className="shrink-0 [&_svg]:size-5" />
|
|
||||||
</div>
|
</div>
|
||||||
{parentEventId && (
|
</Collapsible>
|
||||||
<ParentNotePreview
|
{show && <NoteStats className="ml-14 mr-4 mt-2" event={event} displayTopZapsAndLikes />}
|
||||||
className="mt-2"
|
|
||||||
eventId={parentEventId}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
onClickParent()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{show ? (
|
|
||||||
<>
|
|
||||||
<Content className="mt-2" event={event} />
|
|
||||||
<NoteStats className="mt-2" event={event} displayTopZapsAndLikes />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="text-muted-foreground font-medium mt-2"
|
|
||||||
onClick={() => setShowMuted(true)}
|
|
||||||
>
|
|
||||||
{t('Temporarily display this reply')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import NotFoundPage from '../NotFoundPage'
|
import NotFoundPage from '../NotFoundPage'
|
||||||
import Followings from './Followings'
|
import Followings from './Followings'
|
||||||
import Relays from './Relays'
|
import Relays from './Relays'
|
||||||
|
import Collapsible from '@/components/Collapsible'
|
||||||
|
|
||||||
const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => {
|
const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -116,10 +117,12 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },
|
|||||||
<PubkeyCopy pubkey={pubkey} />
|
<PubkeyCopy pubkey={pubkey} />
|
||||||
<QrCodePopover pubkey={pubkey} />
|
<QrCodePopover pubkey={pubkey} />
|
||||||
</div>
|
</div>
|
||||||
<ProfileAbout
|
<Collapsible>
|
||||||
about={about}
|
<ProfileAbout
|
||||||
className="text-wrap break-words whitespace-pre-wrap mt-2 select-text"
|
about={about}
|
||||||
/>
|
className="text-wrap break-words whitespace-pre-wrap mt-2 select-text"
|
||||||
|
/>
|
||||||
|
</Collapsible>
|
||||||
{website && (
|
{website && (
|
||||||
<div className="flex gap-1 items-center text-primary mt-2 truncate select-text">
|
<div className="flex gap-1 items-center text-primary mt-2 truncate select-text">
|
||||||
<Link size={14} className="shrink-0" />
|
<Link size={14} className="shrink-0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user