feat: collapse long replies and profile bios

This commit is contained in:
codytseng
2025-05-16 23:14:03 +08:00
parent e1fbda5efc
commit fa5e198b8a
3 changed files with 73 additions and 65 deletions

View File

@@ -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>
) )
} }

View File

@@ -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>
) )
} }

View File

@@ -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" />