From 6e03a4abb9935be03552ddd715a1ce4146e59a34 Mon Sep 17 00:00:00 2001 From: Chad Curtis Date: Sat, 27 Jun 2026 12:40:18 -0500 Subject: [PATCH] Showcase all Venezuela relief campaigns instead of one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the single baked-in terremoto-venezuela campaign with a live, agnostic showcase: every Venezuela-located campaign tagged `humanitarian-aid` or `emergency-relief` and created since the earthquake is pulled in and rendered as a card carousel across the home hero and the dedicated relief page. - New useVenezuelaReliefCampaigns hook queries kind 33863 filtered by `#i: [iso3166:VE]` + `#t: [humanitarian-aid, emergency-relief]` + `since` (quake timestamp), and aggregates on-chain raised totals the same way useProfileCampaignStats does (one /address lookup per campaign, summed; no per-receipt /tx fan-out). The flagship terremoto-venezuela campaign predates the geo-tag convention and carries no `iso3166:VE` tag, so it's pinned by coordinate and merged ahead of the filtered results, deduped by aTag. - Extend useCampaigns with `categories` (`#t`) and `since` options. - New VenezuelaReliefShowcase: an auto-panning marquee of CampaignCards (seamless -50% loop, hover/focus pause, click-drag + momentum, edge fades, prefers-reduced-motion fallback to a native scroll rail), ported from the surveil deck shelf. - Hero/page CTAs and the popup donate button now point at the relief page (no more single-campaign naddr deep-link); the page CTA scrolls to the showcase. Drop the embedded CampaignDetailPage, the .relief-donate-flash CSS, and the now-unused scroll-to-campaign logic. - VenezuelaReliefGoal now reads the aggregate hook and shows the combined raised total + matching-campaign count only (no goal bar — an aggregate goal across independent campaigns is meaningless). - Tighten the hero height so the carousel sits near the fold. - Locale updates across all 16 languages: drop `startCampaign`/`goalOf`, add `showcaseTitle` + `campaignCount`. --- package-lock.json | 4 +- src/components/VenezuelaReliefBanner.tsx | 36 ++- src/components/VenezuelaReliefGoal.tsx | 84 +++--- src/components/VenezuelaReliefPopup.tsx | 3 +- src/components/VenezuelaReliefShowcase.tsx | 332 +++++++++++++++++++++ src/hooks/useCampaigns.ts | 22 +- src/hooks/useVenezuelaReliefCampaign.ts | 73 ----- src/hooks/useVenezuelaReliefCampaigns.ts | 125 ++++++++ src/index.css | 33 -- src/lib/venezuelaRelief.ts | 70 +++-- src/locales/ar.json | 5 +- src/locales/en.json | 5 +- src/locales/es.json | 5 +- src/locales/fa.json | 5 +- src/locales/fr.json | 5 +- src/locales/hi.json | 5 +- src/locales/id.json | 5 +- src/locales/km.json | 5 +- src/locales/ps.json | 5 +- src/locales/pt.json | 5 +- src/locales/ru.json | 5 +- src/locales/sn.json | 7 +- src/locales/sw.json | 5 +- src/locales/tr.json | 5 +- src/locales/zh-Hant.json | 5 +- src/locales/zh.json | 5 +- src/pages/VenezuelaReliefPage.tsx | 99 ++---- 27 files changed, 673 insertions(+), 290 deletions(-) create mode 100644 src/components/VenezuelaReliefShowcase.tsx delete mode 100644 src/hooks/useVenezuelaReliefCampaign.ts create mode 100644 src/hooks/useVenezuelaReliefCampaigns.ts diff --git a/package-lock.json b/package-lock.json index 4f631e63..f71ba94b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "agora", - "version": "2.8.9", + "version": "2.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agora", - "version": "2.8.9", + "version": "2.9.0", "dependencies": { "@breeztech/breez-sdk-spark": "^0.10.0", "@capacitor/app": "^8.0.0", diff --git a/src/components/VenezuelaReliefBanner.tsx b/src/components/VenezuelaReliefBanner.tsx index e125c6c2..e5374705 100644 --- a/src/components/VenezuelaReliefBanner.tsx +++ b/src/components/VenezuelaReliefBanner.tsx @@ -5,11 +5,11 @@ import { HeartHandshake, Share2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { HeroBanner } from '@/components/HeroBanner'; import { VenezuelaReliefGoal } from '@/components/VenezuelaReliefGoal'; +import { VenezuelaReliefShowcase } from '@/components/VenezuelaReliefShowcase'; import { useShareOrigin } from '@/hooks/useShareOrigin'; import { useToast } from '@/hooks/useToast'; import { shareOrCopy } from '@/lib/share'; import { - VENEZUELA_DONATE_PATH, VENEZUELA_RELIEF_IMAGES, VENEZUELA_RELIEF_PATH, } from '@/lib/venezuelaRelief'; @@ -40,9 +40,13 @@ const VENEZUELA_RELIEF_BANNER_IMAGES = VENEZUELA_RELIEF_IMAGES; * - A large display headline ("Venezuela needs you") with the final * word painted inside a solid brand-orange highlighter block — the * same idiom as the home hero's "unstoppable". - * - A primary call to action — **Donate to relief** — deep-links - * straight to the baked-in relief campaign (its naddr) so donors land - * on the campaign's detail page, plus a **Share** action. + * - A primary call to action — **Donate to relief** — links to the + * dedicated relief page ({@link VENEZUELA_RELIEF_PATH}), which showcases + * every Venezuela campaign tagged for relief, plus a **Share** action. + * + * Beneath the hero sits a live showcase rail + * ({@link VenezuelaReliefShowcase}) of those same matching campaigns, so + * donors can pick a specific effort without leaving the home page. * * Not dismissible by design — while the appeal is active it stays put * for every visitor (product decision). When the response winds down, @@ -123,12 +127,12 @@ export function VenezuelaReliefBanner({ className }: { className?: string }) { }} /> - {/* Layer 3 — content. Fills ~85% of the initial viewport so it - reads as the headline of the day rather than a sibling band, - with a sensible minimum on very short / very tall screens. + {/* Layer 3 — content. Sized so the headline + CTAs read as the + page's hero while still leaving the showcase rail below partly + in view above the fold (no full-viewport gap to scroll past). `dvh` so mobile browser chrome (collapsing address bar) doesn't jump the height. */} -
+

- {/* Primary CTA — donate to Venezuela-filtered relief campaigns */} + {/* Primary CTA — the dedicated relief page showcasing every + Venezuela campaign tagged for relief. */}

+ + {/* Showcase rail — every matching relief campaign, pulled in live so + donors can pick a specific effort straight from the home page. + Sits on a translucent panel anchored to the bottom of the hero so + it reads as part of the appeal, over the photo gallery. Renders + nothing until campaigns resolve. */} +
+
+ +
+
); } diff --git a/src/components/VenezuelaReliefGoal.tsx b/src/components/VenezuelaReliefGoal.tsx index 6480ad88..6350a629 100644 --- a/src/components/VenezuelaReliefGoal.tsx +++ b/src/components/VenezuelaReliefGoal.tsx @@ -1,103 +1,87 @@ import { useTranslation } from 'react-i18next'; -import { Progress } from '@/components/ui/progress'; import { Skeleton } from '@/components/ui/skeleton'; import { cn } from '@/lib/utils'; -import { formatCampaignAmount, formatUsdGoal } from '@/lib/formatCampaignAmount'; -import { useVenezuelaReliefCampaign } from '@/hooks/useVenezuelaReliefCampaign'; +import { formatCampaignAmount } from '@/lib/formatCampaignAmount'; +import { useVenezuelaReliefCampaigns } from '@/hooks/useVenezuelaReliefCampaigns'; interface VenezuelaReliefGoalProps { /** - * `overlay` — light text + translucent track, for the dark hero photo - * backgrounds (banner + page). `card` — foreground text + muted track, - * for the popup's light card surface. + * `overlay` — light text, for the dark hero photo backgrounds (banner + + * page). `card` — foreground text, for the popup's light card surface. */ variant?: 'overlay' | 'card'; className?: string; } /** - * Live fundraising readout for the baked-in Venezuela relief campaign — - * the raised total, goal, donation count, and a progress bar. Shared by - * the home hero ({@link VenezuelaReliefBanner}), the session popup + * Aggregate fundraising readout for the Venezuela relief showcase — the + * combined raised total across every matching campaign, plus the + * matching-campaign count. Shared by the home hero + * ({@link VenezuelaReliefBanner}), the session popup * ({@link VenezuelaReliefPopup}), and the dedicated page * ({@link VenezuelaReliefPage}) so each surface is an info + donation - * hybrid backed by the same numbers as the campaign detail page. + * hybrid backed by the same numbers. * - * Renders nothing once loaded if the campaign can't be resolved or has no - * goal/raised data — the surrounding appeal copy and CTAs stand on their + * No goal or progress bar: the appeal spans many independent campaigns + * (each with its own goal, shown on its card), so an aggregate "goal" is + * meaningless here — we surface the combined raised total only. + * + * Renders nothing once loaded if no matching campaigns resolve, or they've + * raised nothing yet — the surrounding appeal copy and CTAs stand on their * own, so this never leaves an empty box. */ export function VenezuelaReliefGoal({ variant = 'overlay', className }: VenezuelaReliefGoalProps) { const { t } = useTranslation(); - const { isLoading, raisedSats, goalUsd, donationCount, percent, btcPrice } = - useVenezuelaReliefCampaign(); + const { isLoading, raisedSats, campaignCount, btcPrice } = + useVenezuelaReliefCampaigns(); const isOverlay = variant === 'overlay'; if (isLoading) { return (
- - + +
); } // Nothing meaningful to show — let the appeal copy carry the surface. - if (raisedSats <= 0 && !goalUsd) return null; + if (raisedSats <= 0) return null; const raisedLabel = formatCampaignAmount(raisedSats, btcPrice); return (
-
+ + {raisedLabel} - {raisedLabel} - - {t('campaignsDetail.raised')} - + {t('campaignsDetail.raised')} - {goalUsd ? ( - - {t('campaigns.home.venezuelaRelief.goalOf', { amount: formatUsdGoal(goalUsd) })} - - ) : null} -
+ - {percent !== undefined && ( - - )} - - {donationCount > 0 && ( + {campaignCount > 0 && (

- {t('campaignsDetail.donationCount', { count: donationCount })} + {t('campaigns.home.venezuelaRelief.campaignCount', { count: campaignCount })}

)}
diff --git a/src/components/VenezuelaReliefPopup.tsx b/src/components/VenezuelaReliefPopup.tsx index 7dce68f1..65b52b99 100644 --- a/src/components/VenezuelaReliefPopup.tsx +++ b/src/components/VenezuelaReliefPopup.tsx @@ -17,7 +17,6 @@ import { useShareOrigin } from '@/hooks/useShareOrigin'; import { useToast } from '@/hooks/useToast'; import { shareOrCopy } from '@/lib/share'; import { - VENEZUELA_DONATE_PATH, VENEZUELA_RELIEF_IMAGES, VENEZUELA_RELIEF_PATH, VENEZUELA_RELIEF_POPUP_SEEN_KEY, @@ -129,7 +128,7 @@ export function VenezuelaReliefPopup() {
- {/* The actual relief campaign, baked in: story, donate panel, - ledger, and comments — the full detail UI for the campaign at - VENEZUELA_DONATE_PATH. The "Donate to relief" CTA flashes a - highlight ring on the donate panel (mobile) or this section - (desktop) via the `.relief-donate-flash` class — see - handleScrollToCampaign. */} -
- + {/* Body: how-it-works explainer + the live showcase of every matching + Venezuela relief campaign. */} +
+
+

+ {t('campaigns.home.venezuelaRelief.pageHow')} +

+

+ {t('campaigns.home.venezuelaRelief.pageHowBody')} +

+
+ +
);