refactor: new notes button

This commit is contained in:
codytseng
2025-04-14 12:41:57 +08:00
parent 8c141bbbe4
commit 1c3e54c895
16 changed files with 82 additions and 102 deletions

View File

@@ -1,84 +1,68 @@
import React, { useState, useEffect } from 'react'
import UserAvatar from '@/components/UserAvatar'
import { Button } from '@/components/ui/button'
import { SimpleUserAvatar } from '@/components/UserAvatar'
import { cn } from '@/lib/utils'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
export type User = {
pubkey: string
}
interface NewNotesButtonProps {
users?: User[]
eventCount?: number
onShowEvents?: () => void
}
const NewNotesButton: React.FC<NewNotesButtonProps> = ({
users = [],
eventCount: initialEventCount = 0,
onShowEvents
}) => {
const [newNotesCount, setNewNotesCount] = useState(initialEventCount)
export default function NewNotesButton({
newEvents = [],
onClick
}: {
newEvents?: Event[]
onClick?: () => void
}) {
const { t } = useTranslation()
const { isSmallScreen } = useScreenSize()
const { deepBrowsing } = useDeepBrowsing()
useEffect(() => {
setNewNotesCount(initialEventCount)
}, [initialEventCount])
const handleClick = () => {
if (onShowEvents) {
onShowEvents()
} else {
console.log('Showing new notes...')
const pubkeys = useMemo(() => {
const arr: string[] = []
for (const event of newEvents) {
if (!arr.includes(event.pubkey)) {
arr.push(event.pubkey)
}
if (arr.length >= 3) break
}
setNewNotesCount(0)
}
const getDesktopPosition = () => {
return deepBrowsing
? 'absolute left-0 right-0 top-[3.5rem] w-full flex justify-center z-50'
: 'absolute left-0 right-0 top-[6.5rem] w-full flex justify-center z-50'
}
return arr
}, [newEvents])
return (
<>
{newNotesCount > 0 && (
{newEvents.length > 0 && (
<div
className={
isSmallScreen
? 'fixed left-0 right-0 w-full flex justify-center z-[9999]'
: getDesktopPosition()
}
className={cn(
'w-full flex justify-center z-40',
isSmallScreen ? 'fixed' : 'absolute bottom-4'
)}
style={isSmallScreen ? { bottom: 'calc(4rem + env(safe-area-inset-bottom))' } : undefined}
>
<button
onClick={handleClick}
className="flex items-center bg-purple-600 hover:bg-purple-700 text-white px-3 py-2 rounded-full text-sm font-medium shadow-lg"
<Button
onClick={onClick}
className="group rounded-full h-fit pl-2 pr-3 hover:bg-primary-hover"
>
{users && users.length > 0 && (
<div className="flex items-center mr-1">
{users.slice(0, 3).map((user, index) => (
{pubkeys.length > 0 && (
<div className="flex items-center">
{pubkeys.map((pubkey, index) => (
<div
key={user.pubkey}
key={pubkey}
className="relative -mr-2.5 last:mr-0"
style={{ zIndex: 3 - index }}
>
<div className="w-7 h-7 rounded-full border-2 border-purple-600 overflow-hidden flex items-center justify-center bg-background">
<UserAvatar userId={user.pubkey} size="small" />
</div>
<SimpleUserAvatar
userId={pubkey}
size="small"
className="border-primary border-2 group-hover:border-primary-hover"
/>
</div>
))}
</div>
)}
<span className="whitespace-nowrap ml-1">
Show {newNotesCount > 99 ? '99+' : newNotesCount} new events
</span>
</button>
<div className="text-md font-medium">
{t('Show n new notes', { n: newEvents.length > 99 ? '99+' : newEvents.length })}
</div>
</Button>
</div>
)}
</>
)
}
export default NewNotesButton

View File

@@ -59,6 +59,14 @@ export default function NoteList({
}
}, [JSON.stringify(filter), isPictures])
const topRef = useRef<HTMLDivElement | null>(null)
const filteredNewEvents = useMemo(() => {
return newEvents.filter((event: Event) => {
return (
(!filterMutedNotes || !mutePubkeys.includes(event.pubkey)) &&
(listMode !== 'posts' || !isReplyNoteEvent(event))
)
})
}, [newEvents, listMode, filterMutedNotes, mutePubkeys])
useEffect(() => {
if (relayUrls.length === 0 && !noteFilter.authors?.length) return
@@ -168,29 +176,6 @@ export default function NoteList({
setNewEvents([])
}
const newUsers = useMemo(() => {
return newEvents
.filter((event: Event) => {
return (
(!filterMutedNotes || !mutePubkeys.includes(event.pubkey)) &&
(listMode !== 'posts' || !isReplyNoteEvent(event))
)
})
.slice(0, 3)
.map((event) => {
return {
pubkey: event.pubkey
}
})
}, [newEvents, filterMutedNotes, mutePubkeys, listMode])
const filteredNewEventsCount = newEvents.filter((event: Event) => {
return (
(!filterMutedNotes || !mutePubkeys.includes(event.pubkey)) &&
(listMode !== 'posts' || !isReplyNoteEvent(event))
)
}).length
return (
<div className={className}>
<ListModeSwitch
@@ -203,12 +188,8 @@ export default function NoteList({
}}
/>
<div ref={topRef} />
{filteredNewEventsCount > 0 && (
<NewNotesButton
users={newUsers}
eventCount={filteredNewEventsCount}
onShowEvents={showNewEvents}
/>
{filteredNewEvents.length > 0 && (
<NewNotesButton newEvents={filteredNewEvents} onClick={showNewEvents} />
)}
<PullToRefresh
onRefresh={async () => {

View File

@@ -216,6 +216,7 @@ export default {
'Media upload service': 'خدمة تحميل الوسائط',
'Choose a relay': 'اختر ريلاي',
'no relays found': 'لم يتم العثور على ريلايات',
video: 'فيديو'
video: 'فيديو',
'Show n new notes': 'عرض {{n}} ملاحظات جديدة'
}
}

View File

@@ -220,6 +220,7 @@ export default {
'Media upload service': 'Medien-Upload-Service',
'Choose a relay': 'Wähle ein Relay',
'no relays found': 'Keine Relays gefunden',
video: 'Video'
video: 'Video',
'Show n new notes': 'Zeige {{n}} neue Notizen'
}
}

View File

@@ -216,6 +216,7 @@ export default {
'Media upload service': 'Media upload service',
'Choose a relay': 'Choose a relay',
'no relays found': 'no relays found',
video: 'video'
video: 'video',
'Show n new notes': 'Show {{n}} new notes'
}
}

View File

@@ -220,6 +220,7 @@ export default {
'Media upload service': 'Servicio de carga de medios',
'Choose a relay': 'Selecciona un relé',
'no relays found': 'no se encontraron relés',
video: 'video'
video: 'video',
'Show n new notes': 'Mostrar {{n}} nuevas notas'
}
}

View File

@@ -219,6 +219,7 @@ export default {
'Media upload service': 'Service de téléchargement de médias',
'Choose a relay': 'Choisir un relais',
'no relays found': 'aucun relais trouvé',
video: 'vidéo'
video: 'vidéo',
'Show n new notes': 'Afficher {{n}} nouvelles notes'
}
}

View File

@@ -219,6 +219,7 @@ export default {
'Media upload service': 'Servizio di caricamento media',
'Choose a relay': 'Scegli un relay',
'no relays found': 'Nessun relay trovato',
video: 'video'
video: 'video',
'Show n new notes': 'Mostra {{n}} nuove note'
}
}

View File

@@ -217,6 +217,7 @@ export default {
'Media upload service': 'メディアアップロードサービス',
'Choose a relay': 'リレイを選択',
'no relays found': 'リレイが見つかりません',
video: 'ビデオ'
video: 'ビデオ',
'Show n new notes': '新しいノートを{{n}}件表示'
}
}

View File

@@ -218,6 +218,7 @@ export default {
'Media upload service': 'Usługa przesyłania mediów',
'Choose a relay': 'Wybierz transmiter',
'no relays found': 'Nie znaleziono transmiterów',
video: 'wideo'
video: 'wideo',
'Show n new notes': 'Pokaż {{n}} nowych wpisów'
}
}

View File

@@ -218,6 +218,7 @@ export default {
'Media upload service': 'Serviço de upload de mídia',
'Choose a relay': 'Escolher um relé',
'no relays found': 'nenhum relé encontrado',
video: 'vídeo'
video: 'vídeo',
'Show n new notes': 'Mostrar {{n}} novas notas'
}
}

View File

@@ -219,6 +219,7 @@ export default {
'Media upload service': 'Serviço de Upload de Mídia',
'Choose a relay': 'Escolher um Relé',
'no relays found': 'nenhum relé encontrado',
video: 'vídeo'
video: 'vídeo',
'Show n new notes': 'Mostrar {{n}} novas notas'
}
}

View File

@@ -220,6 +220,7 @@ export default {
'Media upload service': 'Служба загрузки медиафайлов',
'Choose a relay': 'Выберите ретранслятор',
'no relays found': 'ретрансляторы не найдены',
video: 'видео'
video: 'видео',
'Show n new notes': 'Показать {{n}} новых заметок'
}
}

View File

@@ -217,6 +217,7 @@ export default {
'Media upload service': '媒体上传服务',
'Choose a relay': '选择一个服务器',
'no relays found': '未找到服务器',
video: '视频'
video: '视频',
'Show n new notes': '显示 {{n}} 条新笔记'
}
}

View File

@@ -56,6 +56,7 @@
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 259 43% 56%;
--primary-hover: 259 43% 65%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
@@ -83,6 +84,7 @@
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 259 43% 56%;
--primary-hover: 259 43% 65%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;