feat: 💨

This commit is contained in:
codytseng
2025-12-01 00:16:52 +08:00
parent 7ec4835c61
commit a6c41d8d3f
2 changed files with 335 additions and 276 deletions

View File

@@ -92,6 +92,8 @@ export default function NormalFeed({
showKinds={temporaryShowKinds} showKinds={temporaryShowKinds}
subRequests={subRequests} subRequests={subRequests}
filterFn={filterFn} filterFn={filterFn}
areAlgoRelays={areAlgoRelays}
showRelayCloseReason={showRelayCloseReason}
/> />
) : ( ) : (
<NoteList <NoteList

View File

@@ -12,6 +12,7 @@ import { useDeletedEvent } from '@/providers/DeletedEventProvider'
import { useMuteList } from '@/providers/MuteListProvider' import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider' import { useNostr } from '@/providers/NostrProvider'
import { usePinnedUsers } from '@/providers/PinnedUsersProvider' import { usePinnedUsers } from '@/providers/PinnedUsersProvider'
import { useReply } from '@/providers/ReplyProvider'
import { useUserTrust } from '@/providers/UserTrustProvider' import { useUserTrust } from '@/providers/UserTrustProvider'
import client from '@/services/client.service' import client from '@/services/client.service'
import userAggregationService, { TUserAggregation } from '@/services/user-aggregation.service' import userAggregationService, { TUserAggregation } from '@/services/user-aggregation.service'
@@ -30,7 +31,9 @@ import {
} from 'react' } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import PullToRefresh from 'react-simple-pull-to-refresh' import PullToRefresh from 'react-simple-pull-to-refresh'
import { toast } from 'sonner'
import { LoadingBar } from '../LoadingBar' import { LoadingBar } from '../LoadingBar'
import NewNotesButton from '../NewNotesButton'
const LIMIT = 500 const LIMIT = 500
const SHOW_COUNT = 20 const SHOW_COUNT = 20
@@ -47,8 +50,21 @@ const UserAggregationList = forwardRef<
showKinds?: number[] showKinds?: number[]
filterFn?: (event: Event) => boolean filterFn?: (event: Event) => boolean
filterMutedNotes?: boolean filterMutedNotes?: boolean
areAlgoRelays?: boolean
showRelayCloseReason?: boolean
} }
>(({ subRequests, showKinds, filterFn, filterMutedNotes = true }, ref) => { >(
(
{
subRequests,
showKinds,
filterFn,
filterMutedNotes = true,
areAlgoRelays = false,
showRelayCloseReason = false
},
ref
) => {
const { t } = useTranslation() const { t } = useTranslation()
const { startLogin } = useNostr() const { startLogin } = useNostr()
const { push } = useSecondaryPage() const { push } = useSecondaryPage()
@@ -57,8 +73,10 @@ const UserAggregationList = forwardRef<
const { pinnedPubkeySet } = usePinnedUsers() const { pinnedPubkeySet } = usePinnedUsers()
const { hideContentMentioningMutedUsers } = useContentPolicy() const { hideContentMentioningMutedUsers } = useContentPolicy()
const { isEventDeleted } = useDeletedEvent() const { isEventDeleted } = useDeletedEvent()
const { addReplies } = useReply()
const [since, setSince] = useState(() => dayjs().subtract(1, 'day').unix()) const [since, setSince] = useState(() => dayjs().subtract(1, 'day').unix())
const [events, setEvents] = useState<Event[]>([]) const [events, setEvents] = useState<Event[]>([])
const [newEvents, setNewEvents] = useState<Event[]>([])
const [timelineKey, setTimelineKey] = useState<string | undefined>(undefined) const [timelineKey, setTimelineKey] = useState<string | undefined>(undefined)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [showLoadingBar, setShowLoadingBar] = useState(true) const [showLoadingBar, setShowLoadingBar] = useState(true)
@@ -102,9 +120,12 @@ const UserAggregationList = forwardRef<
async function init() { async function init() {
setLoading(true) setLoading(true)
setEvents([]) setEvents([])
setNewEvents([])
setHasMore(true)
if (showKinds?.length === 0 && subRequests.every(({ filter }) => !filter.kinds)) { if (showKinds?.length === 0 && subRequests.every(({ filter }) => !filter.kinds)) {
setLoading(false) setLoading(false)
setHasMore(false)
return () => {} return () => {}
} }
@@ -122,25 +143,42 @@ const UserAggregationList = forwardRef<
if (events.length > 0) { if (events.length > 0) {
setEvents(events) setEvents(events)
} }
if (eosed) { if (areAlgoRelays) {
setLoading(false)
if (events.length === 0) {
setHasMore(false) setHasMore(false)
} }
if (eosed) {
setLoading(false)
setHasMore(events.length > 0)
addReplies(events)
} }
}, },
onNew: (event) => { onNew: (event) => {
setEvents((oldEvents) => { setNewEvents((oldEvents) =>
const newEvents = oldEvents.some((e) => e.id === event.id) [event, ...oldEvents].sort((a, b) => b.created_at - a.created_at)
? oldEvents )
: [event, ...oldEvents] addReplies([event])
return newEvents },
}) onClose: (url, reason) => {
if (!showRelayCloseReason) return
// ignore reasons from nostr-tools
if (
[
'closed by caller',
'relay connection errored',
'relay connection closed',
'pingpong timed out',
'relay connection closed by us'
].includes(reason)
) {
return
}
toast.error(`${url}: ${reason}`)
} }
}, },
{ {
startLogin, startLogin,
needSort: true needSort: !areAlgoRelays
} }
) )
setTimelineKey(timelineKey) setTimelineKey(timelineKey)
@@ -218,6 +256,10 @@ const UserAggregationList = forwardRef<
return events.filter((evt) => evt.created_at >= since && !shouldHideEvent(evt)) return events.filter((evt) => evt.created_at >= since && !shouldHideEvent(evt))
}, [events, since, shouldHideEvent]) }, [events, since, shouldHideEvent])
const filteredNewEvents = useMemo(() => {
return newEvents.filter((evt) => evt.created_at >= since && !shouldHideEvent(evt))
}, [newEvents, since, shouldHideEvent])
const aggregations = useMemo(() => { const aggregations = useMemo(() => {
const aggs = userAggregationService.aggregateByUser(filteredEvents) const aggs = userAggregationService.aggregateByUser(filteredEvents)
userAggregationService.saveAggregations(feedId, aggs) userAggregationService.saveAggregations(feedId, aggs)
@@ -286,6 +328,14 @@ const UserAggregationList = forwardRef<
setShowCount(SHOW_COUNT) setShowCount(SHOW_COUNT)
} }
const showNewEvents = () => {
setEvents((oldEvents) => [...newEvents, ...oldEvents])
setNewEvents([])
setTimeout(() => {
scrollToTop('smooth')
}, 0)
}
const list = ( const list = (
<div className="min-h-screen"> <div className="min-h-screen">
{pinnedAggregations.map((agg) => ( {pinnedAggregations.map((agg) => (
@@ -329,7 +379,9 @@ const UserAggregationList = forwardRef<
<div className="border-b h-12 pl-4 pr-1 flex items-center justify-between gap-2"> <div className="border-b h-12 pl-4 pr-1 flex items-center justify-between gap-2">
<div className="text-sm text-muted-foreground flex items-center gap-1.5 min-w-0"> <div className="text-sm text-muted-foreground flex items-center gap-1.5 min-w-0">
<span className="font-medium text-foreground"> <span className="font-medium text-foreground">
{lastXDays === 1 ? t('Last 24 hours') : t('Last {{count}} days', { count: lastXDays })} {lastXDays === 1
? t('Last 24 hours')
: t('Last {{count}} days', { count: lastXDays })}
</span> </span>
· ·
<span> <span>
@@ -359,9 +411,14 @@ const UserAggregationList = forwardRef<
) : ( ) : (
list list
)} )}
<div className="h-40" />
{filteredNewEvents.length > 0 && (
<NewNotesButton newEvents={filteredNewEvents} onClick={showNewEvents} />
)}
</div> </div>
) )
}) }
)
UserAggregationList.displayName = 'UserAggregationList' UserAggregationList.displayName = 'UserAggregationList'
export default UserAggregationList export default UserAggregationList