You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
2.9 KiB
93 lines
2.9 KiB
import { ExtendedKind, FAST_READ_RELAY_URLS, SEARCHABLE_RELAY_URLS } from '@/constants' |
|
import { |
|
filterZapPollVoteReceiptsForVoter, |
|
getPollIdFromZapReceipt, |
|
parseZapPollEvent, |
|
userZapPollVoteOption |
|
} from '@/lib/zap-poll' |
|
import { normalizeUrl } from '@/lib/url' |
|
import client from '@/services/client.service' |
|
import { Event } from 'nostr-tools' |
|
import { kinds } from 'nostr-tools' |
|
import { useCallback, useEffect, useMemo, useState } from 'react' |
|
|
|
function participationRelayUrls(): string[] { |
|
const seen = new Set<string>() |
|
const out: string[] = [] |
|
for (const u of [...SEARCHABLE_RELAY_URLS, ...FAST_READ_RELAY_URLS]) { |
|
const n = normalizeUrl(u) || u |
|
if (!n || seen.has(n)) continue |
|
seen.add(n) |
|
out.push(n) |
|
} |
|
return out.slice(0, 14) |
|
} |
|
|
|
export type TZapPollProfileRow = { |
|
poll: Event |
|
voteReceipt: Event |
|
optionIndex: number |
|
} |
|
|
|
/** |
|
* Zap poll votes by `profilePubkey` (kind 9735 with P=profile and k=6969 in embedded zap request), |
|
* resolved to kind 6969 poll events for profile timeline merge. |
|
*/ |
|
export function useProfileZapPollParticipation(profilePubkey: string | undefined) { |
|
const [rows, setRows] = useState<TZapPollProfileRow[]>([]) |
|
const [loading, setLoading] = useState(false) |
|
|
|
const load = useCallback(async () => { |
|
if (!profilePubkey) { |
|
setRows([]) |
|
return |
|
} |
|
setLoading(true) |
|
try { |
|
const urls = participationRelayUrls() |
|
const receipts = await client.fetchEvents(urls, { |
|
kinds: [kinds.Zap], |
|
'#p': [profilePubkey.trim().toLowerCase()], |
|
limit: 300 |
|
}) |
|
const voteReceipts = filterZapPollVoteReceiptsForVoter(receipts, profilePubkey) |
|
const pollIds = [...new Set(voteReceipts.map(getPollIdFromZapReceipt).filter(Boolean) as string[])] |
|
if (pollIds.length === 0) { |
|
setRows([]) |
|
return |
|
} |
|
const polls = await client.fetchEvents(urls, { |
|
kinds: [ExtendedKind.ZAP_POLL], |
|
ids: pollIds, |
|
limit: pollIds.length |
|
}) |
|
const pollById = new Map(polls.map((p) => [p.id, p])) |
|
const built: TZapPollProfileRow[] = [] |
|
for (const vr of voteReceipts) { |
|
const pid = getPollIdFromZapReceipt(vr) |
|
if (!pid) continue |
|
const poll = pollById.get(pid) |
|
if (!poll) continue |
|
const pollMeta = parseZapPollEvent(poll) |
|
if (!pollMeta) continue |
|
const opt = userZapPollVoteOption(poll, pollMeta, profilePubkey, [vr]) |
|
if (opt === undefined) continue |
|
built.push({ poll, voteReceipt: vr, optionIndex: opt }) |
|
} |
|
built.sort((a, b) => b.voteReceipt.created_at - a.voteReceipt.created_at) |
|
setRows(built) |
|
} catch { |
|
setRows([]) |
|
} finally { |
|
setLoading(false) |
|
} |
|
}, [profilePubkey]) |
|
|
|
useEffect(() => { |
|
void load() |
|
}, [load]) |
|
|
|
const pollIdsVoted = useMemo(() => new Set(rows.map((r) => r.poll.id)), [rows]) |
|
|
|
return { rows, loading, reload: load, pollIdsVoted } |
|
}
|
|
|