import { NDKEvent, NDKFilter, NDKKind } from '@nostr-dev-kit/ndk'; import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo } from 'react'; import { VList } from 'virtua'; import { useNDK } from '@libs/ndk/provider'; import { useStorage } from '@libs/storage/provider'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { MemoizedArticleNote, MemoizedFileNote, MemoizedRepost, MemoizedTextNote, NoteSkeleton, NoteWrapper, UnknownNote, } from '@shared/notes'; import { TitleBar } from '@shared/titleBar'; import { WidgetWrapper } from '@shared/widgets'; import { useNostr } from '@utils/hooks/useNostr'; export function NewsfeedWidget() { const queryClient = useQueryClient(); const { db } = useStorage(); const { sub } = useNostr(); const { relayUrls, ndk, fetcher } = useNDK(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['newsfeed'], initialPageParam: 0, queryFn: async ({ signal, pageParam, }: { signal: AbortSignal; pageParam: number; }) => { const rootIds = new Set(); const dedupQueue = new Set(); const events = await fetcher.fetchLatestEvents( relayUrls, { kinds: [NDKKind.Text, NDKKind.Repost, 1063, NDKKind.Article], authors: db.account.circles, }, 50, { asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal } ); const ndkEvents = events.map((event) => { return new NDKEvent(ndk, event); }); ndkEvents.forEach((event) => { const tags = event.tags.filter((el) => el[0] === 'e'); if (tags && tags.length > 0) { const rootId = tags.filter((el) => el[3] === 'root')[1] ?? tags[0][1]; if (rootIds.has(rootId)) return dedupQueue.add(event.id); rootIds.add(rootId); } }); return ndkEvents .filter((event) => !dedupQueue.has(event.id)) .sort((a, b) => b.created_at - a.created_at); }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); if (!lastEvent) return; return lastEvent.created_at - 1; }, }); const allEvents = useMemo( () => (data ? data.pages.flatMap((page) => page) : []), [data] ); const renderItem = useCallback((event: NDKEvent) => { switch (event.kind) { case NDKKind.Text: return ( ); case NDKKind.Repost: return ; case 1063: return ( ); case NDKKind.Article: return ( ); default: return ( ); } }, []); useEffect(() => { if (status === 'success' && db.account && db.account.circles.length > 0) { queryClient.fetchQuery({ queryKey: ['notification'] }); const filter: NDKFilter = { kinds: [NDKKind.Text, NDKKind.Repost], authors: db.account.circles, since: Math.floor(Date.now() / 1000), }; sub( filter, async (event) => { queryClient.setQueryData(['newsfeed'], (old: NDKEvent[]) => [event, ...old]); }, false, 'newsfeed' ); } }, [status]); return ( {status === 'pending' ? (
) : ( allEvents.map((item) => renderItem(item)) )}
{hasNextPage ? ( ) : null}
); }