feat: collapse long notes

This commit is contained in:
codytseng
2025-04-28 22:32:52 +08:00
parent 3ffad2ed49
commit 3a98629617
16 changed files with 95 additions and 28 deletions

View File

@@ -9,7 +9,6 @@ import Content from '../Content'
import { FormattedTimestamp } from '../FormattedTimestamp' import { FormattedTimestamp } from '../FormattedTimestamp'
import ImageGallery from '../ImageGallery' import ImageGallery from '../ImageGallery'
import NoteOptions from '../NoteOptions' import NoteOptions from '../NoteOptions'
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'
@@ -18,16 +17,12 @@ export default function Note({
event, event,
size = 'normal', size = 'normal',
className, className,
hideParentNotePreview = false, hideParentNotePreview = false
hideStats = false,
fetchNoteStats = false
}: { }: {
event: Event event: Event
size?: 'normal' | 'small' size?: 'normal' | 'small'
className?: string className?: string
hideParentNotePreview?: boolean hideParentNotePreview?: boolean
hideStats?: boolean
fetchNoteStats?: boolean
}) { }) {
const { push } = useSecondaryPage() const { push } = useSecondaryPage()
const parentEventId = useMemo( const parentEventId = useMemo(
@@ -78,9 +73,6 @@ export default function Note({
{event.kind === ExtendedKind.PICTURE && imageInfos.length > 0 && ( {event.kind === ExtendedKind.PICTURE && imageInfos.length > 0 && (
<ImageGallery images={imageInfos} /> <ImageGallery images={imageInfos} />
)} )}
{!hideStats && (
<NoteStats className="mt-3" event={event} fetchIfNotExisting={fetchNoteStats} />
)}
</div> </div>
) )
} }

View File

@@ -1,8 +1,13 @@
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator' import { Separator } from '@/components/ui/separator'
import { toNote } from '@/lib/link' import { toNote } from '@/lib/link'
import { cn } from '@/lib/utils'
import { useSecondaryPage } from '@/PageManager' import { useSecondaryPage } from '@/PageManager'
import { Event } from 'nostr-tools' import { Event } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Note from '../Note' import Note from '../Note'
import NoteStats from '../NoteStats'
import RepostDescription from './RepostDescription' import RepostDescription from './RepostDescription'
export default function MainNoteCard({ export default function MainNoteCard({
@@ -16,20 +21,77 @@ export default function MainNoteCard({
reposter?: string reposter?: string
embedded?: boolean embedded?: boolean
}) { }) {
const { t } = useTranslation()
const { push } = useSecondaryPage() const { push } = useSecondaryPage()
const containerRef = useRef<HTMLDivElement>(null)
const [expanded, setExpanded] = useState(false)
const [shouldCollapse, setShouldCollapse] = useState(false)
useEffect(() => {
if (embedded || shouldCollapse) return
const contentEl = containerRef.current
if (!contentEl) return
const checkHeight = () => {
const fullHeight = contentEl.scrollHeight
if (fullHeight > 900) {
setShouldCollapse(true)
}
}
checkHeight()
const observer = new ResizeObserver(() => {
checkHeight()
})
observer.observe(contentEl)
return () => {
observer.disconnect()
}
}, [embedded, shouldCollapse])
return ( return (
<div <div
ref={containerRef}
className={className} className={className}
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
push(toNote(event)) push(toNote(event))
}} }}
> >
<div className={`clickable ${embedded ? 'p-2 sm:p-3 border rounded-lg' : 'py-3'}`}>
<div <div
className={`clickable text-left ${embedded ? 'p-2 sm:p-3 border rounded-lg' : 'px-4 py-3'}`} className="relative text-left overflow-hidden"
style={{
maxHeight: !shouldCollapse || expanded ? 'none' : '600px'
}}
> >
<RepostDescription reposter={reposter} /> <RepostDescription className={embedded ? '' : 'px-4'} reposter={reposter} />
<Note size={embedded ? 'small' : 'normal'} event={event} hideStats={embedded} /> <Note
className={embedded ? '' : 'px-4'}
size={embedded ? 'small' : 'normal'}
event={event}
/>
{shouldCollapse && !expanded && (
<div className="absolute bottom-0 h-20 w-full bg-gradient-to-b from-transparent to-muted flex items-center justify-center">
<div className="bg-background rounded-md">
<Button
className="bg-foreground hover:bg-foreground/80"
onClick={(e) => {
e.stopPropagation()
setExpanded(!expanded)
}}
>
{t('Show more')}
</Button>
</div>
</div>
)}
</div>
{!embedded && <NoteStats className={cn('mt-3', embedded ? '' : 'px-4')} event={event} />}
</div> </div>
{!embedded && <Separator />} {!embedded && <Separator />}
</div> </div>

View File

@@ -119,7 +119,7 @@ export default function NormalPostContent({
{parentEvent && ( {parentEvent && (
<ScrollArea className="flex max-h-48 flex-col overflow-y-auto rounded-lg border bg-muted/40"> <ScrollArea className="flex max-h-48 flex-col overflow-y-auto rounded-lg border bg-muted/40">
<div className="p-2 sm:p-3 pointer-events-none"> <div className="p-2 sm:p-3 pointer-events-none">
<Note size="small" event={parentEvent} hideStats hideParentNotePreview /> <Note size="small" event={parentEvent} hideParentNotePreview />
</div> </div>
</ScrollArea> </ScrollArea>
)} )}

View File

@@ -223,6 +223,7 @@ export default {
'Remove bookmark': 'إزالة الإشارة', 'Remove bookmark': 'إزالة الإشارة',
'no bookmarks found': 'لم يتم العثور على إشارات', 'no bookmarks found': 'لم يتم العثور على إشارات',
'no more bookmarks': 'لا مزيد من الإشارات', 'no more bookmarks': 'لا مزيد من الإشارات',
Bookmarks: 'الإشارات المرجعية' Bookmarks: 'الإشارات المرجعية',
'Show more': 'عرض المزيد'
} }
} }

View File

@@ -227,6 +227,7 @@ export default {
'Remove bookmark': 'Lesezeichen entfernen', 'Remove bookmark': 'Lesezeichen entfernen',
'no bookmarks found': 'Keine Lesezeichen gefunden', 'no bookmarks found': 'Keine Lesezeichen gefunden',
'no more bookmarks': 'Keine weiteren Lesezeichen', 'no more bookmarks': 'Keine weiteren Lesezeichen',
Bookmarks: 'Lesezeichen' Bookmarks: 'Lesezeichen',
'Show more': 'Mehr anzeigen'
} }
} }

View File

@@ -223,6 +223,7 @@ export default {
'Remove bookmark': 'Remove bookmark', 'Remove bookmark': 'Remove bookmark',
'no bookmarks found': 'no bookmarks found', 'no bookmarks found': 'no bookmarks found',
'no more bookmarks': 'no more bookmarks', 'no more bookmarks': 'no more bookmarks',
Bookmarks: 'Bookmarks' Bookmarks: 'Bookmarks',
'Show more': 'Show more'
} }
} }

View File

@@ -227,6 +227,7 @@ export default {
'Remove bookmark': 'Quitar marcador', 'Remove bookmark': 'Quitar marcador',
'no bookmarks found': 'No se encontraron marcadores', 'no bookmarks found': 'No se encontraron marcadores',
'no more bookmarks': 'No hay más marcadores', 'no more bookmarks': 'No hay más marcadores',
Bookmarks: 'Marcadores' Bookmarks: 'Marcadores',
'Show more': 'Mostrar más'
} }
} }

View File

@@ -226,6 +226,7 @@ export default {
'Remove bookmark': 'Retirer le favori', 'Remove bookmark': 'Retirer le favori',
'no bookmarks found': 'Aucun favori trouvé', 'no bookmarks found': 'Aucun favori trouvé',
'no more bookmarks': 'Plus de favoris', 'no more bookmarks': 'Plus de favoris',
Bookmarks: 'Favoris' Bookmarks: 'Favoris',
'Show more': 'Afficher plus'
} }
} }

View File

@@ -226,6 +226,7 @@ export default {
'Remove bookmark': 'Rimuovi segnalibro', 'Remove bookmark': 'Rimuovi segnalibro',
'no bookmarks found': 'Nessun segnalibro trovato', 'no bookmarks found': 'Nessun segnalibro trovato',
'no more bookmarks': 'Nessun altro segnalibro', 'no more bookmarks': 'Nessun altro segnalibro',
Bookmarks: 'Segnalibri' Bookmarks: 'Segnalibri',
'Show more': 'Mostra di più'
} }
} }

View File

@@ -224,6 +224,7 @@ export default {
'Remove bookmark': 'ブックマークを削除', 'Remove bookmark': 'ブックマークを削除',
'no bookmarks found': 'ブックマークが見つかりません', 'no bookmarks found': 'ブックマークが見つかりません',
'no more bookmarks': 'これ以上ブックマークはありません', 'no more bookmarks': 'これ以上ブックマークはありません',
Bookmarks: 'ブックマーク一覧' Bookmarks: 'ブックマーク一覧',
'Show more': 'もっと見る'
} }
} }

View File

@@ -225,6 +225,7 @@ export default {
'Remove bookmark': 'Usuń zakładkę', 'Remove bookmark': 'Usuń zakładkę',
'no bookmarks found': 'Nie znaleziono zakładek', 'no bookmarks found': 'Nie znaleziono zakładek',
'no more bookmarks': 'Koniec zakładek', 'no more bookmarks': 'Koniec zakładek',
Bookmarks: 'Zakładki' Bookmarks: 'Zakładki',
'Show more': 'Pokaż więcej'
} }
} }

View File

@@ -225,6 +225,7 @@ export default {
'Remove bookmark': 'Remover favorito', 'Remove bookmark': 'Remover favorito',
'no bookmarks found': 'Nenhum favorito encontrado', 'no bookmarks found': 'Nenhum favorito encontrado',
'no more bookmarks': 'Sem mais favoritos', 'no more bookmarks': 'Sem mais favoritos',
Bookmarks: 'Favoritos' Bookmarks: 'Favoritos',
'Show more': 'Mostrar mais'
} }
} }

View File

@@ -226,6 +226,7 @@ export default {
'Remove bookmark': 'Remover favorito', 'Remove bookmark': 'Remover favorito',
'no bookmarks found': 'Nenhum favorito encontrado', 'no bookmarks found': 'Nenhum favorito encontrado',
'no more bookmarks': 'Sem mais favoritos', 'no more bookmarks': 'Sem mais favoritos',
Bookmarks: 'Favoritos' Bookmarks: 'Favoritos',
'Show more': 'Mostrar mais'
} }
} }

View File

@@ -227,6 +227,7 @@ export default {
'Remove bookmark': 'Удалить закладку', 'Remove bookmark': 'Удалить закладку',
'no bookmarks found': 'Закладки не найдены', 'no bookmarks found': 'Закладки не найдены',
'no more bookmarks': 'Больше нет закладок', 'no more bookmarks': 'Больше нет закладок',
Bookmarks: 'Закладки' Bookmarks: 'Закладки',
'Show more': 'Показать больше'
} }
} }

View File

@@ -224,6 +224,7 @@ export default {
'Remove bookmark': '取消收藏', 'Remove bookmark': '取消收藏',
'no bookmarks found': '暂无收藏', 'no bookmarks found': '暂无收藏',
'no more bookmarks': '到底了', 'no more bookmarks': '到底了',
Bookmarks: '收藏' Bookmarks: '收藏',
'Show more': '显示更多'
} }
} }

View File

@@ -2,6 +2,7 @@ import { useSecondaryPage } from '@/PageManager'
import ContentPreview from '@/components/ContentPreview' 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 NoteStats from '@/components/NoteStats'
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'
@@ -74,9 +75,9 @@ const NotePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref
key={`note-${event.id}`} key={`note-${event.id}`}
event={event} event={event}
className="select-text" className="select-text"
fetchNoteStats
hideParentNotePreview hideParentNotePreview
/> />
<NoteStats className="mt-3" event={event} fetchIfNotExisting />
</div> </div>
<Separator className="mt-4" /> <Separator className="mt-4" />
{event.kind === kinds.ShortTextNote ? ( {event.kind === kinds.ShortTextNote ? (