Skip to content

Commit 5a7bcb4

Browse files
authored
Merge pull request #973 from MakePrisms/fix/unify-offer-gift-card-pattern
refactor: unify offer and gift card rendering patterns
2 parents 2949c52 + e4713a4 commit 5a7bcb4

6 files changed

Lines changed: 73 additions & 84 deletions

File tree

app/features/gift-cards/gift-cards.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import {
1616
import { DiscoverGiftCards } from './discover-gift-cards';
1717
import { EmptyState } from './empty-state';
1818
import { GiftCardItem } from './gift-card-item';
19-
import { OfferItem } from './offer-item';
2019
import {
20+
getCardByUrl,
2121
getGiftCardImageByUrl,
2222
useDiscoverGiftCards,
2323
} from './use-discover-cards';
@@ -97,7 +97,12 @@ export function GiftCards() {
9797
: undefined,
9898
}}
9999
>
100-
<OfferItem account={account} />
100+
<GiftCardItem
101+
account={account}
102+
image={getCardByUrl(account.mintUrl)?.image}
103+
hideOverlayContent
104+
className="w-full max-w-none"
105+
/>
101106
</button>
102107
))}
103108
</div>

app/features/gift-cards/offer-card-images.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

app/features/gift-cards/offer-details.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import { MoneyWithConvertedAmount } from '~/features/shared/money-with-converted
1717
import { useBuildLinkWithSearchParams } from '~/hooks/use-search-params-link';
1818
import { LinkWithViewTransition } from '~/lib/transitions';
1919
import { CARD_WIDTH } from './card-stack-constants';
20-
import { OfferItem } from './offer-item';
20+
import { GiftCardItem } from './gift-card-item';
21+
import { getCardByUrl } from './use-discover-cards';
2122

2223
function formatExpiryDate(expiresAt: string): string {
2324
const date = new Date(expiresAt);
@@ -83,7 +84,12 @@ export default function OfferDetails({ accountId }: OfferDetailsProps) {
8384
className="w-full"
8485
aria-label={`Close ${offer.name} offer`}
8586
>
86-
<OfferItem account={offer} />
87+
<GiftCardItem
88+
account={offer}
89+
image={getCardByUrl(offer.mintUrl)?.image}
90+
hideOverlayContent
91+
className="w-full max-w-none"
92+
/>
8793
</button>
8894
</div>
8995

app/features/gift-cards/offer-item.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

app/features/gift-cards/use-discover-cards.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,39 @@ import compassCoffeeCard from '~/assets/gift-cards/compass.agi.cash.webp';
44
import mapleCard from '~/assets/gift-cards/maple.agi.cash.webp';
55
import pinkOwlCoffeeCard from '~/assets/gift-cards/pinkowl.agi.cash.webp';
66
import pubkeyCard from '~/assets/gift-cards/pubkey.agi.cash.webp';
7+
import sfFreeCoffeeCard from '~/assets/gift-cards/sf-free-coffee.webp';
78
import theShackCard from '~/assets/gift-cards/shack.agi.cash.webp';
89
import theEpicurianTraderCard from '~/assets/gift-cards/theepicureantrader.agi.cash.webp';
910
import { useAccounts } from '../accounts/account-hooks';
1011
import {
1112
type GiftCardConfig,
1213
JsonGiftCardConfigSchema,
1314
} from './gift-card-config';
15+
import {
16+
JsonOfferCardConfigSchema,
17+
type OfferCardConfig,
18+
} from './offer-card-config';
19+
20+
export type CardInfo = {
21+
url: string;
22+
name: string;
23+
image: string;
24+
addCardDisclaimer?: string;
25+
};
1426

1527
export type GiftCardInfo = GiftCardConfig & {
1628
image: string;
1729
};
1830

19-
const GIFT_CARD_IMAGES: Record<string, string> = {
31+
const CARD_IMAGES: Record<string, string> = {
2032
'https://blockandbean.agi.cash': blockAndBeanCard,
2133
'https://pubkey.agi.cash': pubkeyCard,
2234
'https://maple.agi.cash': mapleCard,
2335
'https://compass.agi.cash': compassCoffeeCard,
2436
'https://pinkowl.agi.cash': pinkOwlCoffeeCard,
2537
'https://shack.agi.cash': theShackCard,
2638
'https://theepicureantrader.agi.cash': theEpicurianTraderCard,
39+
'http://localhost:8104': sfFreeCoffeeCard,
2740
};
2841

2942
function loadGiftCardsFromEnv(): GiftCardInfo[] {
@@ -33,11 +46,31 @@ function loadGiftCardsFromEnv(): GiftCardInfo[] {
3346
// Validated at build time by vite.config.ts — safe to throw here.
3447
return JsonGiftCardConfigSchema.parse(raw).map((card) => ({
3548
...card,
36-
image: GIFT_CARD_IMAGES[card.url] ?? '',
49+
image: CARD_IMAGES[card.url] ?? '',
3750
}));
3851
}
3952

53+
type OfferCardInfo = OfferCardConfig & {
54+
image: string;
55+
};
56+
57+
function loadOfferCardsFromEnv(): OfferCardInfo[] {
58+
const raw = import.meta.env.VITE_OFFER_CARDS;
59+
if (!raw) return [];
60+
61+
return JsonOfferCardConfigSchema.parse(raw).map((card) => {
62+
const image = CARD_IMAGES[card.url];
63+
if (!image) {
64+
throw new Error(
65+
`Missing image for offer card: ${card.url}. Add an entry to CARD_IMAGES.`,
66+
);
67+
}
68+
return { ...card, image };
69+
});
70+
}
71+
4072
export const GIFT_CARDS: GiftCardInfo[] = loadGiftCardsFromEnv();
73+
export const OFFER_CARDS: OfferCardInfo[] = loadOfferCardsFromEnv();
4174

4275
/**
4376
* Returns the gift card image for a given URL, if one exists.
@@ -53,6 +86,18 @@ export function getGiftCardByUrl(url: string): GiftCardInfo | undefined {
5386
return GIFT_CARDS.find((card) => card.url === url);
5487
}
5588

89+
/**
90+
* Returns card info (image, name, etc.) for a given mint URL,
91+
* regardless of whether it's a gift card or offer card.
92+
*/
93+
export function getCardByUrl(url: string): CardInfo | undefined {
94+
const giftCard = GIFT_CARDS.find((card) => card.url === url);
95+
if (giftCard) return giftCard;
96+
const offerCard = OFFER_CARDS.find((card) => card.url === url);
97+
if (offerCard) return offerCard;
98+
return undefined;
99+
}
100+
56101
/**
57102
* Returns gift cards that the user has not yet added.
58103
*/

app/features/receive/receive-cashu-token.tsx

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ import {
2929
import { getAccountHomePath } from '../accounts/account';
3030
import { AccountSelector } from '../accounts/account-selector';
3131
import { GiftCardItem } from '../gift-cards/gift-card-item';
32-
import { OfferItem } from '../gift-cards/offer-item';
33-
import { getGiftCardByUrl } from '../gift-cards/use-discover-cards';
32+
import { getCardByUrl } from '../gift-cards/use-discover-cards';
3433
import { tokenToMoney } from '../shared/cashu';
3534
import { getErrorMessage } from '../shared/error';
3635
import { MoneyWithConvertedAmount } from '../shared/money-with-converted-amount';
@@ -119,7 +118,7 @@ export default function ReceiveToken({
119118
setReceiveAccount,
120119
addAndSetReceiveAccount,
121120
} = useReceiveCashuTokenAccounts(token, preferredReceiveAccountId);
122-
const giftCard = getGiftCardByUrl(sourceAccount.mintUrl);
121+
const card = getCardByUrl(sourceAccount.mintUrl);
123122

124123
const isReceiveAccountKnown = receiveAccount?.isUnknown === false;
125124

@@ -209,21 +208,19 @@ export default function ReceiveToken({
209208
<div className="absolute top-0 right-0 bottom-0 left-0 mx-auto flex max-w-sm items-center justify-center">
210209
{claimableToken && receiveAccount ? (
211210
<div className="w-full max-w-sm px-4">
212-
{giftCard ? (
211+
{card ? (
213212
<div className="flex flex-col items-center gap-3">
214213
<GiftCardItem
215214
account={sourceAccount}
216-
image={giftCard?.image}
215+
image={card.image}
217216
hideOverlayContent
218217
/>
219-
{giftCard?.addCardDisclaimer && (
218+
{card.addCardDisclaimer && (
220219
<p className="text-center text-muted-foreground text-sm">
221-
{giftCard.addCardDisclaimer}
220+
{card.addCardDisclaimer}
222221
</p>
223222
)}
224223
</div>
225-
) : sourceAccount.purpose === 'offer' ? (
226-
<OfferItem account={sourceAccount} />
227224
) : (
228225
<AccountSelector
229226
accounts={selectableAccounts}
@@ -310,7 +307,7 @@ export function PublicReceiveCashuToken({ token }: { token: Token }) {
310307
token,
311308
});
312309

313-
const giftCard = getGiftCardByUrl(sourceAccount.mintUrl);
310+
const card = getCardByUrl(sourceAccount.mintUrl);
314311

315312
const encodedToken = getEncodedToken(claimableToken ?? token);
316313

@@ -376,21 +373,19 @@ export function PublicReceiveCashuToken({ token }: { token: Token }) {
376373
<div className="absolute top-0 right-0 bottom-0 left-0 mx-auto flex max-w-sm items-center justify-center">
377374
{claimableToken && sourceAccount.canReceive ? (
378375
<div className="w-full max-w-sm px-4">
379-
{giftCard ? (
376+
{card ? (
380377
<div className="flex flex-col items-center gap-3">
381378
<GiftCardItem
382379
account={sourceAccount}
383-
image={giftCard?.image}
380+
image={card.image}
384381
hideOverlayContent
385382
/>
386-
{giftCard?.addCardDisclaimer && (
383+
{card.addCardDisclaimer && (
387384
<p className="text-center text-muted-foreground text-sm">
388-
{giftCard.addCardDisclaimer}
385+
{card.addCardDisclaimer}
389386
</p>
390387
)}
391388
</div>
392-
) : sourceAccount.purpose === 'offer' ? (
393-
<OfferItem account={sourceAccount} />
394389
) : (
395390
<AccountSelector
396391
accounts={selectableAccounts}

0 commit comments

Comments
 (0)