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}
);
}