feat: improve "show new notes" button (#268)
This commit is contained in:
84
src/components/NewNotesButton/index.tsx
Normal file
84
src/components/NewNotesButton/index.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import UserAvatar from '@/components/UserAvatar'
|
||||||
|
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||||
|
import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider'
|
||||||
|
|
||||||
|
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)
|
||||||
|
const { isSmallScreen } = useScreenSize()
|
||||||
|
const { deepBrowsing } = useDeepBrowsing()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNewNotesCount(initialEventCount)
|
||||||
|
}, [initialEventCount])
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (onShowEvents) {
|
||||||
|
onShowEvents()
|
||||||
|
} else {
|
||||||
|
console.log('Showing new notes...')
|
||||||
|
}
|
||||||
|
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 (
|
||||||
|
<>
|
||||||
|
{newNotesCount > 0 && (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
isSmallScreen
|
||||||
|
? 'fixed left-0 right-0 w-full flex justify-center z-[9999]'
|
||||||
|
: getDesktopPosition()
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
{users && users.length > 0 && (
|
||||||
|
<div className="flex items-center mr-1">
|
||||||
|
{users.slice(0, 3).map((user, index) => (
|
||||||
|
<div
|
||||||
|
key={user.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>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className="whitespace-nowrap ml-1">
|
||||||
|
Show {newNotesCount > 99 ? '99+' : newNotesCount} new events
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewNotesButton
|
||||||
@@ -4,6 +4,7 @@ import { ExtendedKind } from '@/constants'
|
|||||||
import { isReplyNoteEvent } from '@/lib/event'
|
import { isReplyNoteEvent } from '@/lib/event'
|
||||||
import { checkAlgoRelay } from '@/lib/relay'
|
import { checkAlgoRelay } from '@/lib/relay'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
import NewNotesButton from '@/components/NewNotesButton'
|
||||||
import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider'
|
import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
import { useNostr } from '@/providers/NostrProvider'
|
import { useNostr } from '@/providers/NostrProvider'
|
||||||
@@ -167,6 +168,29 @@ export default function NoteList({
|
|||||||
setNewEvents([])
|
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 (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<ListModeSwitch
|
<ListModeSwitch
|
||||||
@@ -179,13 +203,13 @@ export default function NoteList({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div ref={topRef} />
|
<div ref={topRef} />
|
||||||
{events.length > 0 &&
|
{filteredNewEventsCount > 0 && (
|
||||||
newEvents.filter((event: Event) => {
|
<NewNotesButton
|
||||||
return (
|
users={newUsers}
|
||||||
(!filterMutedNotes || !mutePubkeys.includes(event.pubkey)) &&
|
eventCount={filteredNewEventsCount}
|
||||||
(listMode !== 'posts' || !isReplyNoteEvent(event))
|
onShowEvents={showNewEvents}
|
||||||
)
|
/>
|
||||||
}).length > 0 && <ShowNewButton onClick={showNewEvents} />}
|
)}
|
||||||
<PullToRefresh
|
<PullToRefresh
|
||||||
onRefresh={async () => {
|
onRefresh={async () => {
|
||||||
setRefreshCount((count) => count + 1)
|
setRefreshCount((count) => count + 1)
|
||||||
@@ -236,24 +260,6 @@ export default function NoteList({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ShowNewButton({ onClick }: { onClick: () => void }) {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { deepBrowsing, lastScrollTop } = useDeepBrowsing()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'sticky top-[6.25rem] flex justify-center w-full my-2 z-30 duration-700 transition-transform',
|
|
||||||
deepBrowsing && lastScrollTop > 800 ? '-translate-y-10' : ''
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Button size="lg" onClick={onClick} className="drop-shadow-xl shadow-primary/50">
|
|
||||||
{t('show new notes')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ListModeSwitch({
|
function ListModeSwitch({
|
||||||
listMode,
|
listMode,
|
||||||
setListMode
|
setListMode
|
||||||
|
|||||||
Reference in New Issue
Block a user