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')} +

+
+ +
);