Skip to content

Commit 4a9749b

Browse files
Merge pull request #1958 from Solid-Money/claude/cashback-escrow-status-W0JQd
Add escrow status and countdown timer for cashback releases
2 parents f0f2a2c + 1140c4b commit 4a9749b

4 files changed

Lines changed: 44 additions & 5 deletions

File tree

app/(protected)/(tabs)/activity/[clientTxId].tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Linking, Pressable, View } from 'react-native';
33
import { useLocalSearchParams, useRouter } from 'expo-router';
44
import * as Sentry from '@sentry/react-native';
55
import { useQuery } from '@tanstack/react-query';
6-
import { format, minutesToSeconds } from 'date-fns';
6+
import { format, formatDistanceStrict, minutesToSeconds } from 'date-fns';
77
import { ArrowUpRight, ChevronLeft, X } from 'lucide-react-native';
88
import { mainnet } from 'viem/chains';
99

@@ -92,6 +92,21 @@ const Value = memo(function Value({ children, className }: ValueProps) {
9292
return <Text className={cn('text-lg font-bold', className)}>{children}</Text>;
9393
});
9494

95+
const EscrowTimeLeft = memo(function EscrowTimeLeft({ payoutAt }: { payoutAt: string }) {
96+
const [now, setNow] = useState(() => Date.now());
97+
98+
useEffect(() => {
99+
const interval = setInterval(() => setNow(Date.now()), 60_000);
100+
return () => clearInterval(interval);
101+
}, []);
102+
103+
const target = useMemo(() => new Date(payoutAt).getTime(), [payoutAt]);
104+
105+
if (target - now <= 0) return <Value>Releasing soon</Value>;
106+
107+
return <Value>{formatDistanceStrict(target, now)}</Value>;
108+
});
109+
95110
const Back = memo(function Back({ title, className }: BackProps) {
96111
const router = useRouter();
97112
const params = useLocalSearchParams<{ tab?: string; from?: string }>();
@@ -217,12 +232,24 @@ const CardTransactionDetail = memo(function CardTransactionDetail({
217232
<Value
218233
className={cashbackInfo.amount === 'Pending' ? 'text-yellow-500' : 'text-[#34C759]'}
219234
>
220-
{cashbackInfo.isPending && cashbackInfo.amount !== 'Pending'
221-
? `${cashbackInfo.amount} (Pending)`
222-
: cashbackInfo.amount}
235+
{cashbackInfo.amount === 'Pending'
236+
? cashbackInfo.isEscrowed
237+
? 'Escrowed'
238+
: 'Pending'
239+
: cashbackInfo.isEscrowed
240+
? `${cashbackInfo.amount} (Escrowed)`
241+
: cashbackInfo.isPending
242+
? `${cashbackInfo.amount} (Pending)`
243+
: cashbackInfo.amount}
223244
</Value>
224245
),
225246
},
247+
cashbackInfo?.isEscrowed &&
248+
cashbackInfo.payoutAt && {
249+
key: 'cashback-escrow-time-left',
250+
label: <Label>Releases in</Label>,
251+
value: <EscrowTimeLeft payoutAt={cashbackInfo.payoutAt} />,
252+
},
226253
txHash && {
227254
key: 'explorer',
228255
label: <Label>Explorer</Label>,

components/Activity/CardTransactions.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@ export default function CardTransactions() {
207207
<View className="mt-0.5 flex-row items-center gap-1">
208208
<Diamond />
209209
<Text className="text-sm text-[#8E8E93]">
210-
{cashbackInfo.isPending ? 'Cashback (Pending)' : 'Cashback'}
210+
{cashbackInfo.isEscrowed
211+
? 'Cashback (Escrowed)'
212+
: cashbackInfo.isPending
213+
? 'Cashback (Pending)'
214+
: 'Cashback'}
211215
</Text>
212216
</View>
213217
)}

lib/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,12 +923,15 @@ export interface Cashback {
923923
fuseUsdPrice?: string;
924924
fiatAmount?: string;
925925
fiatCurrency?: string;
926+
payoutAt?: string;
926927
createdAt: string;
927928
}
928929

929930
export interface CashbackInfo {
930931
amount: string;
931932
isPending: boolean;
933+
isEscrowed: boolean;
934+
payoutAt?: string;
932935
}
933936

934937
export interface SourceDepositInstructions {

lib/utils/cardHelpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,15 @@ export const getCashbackAmount = (
117117
}
118118

119119
const isPending = PENDING_CASHBACK_STATUSES.includes(cashback.status);
120+
const isEscrowed = cashback.status === CashbackStatus.Escrowed;
120121

121122
// For pending cashbacks without fuseAmount yet, show pending indicator without amount
122123
if (!cashback.fuseAmount) {
123124
return {
124125
amount: 'Pending',
125126
isPending: true,
127+
isEscrowed,
128+
payoutAt: cashback.payoutAt,
126129
};
127130
}
128131

@@ -137,5 +140,7 @@ export const getCashbackAmount = (
137140
return {
138141
amount: `+$${amount.toFixed(2)}`,
139142
isPending,
143+
isEscrowed,
144+
payoutAt: cashback.payoutAt,
140145
};
141146
};

0 commit comments

Comments
 (0)