diff --git a/src/components/Note/Poll.tsx b/src/components/Note/Poll.tsx index 9ada9357..03eff953 100644 --- a/src/components/Note/Poll.tsx +++ b/src/components/Note/Poll.tsx @@ -10,7 +10,7 @@ import dayjs from 'dayjs' import { Skeleton } from '@/components/ui/skeleton' import { CheckCircle2 } from 'lucide-react' import { Event } from 'nostr-tools' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import logger from '@/lib/logger' @@ -42,6 +42,12 @@ export default function Poll({ event, className }: { event: Event; className?: s return Boolean(isExpired) || resultsRevealed || event.pubkey === pubkey || !canVote }, [isExpired, resultsRevealed, event.pubkey, pubkey, canVote]) const [containerElement, setContainerElement] = useState(null) + /** Stops viewport-triggered refetch loops when the first load fails or yields no subscriber update. */ + const pollResultsViewportFetchDoneRef = useRef(false) + + useEffect(() => { + pollResultsViewportFetchDoneRef.current = false + }, [event.id]) const fetchResults = useCallback(async () => { const meta = getPollMetadataFromEvent(event) @@ -62,12 +68,21 @@ export default function Poll({ event, className }: { event: Event; className?: s logger.error('Failed to fetch poll results', { error, eventId: event.id }) toast.error('Failed to fetch poll results: ' + (error as Error).message) } finally { + pollResultsViewportFetchDoneRef.current = true setIsLoadingResults(false) } }, [event]) useEffect(() => { - if (pollResults || isLoadingResults || !containerElement) return + if ( + isExpired || + pollResults || + isLoadingResults || + !containerElement || + pollResultsViewportFetchDoneRef.current + ) { + return + } const observer = new IntersectionObserver( ([entry]) => { @@ -87,7 +102,7 @@ export default function Poll({ event, className }: { event: Event; className?: s return () => { observer.unobserve(containerElement) } - }, [pollResults, isLoadingResults, containerElement, fetchResults]) + }, [isExpired, pollResults, isLoadingResults, containerElement, fetchResults]) useEffect(() => { if (!poll || !isExpired) return diff --git a/src/hooks/useProfileZapPollParticipation.tsx b/src/hooks/useProfileZapPollParticipation.tsx index 47a74d1f..9dc6df86 100644 --- a/src/hooks/useProfileZapPollParticipation.tsx +++ b/src/hooks/useProfileZapPollParticipation.tsx @@ -47,7 +47,7 @@ export function useProfileZapPollParticipation(profilePubkey: string | undefined const urls = participationRelayUrls() const receipts = await client.fetchEvents(urls, { kinds: [kinds.Zap], - '#P': [profilePubkey.trim().toLowerCase()], + '#p': [profilePubkey.trim().toLowerCase()], limit: 300 }) const voteReceipts = filterZapPollVoteReceiptsForVoter(receipts, profilePubkey) diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx index 5c239175..2d45ade7 100644 --- a/src/providers/NostrProvider/index.tsx +++ b/src/providers/NostrProvider/index.tsx @@ -735,7 +735,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { limit: 100 }, { - '#P': [pubkey], + '#p': [pubkey], kinds: [kinds.Zap], limit: 100 } diff --git a/src/services/poll-results.service.ts b/src/services/poll-results.service.ts index 8ae9876f..c4194bbe 100644 --- a/src/services/poll-results.service.ts +++ b/src/services/poll-results.service.ts @@ -142,9 +142,7 @@ class PollResultsService { }) this.pollResultsMap.set(pollEventId, { ...results }) - if (responseEvents.length) { - this.notifyPollResults(pollEventId) - } + this.notifyPollResults(pollEventId) return results }