const { data: baseline } = useQuery({ queryKey: ["block-raised", ...], queryFn });
const delta = usePipelineDonationSum(campaignID, {
start: baseline?.asOf,
end: scheduleItem.ends_at,
currency: baseline?.currency ?? undefined,
});
const raised = (baseline?.raised ?? 0) + (delta.raised ?? 0);
Reduce the WebSocket-driven
donations[campaignID]slice into a per-currency sum inside a caller-supplied[start, end)window.Purpose
Overlays that display "$X raised this block" for a per-streamer schedule item historically polled
GET /schedules/campaigns/{id}/raisedevery 30 s. That endpoint sumstiltify_campaign_donations— the same tablesaveDonation()writes to insidehandleTiltifyWebhookData, right before the handler fans the donation out over the WebSocket firehose aspublic:direct:donation_updated. The store's_processDonationaction pushes every such donation ontodonations[campaign_id], so overlays already carry a stream of every donation that would ever count toward the sum.This hook closes the loop: pass the REST endpoint's
asOftimestamp asoptions.startand the block'sends_atasoptions.end, and the returnedraisedvalue is the pure post-baseline delta. Combine it with the baseline scalar to get a live block total that updates on every donation without a fetch.Semantics
startand exclusively againstend(i.e.start <= completed_at < end), matching the REST endpoint's SQLcompleted_at BETWEEN start AND endwhen paired with anasOf-basedstart.completed_atare dropped.idso a Tiltify re-delivery of the samedonation_updatedevent doesn't double-count. The last-seen amount wins so post-edit corrections propagate.test: true) are excluded unless PipelineDonationSumOptions.includeTest is set — see the option's JSDoc for why.options.currencyis set, only matching donations are counted and bothraisedandcurrencyare scalar. Otherwiseraisedcollapses to the sole currency present in the window, ornullwhen multiple currencies appear.raised: 0,currency: null,donationCount: 0,byCurrency: []— same shape as the REST endpoint's empty-window response.Memoization
The underlying
donations[campaignID]reference is only replaced when a new donation lands (Zustand + spread semantics inside_processDonation), so the enclosinguseMemore-runs at most once per incoming donation for a given(start, end, currency, includeTest)tuple.