import { NDKEvent, NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk'; import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo } from 'react'; import { VList } from 'virtua'; import { useArk } from '@libs/ark'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; import { MemoizedNotifyNote, NoteSkeleton } from '@shared/notes'; import { TitleBar, WidgetWrapper } from '@shared/widgets'; import { FETCH_LIMIT } from '@utils/constants'; import { sendNativeNotification } from '@utils/notification'; export function NotificationWidget() { const queryClient = useQueryClient(); const { ark } = useArk(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ queryKey: ['notification'], initialPageParam: 0, queryFn: async ({ signal, pageParam, }: { signal: AbortSignal; pageParam: number; }) => { const events = await ark.getInfiniteEvents({ filter: { kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], '#p': [ark.account.pubkey], }, limit: FETCH_LIMIT, pageParam, signal, }); return events; }, getNextPageParam: (lastPage) => { const lastEvent = lastPage.at(-1); if (!lastEvent) return; return lastEvent.created_at - 1; }, refetchOnWindowFocus: false, refetchOnMount: false, refetchOnReconnect: false, staleTime: Infinity, }); const allEvents = useMemo( () => (data ? data.pages.flatMap((page) => page) : []), [data] ); const renderEvent = useCallback((event: NDKEvent) => { if (event.pubkey === ark.account.pubkey) return null; return ; }, []); useEffect(() => { let sub: NDKSubscription = undefined; if (status === 'success' && ark.account) { const filter = { kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Reaction, NDKKind.Zap], '#p': [ark.account.pubkey], since: Math.floor(Date.now() / 1000), }; sub = ark.subscribe({ filter, closeOnEose: false, cb: async (event) => { queryClient.setQueryData( ['notification'], (prev: { pageParams: number; pages: Array }) => ({ ...prev, pages: [[event], ...prev.pages], }) ); const profile = await ark.getUserProfile({ pubkey: event.pubkey }); switch (event.kind) { case NDKKind.Text: return await sendNativeNotification( `${profile.displayName || profile.name} has replied to your note` ); case NDKKind.EncryptedDirectMessage: { if (location.pathname !== '/chats') { return await sendNativeNotification( `${ profile.displayName || profile.name } has send you a encrypted message` ); } else { break; } } case NDKKind.Repost: return await sendNativeNotification( `${profile.displayName || profile.name} has reposted to your note` ); case NDKKind.Reaction: return await sendNativeNotification( `${profile.displayName || profile.name} has reacted ${ event.content } to your note` ); case NDKKind.Zap: return await sendNativeNotification( `${profile.displayName || profile.name} has zapped to your note` ); default: break; } }, }); } return () => { if (sub) sub.stop(); }; }, [status]); return ( {status === 'pending' ? (
) : allEvents.length < 1 ? (

🎉

Hmm! Nothing new yet.

) : ( allEvents.map((event) => renderEvent(event)) )}
{hasNextPage ? ( ) : null}
); }