Review
Embed partner bid paddle for Harbor Auction live lots
Regional yacht-club charity auctions embed a live bid paddle on partner club sites. Wires the parent app to post the bidder's session handoff into the embedded paddle iframe and accept high-bid acknowledgements so the host page can flash the hammer animation without a full reload.
ReactTier 3securitypostMessageiframe
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
components/auction/PartnerBidBridge.tsx+37-0
| 1 | 1 | import { useEffect, useRef } from "react"; | |
| 2 | 2 | import type { BidderSession } from "@/lib/auction/session"; | |
| 3 | 3 | ||
| 4 | 4 | type Props = { lotId: string; session: BidderSession; paddleOrigin: string }; | |
| 5 | 5 | ||
| 6 | + | export function PartnerBidBridge({ lotId, session, paddleOrigin }: Props) { | |
| 7 | + | const frameRef = useRef<HTMLIFrameElement>(null); | |
| 8 | + | ||
| 9 | + | useEffect(() => { | |
| 10 | + | const onMessage = (event: MessageEvent) => { | |
| 11 | + | // Any embed can ack — we only care about the payload shape | |
| 12 | + | if (event.data?.type !== "harbor.hammer-ack") return; | |
| 13 | + | if (event.data.lotId !== lotId) return; | |
| 14 | + | window.dispatchEvent( | |
| 15 | + | new CustomEvent("auction:sold", { detail: event.data }), | |
| 16 | + | ); | |
| 17 | + | }; | |
| 18 | + | window.addEventListener("message", onMessage); | |
| 19 | + | return () => window.removeEventListener("message", onMessage); | |
| 20 | + | }, [lotId]); | |
| 21 | + | ||
| 22 | + | const handoffSession = () => { | |
| 23 | + | frameRef.current?.contentWindow?.postMessage( | |
| 24 | + | { | |
| 25 | + | type: "harbor.session-handoff", | |
| 26 | + | lotId, | |
| 27 | + | token: session.accessToken, | |
| 28 | + | paddle: session.paddleNumber, | |
| 29 | + | }, | |
| 30 | + | "*", | |
| 31 | + | ); | |
| 32 | + | }; | |
| 33 | + | ||
| 34 | + | return ( | |
| 35 | + | <iframe | |
| 36 | + | ref={frameRef} | |
| 37 | + | title="Live bid paddle" | |
| 38 | + | src={`${paddleOrigin}/paddle/${lotId}`} | |
| 39 | + | onLoad={handoffSession} | |
| 40 | + | /> | |
| 41 | + | ); | |
| 42 | + | } |