From 78984d8abcd44565d363402e4fb0ce3e46841f22 Mon Sep 17 00:00:00 2001 From: "whey.party" Date: Tue, 13 Jan 2026 00:11:17 +0700 Subject: [PATCH] Polls initial writes --- src/components/UniversalPostRenderer.tsx | 166 +++++++++++++++++++++-- 1 file changed, 155 insertions(+), 11 deletions(-) diff --git a/src/components/UniversalPostRenderer.tsx b/src/components/UniversalPostRenderer.tsx index d362666..22aa370 100644 --- a/src/components/UniversalPostRenderer.tsx +++ b/src/components/UniversalPostRenderer.tsx @@ -1,4 +1,5 @@ import * as ATPAPI from "@atproto/api"; +import { useQuery } from "@tanstack/react-query"; import { useNavigate } from "@tanstack/react-router"; import DOMPurify from "dompurify"; import { useAtom } from "jotai"; @@ -16,6 +17,7 @@ import { } from "~/utils/atoms"; import { useHydratedEmbed } from "~/utils/useHydrated"; import { + constructConstellationQuery, useQueryArbitrary, useQueryConstellation, useQueryIdentity, @@ -2143,9 +2145,82 @@ const stopgap = { }; function PollEmbed({ did, rkey }: { did: string; rkey: string }) { + const { agent } = useAuth(); const pollUri = `at://${did}/app.reddwarf.embed.poll/${rkey}`; const { data: pollRecord, isLoading, error } = useQueryArbitrary(pollUri); + // Query vote counts for each option + const [constellationurl] = useAtom(constellationURLAtom); + + const { data: voteCountsA } = useQueryConstellation({ + method: "/links/count/distinct-dids", + target: pollUri, + collection: "app.reddwarf.poll.vote.a", + path: ".subject.uri", + }); + + const { data: voteCountsB } = useQueryConstellation({ + method: "/links/count/distinct-dids", + target: pollUri, + collection: "app.reddwarf.poll.vote.b", + path: ".subject.uri", + }); + + const { data: voteCountsC } = useQueryConstellation({ + method: "/links/count/distinct-dids", + target: pollUri, + collection: "app.reddwarf.poll.vote.c", + path: ".subject.uri", + }); + + const { data: voteCountsD } = useQueryConstellation({ + method: "/links/count/distinct-dids", + target: pollUri, + collection: "app.reddwarf.poll.vote.d", + path: ".subject.uri", + }); + + // Query first page of voters for each option to get PFPs + const { data: votersA } = useQuery( + constructConstellationQuery({ + constellation: constellationurl, + method: "/links", + target: pollUri, + collection: "app.reddwarf.poll.vote.a", + path: ".subject.uri", + }), + ); + + const { data: votersB } = useQuery( + constructConstellationQuery({ + constellation: constellationurl, + method: "/links", + target: pollUri, + collection: "app.reddwarf.poll.vote.b", + path: ".subject.uri", + }), + ); + + const { data: votersC } = useQuery( + constructConstellationQuery({ + constellation: constellationurl, + method: "/links", + target: pollUri, + collection: "app.reddwarf.poll.vote.c", + path: ".subject.uri", + }), + ); + + const { data: votersD } = useQuery( + constructConstellationQuery({ + constellation: constellationurl, + method: "/links", + target: pollUri, + collection: "app.reddwarf.poll.vote.d", + path: ".subject.uri", + }), + ); + if (isLoading) { return (
@@ -2187,6 +2262,55 @@ function PollEmbed({ did, rkey }: { did: string; rkey: string }) { }) : null; + // Calculate vote counts + const voteData = [ + { + option: "a", + count: parseInt((voteCountsA as any)?.total || "0"), + voters: (votersA as any)?.linking_records || [], + }, + { + option: "b", + count: parseInt((voteCountsB as any)?.total || "0"), + voters: (votersB as any)?.linking_records || [], + }, + { + option: "c", + count: parseInt((voteCountsC as any)?.total || "0"), + voters: (votersC as any)?.linking_records || [], + }, + { + option: "d", + count: parseInt((voteCountsD as any)?.total || "0"), + voters: (votersD as any)?.linking_records || [], + }, + ].slice(0, options.length); + + const totalVotes = voteData.reduce((sum, item) => sum + item.count, 0); + + const handleVote = async (option: string) => { + if (!agent || isExpired) return; + + try { + await agent.com.atproto.repo.createRecord({ + collection: `app.reddwarf.poll.vote.${option}`, + repo: agent.assertDid, + record: { + $type: `app.reddwarf.poll.vote.${option}`, + subject: { + $type: "com.atproto.repo.strongRef", + uri: pollUri, + cid: pollRecord.cid, + }, + createdAt: new Date().toISOString(), + }, + // Let PDS generate rkey automatically + }); + } catch (error) { + console.error("Failed to vote:", error); + } + }; + return (
{/* Header */} @@ -2201,20 +2325,40 @@ function PollEmbed({ did, rkey }: { did: string; rkey: string }) { {poll.multiple ? "Select multiple options" : "Select one option"} + + {/* Total Votes */} + + {totalVotes} vote{totalVotes !== 1 ? "s" : ""} +
- {/* Options List */} + {/* Options List with Results */}
- {options.map((optionText, index) => ( -
- - {optionText} - -
- ))} + {options.map((optionText, index) => { + const optionKey = ["a", "b", "c", "d"][index]; + + return ( +
!isExpired && handleVote(optionKey)} + > + {/* Option text */} + + {optionText} + + + {/* Vote count */} + + {voteData.find(v => v.option === optionKey)?.count ?? 0} + +
+ ); + })}
{/* Footer */} -- 2.51.2