Drop donor count from campaign cards to skip the per-receipt tx fan-out
The card footer showed "x donors", sourced from useCampaignDonations' donorCount. Populating that count required fetching every kind 8333 receipt for the campaign and then verifying each one with a per-receipt Esplora /tx call — a fan-out that, across a ~200-card grid, hammered every configured Esplora backend. Cards only ever render the raised total (the progress bar), which needs just the single /address balance lookup. Add a receipts option to useCampaignDonations that skips the receipt fetch and verification fan-out, and have CampaignCard pass receipts: false. Remove the now-dead donor-count UI and its unused t()/useTranslation import.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useMemo, useRef } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { HandHeart, ShieldCheck } from 'lucide-react';
|
||||
|
||||
@@ -164,7 +163,6 @@ interface CampaignCardProps {
|
||||
* `<Link>` to the campaign's naddr-based detail route.
|
||||
*/
|
||||
export function CampaignCard({ campaign, variant = 'compact', className, footerBadge, showModerationMenu = true }: CampaignCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const { translatedEvent, translateAction } = useEventTranslation(campaign.event, {
|
||||
iconOnly: true,
|
||||
buttonClassName: 'size-8 rounded-full p-0 text-muted-foreground hover:text-primary hover:bg-primary/10',
|
||||
@@ -180,8 +178,14 @@ export function CampaignCard({ campaign, variant = 'compact', className, footerB
|
||||
// already there by the time the user sees it.
|
||||
const cardRef = useRef<HTMLAnchorElement>(null);
|
||||
const inView = useInView(cardRef);
|
||||
// Cards only show the raised total (the progress bar), never the donor
|
||||
// list — so we skip the kind 8333 receipt fetch and the per-receipt
|
||||
// `/tx` verification fan-out. Only the single Esplora `/address` balance
|
||||
// lookup runs, which keeps a ~200-card grid from firing an N-receipt
|
||||
// `/tx` storm per card.
|
||||
const { data: stats, isLoading: donationsLoading } = useCampaignDonations(campaign, {
|
||||
enabled: inView,
|
||||
receipts: false,
|
||||
});
|
||||
const { data: btcPrice } = useBtcPrice();
|
||||
|
||||
@@ -328,11 +332,6 @@ export function CampaignCard({ campaign, variant = 'compact', className, footerB
|
||||
<div className="flex items-center justify-between gap-3 border-t border-border/60 pt-3 text-xs text-muted-foreground">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<AuthorByline pubkey={campaign.pubkey} insideLink />
|
||||
{!isSilentPayment && stats && stats.donorCount > 0 && (
|
||||
<span className="shrink-0 text-muted-foreground/80">
|
||||
· {t('common.donors', { count: stats.donorCount })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{(footerBadge || translateAction) && (
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
|
||||
@@ -97,12 +97,24 @@ export function useCampaignDonations(
|
||||
* option's default was introduced to stop.
|
||||
*/
|
||||
refetchInterval?: number | false;
|
||||
/**
|
||||
* Skip the kind 8333 receipt fetch and the per-receipt `/tx`
|
||||
* verification fan-out — i.e. everything that powers the donor
|
||||
* list, donor count, and per-tx breakdown. Only the single Esplora
|
||||
* `/address` balance lookup that drives the headline `totalSats`
|
||||
* (the progress bar) runs.
|
||||
*
|
||||
* Card grids only render the raised total, never the donor list, so
|
||||
* they pass `receipts: false` to avoid an N-receipt `/tx` storm per
|
||||
* card. The detail page leaves this `true` to populate its donor UI.
|
||||
*/
|
||||
receipts?: boolean;
|
||||
} = {},
|
||||
): {
|
||||
data: CampaignDonationStats;
|
||||
isLoading: boolean;
|
||||
} {
|
||||
const { enabled = true, refetchInterval = false } = options;
|
||||
const { enabled = true, refetchInterval = false, receipts: fetchReceipts = true } = options;
|
||||
const { nostr } = useNostr();
|
||||
const { config } = useAppContext();
|
||||
const { esploraApis } = config;
|
||||
@@ -146,7 +158,7 @@ export function useCampaignDonations(
|
||||
);
|
||||
return events;
|
||||
},
|
||||
enabled: enabled && !!aTag && hasOnchain,
|
||||
enabled: enabled && fetchReceipts && !!aTag && hasOnchain,
|
||||
staleTime: 15_000,
|
||||
});
|
||||
|
||||
@@ -174,7 +186,7 @@ export function useCampaignDonations(
|
||||
queryFn: ({ signal }: { signal: AbortSignal }) =>
|
||||
verifyOnchainZap(event, esploraApis, walletValue, signal),
|
||||
staleTime: 60_000,
|
||||
enabled: enabled && !!walletValue && hasOnchain,
|
||||
enabled: enabled && fetchReceipts && !!walletValue && hasOnchain,
|
||||
})),
|
||||
});
|
||||
|
||||
@@ -205,8 +217,8 @@ export function useCampaignDonations(
|
||||
hasOnchain &&
|
||||
(!enabled ||
|
||||
addressQuery.isLoading ||
|
||||
receiptsQuery.isLoading ||
|
||||
verifications.some((v) => v.isLoading));
|
||||
(fetchReceipts &&
|
||||
(receiptsQuery.isLoading || verifications.some((v) => v.isLoading))));
|
||||
|
||||
return {
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user