Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions app/(protected)/(tabs)/card/pending.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { useEffect } from 'react';
import { Image } from 'expo-image';
import { useRouter } from 'expo-router';

import { CardStatusPage } from '@/components/Card/CardStatusPage';
import { path } from '@/constants/path';
import { useCardStatus } from '@/hooks/useCardStatus';
import { getAsset } from '@/lib/assets';
import { CardStatus, KycStatus, RainApplicationStatus } from '@/lib/types';
import { hasCard } from '@/lib/utils';
import { useKycStore } from '@/store/useKycStore';

const POLL_INTERVAL_MS = 5000;

export default function CardPending() {
const router = useRouter();
const { data: cardStatusResponse } = useCardStatus({ refetchInterval: POLL_INTERVAL_MS });
const kycFlow = useKycStore(state => state.kycFlow);

useEffect(() => {
if (!cardStatusResponse) return;

// VA-initiated KYC: keep the user on the pending submission view. They
// re-enter the VA flow via the Deposit modal when KYC + Rain are ready.
if (kycFlow === 'va') return;

// User already has a card (e.g. status synced after this tab was open).
if (hasCard(cardStatusResponse) && cardStatusResponse.status !== CardStatus.PENDING) {
router.replace(path.CARD_DETAILS);
Expand Down Expand Up @@ -45,14 +53,23 @@ export default function CardPending() {
if (kycStatus && kycStatus !== KycStatus.NOT_STARTED) {
router.replace(`${String(path.CARD_ACTIVATE)}?kycStatus=${kycStatus}` as any);
}
}, [cardStatusResponse, router]);
}, [cardStatusResponse, kycFlow, router]);

return (
<CardStatusPage
title="Your ID verification is under review!"
title="Thank you for your submission!"
description={
'Thanks for your submission. Your\nidentity is now being verified. You will be\nnotified by mail once you get approved'
}
header="Identity Verification"
image={
<Image
source={getAsset('images/identity-review.png')}
alt="Identity Verification"
style={{ width: 402, height: 268 }}
contentFit="contain"
/>
}
/>
);
}
Binary file added assets/images/identity-review.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions components/Card/ActivateCard/UnderReviewState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ export function UnderReviewState() {
/>
</View>

<Text className="mt-6 text-2xl font-bold text-white">Your card is on its way!</Text>
<Text className="mt-6 text-2xl font-bold text-white">Thank you for your submission!</Text>
<Text className="my-3 text-center text-[#ACACAC]">
Thanks for your submission. Your{'\n'}identity is now being verified.
Your identity is now being verified. You{'\n'}will be notified by mail once you get{'\n'}
approved
</Text>
</View>
</View>
Expand Down
28 changes: 20 additions & 8 deletions components/Card/CardStatusPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,41 @@ interface CardStatusPageProps {
title: string;
description?: string;
children?: ReactNode;
header?: string;
image?: ReactNode;
}

export function CardStatusPage({ title, description, children }: CardStatusPageProps) {
export function CardStatusPage({
title,
description,
children,
header,
image,
}: CardStatusPageProps) {
return (
<PageLayout desktopOnly contentClassName="pb-10">
<View className="mx-auto w-full max-w-lg px-4 pt-8">
<View className="flex-row items-center justify-between">
<BackButton fallbackHref="/card" />
<Text className="text-center text-xl font-semibold text-white md:text-2xl">
Solid card
{header ? header : 'Solid card'}
</Text>
<View style={{ width: 40 }} />
</View>

<View className="mb-10 mt-8">
<View className="items-center rounded-2xl border border-white/5 bg-[#1C1C1C] px-6 pb-8 pt-10">
<View className="mb-6">
<Image
source={getAsset('images/card-fade.png')}
alt="Solid Card"
style={{ width: 402, height: 268 }}
contentFit="contain"
/>
{image ? (
image
) : (
<Image
source={getAsset('images/card-fade.png')}
alt="Solid Card"
style={{ width: 402, height: 268 }}
contentFit="contain"
/>
)}
</View>

<Text className="mt-2 text-center text-2xl font-bold text-white">{title}</Text>
Expand Down
35 changes: 20 additions & 15 deletions components/CardWaitlist/SolidCardSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@ type FeatureItemProps = {
icon: React.ReactNode;
label: string;
classNames?: {
container?: string
text?: string
container?: string;
text?: string;
};
};

const FeatureItem = ({ icon, label, classNames }: FeatureItemProps) => (
<View className={cn("flex-row items-center gap-2", classNames?.container)}>
<View className={cn('flex-row items-center gap-2', classNames?.container)}>
<View className="h-9 w-9 items-center justify-center rounded-full bg-[#94F27F26]">{icon}</View>
<Text className={cn("text-xl font-medium", classNames?.text)}>{label}</Text>
<Text className={cn('text-xl font-medium', classNames?.text)}>{label}</Text>
</View>
);

const CashbackBadge = () => (
<Text className="text-base text-brand">3%</Text>
);
const CashbackBadge = () => <Text className="text-base text-brand">3%</Text>;

type SolidCardSummaryProps = {
topUpLabel?: string;
Expand All @@ -38,12 +36,7 @@ const SolidCardSummary = ({
return (
<View className={cn('gap-5', className)}>
<View className="gap-2">
<Text
className={cn(
'font-semibold',
compact ? 'text-2xl' : 'text-3xl md:text-4.5xl',
)}
>
<Text className={cn('font-semibold', compact ? 'text-2xl' : 'text-3xl md:text-4.5xl')}>
Solid card
</Text>
<Text className="max-w-48 text-sm text-white/70 md:text-base">
Expand All @@ -54,9 +47,21 @@ const SolidCardSummary = ({
<View className={cn(compact ? 'gap-3' : 'flex-row flex-wrap gap-x-8 gap-y-3')}>
<FeatureItem icon={<CreditCard size={22} color="#94F27F" />} label="Virtual card" />
<FeatureItem icon={<CashbackBadge />} label="3% Cashback" />
{compact && <FeatureItem icon={<Tag size={20} color="#94F27F" />} label={topUpLabel} classNames={{container:"items-start"}} />}
{compact && (
<FeatureItem
icon={<Tag size={20} color="#94F27F" />}
label={topUpLabel}
classNames={{ container: 'items-start' }}
/>
)}
</View>
{!compact && <FeatureItem icon={<Tag size={20} color="#94F27F" />} label={topUpLabel} classNames={{container:"items-start"}} />}
{!compact && (
<FeatureItem
icon={<Tag size={20} color="#94F27F" />}
label={topUpLabel}
classNames={{ container: 'items-start' }}
/>
)}
</View>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,16 @@
import { useCallback, useMemo } from 'react';
import { View } from 'react-native';
import { Image } from 'expo-image';

import DepositOption from '@/components/DepositOption/DepositOption';
import { DEPOSIT_MODAL } from '@/constants/modals';
import { getAsset } from '@/lib/assets';
import { DepositMethod } from '@/lib/types';
import useDepositBuyCryptoOptions from '@/hooks/useDepositBuyCryptoOptions';
import { getVaultDepositConfig } from '@/lib/vaults';
import { useDepositStore } from '@/store/useDepositStore';

const DepositBuyCryptoOptions = () => {
const setModal = useDepositStore(state => state.setModal);
const { buyCryptoOptions } = useDepositBuyCryptoOptions();
const depositConfig = getVaultDepositConfig();

// const handleBankDepositPress = useCallback(() => {
// setModal(DEPOSIT_MODAL.OPEN_BANK_TRANSFER_AMOUNT);
// }, [setModal]);

// const handleCreditCardPress = useCallback(() => {
// setModal(DEPOSIT_MODAL.OPEN_BUY_CRYPTO);
// }, [setModal]);

const buyCryptoOptions = useMemo(
() => [
// {
// text: 'Debit/Credit Card',
// subtitle: 'Google Pay, card or bank account',
// icon: (
// <Image
// source={getAsset('images/buy_crypto.png')}
// style={{ width: 26, height: 22 }}
// contentFit="contain"
// />
// ),
// onPress: handleCreditCardPress,
// method: 'credit_card' as DepositMethod,
// },
// {
// text: 'Bank Deposit',
// subtitle: 'Make a transfer from your bank.',
// icon: (
// <Image
// source={getAsset('images/bank_deposit.png')}
// style={{ width: 26, height: 22 }}
// contentFit="contain"
// />
// ),
// onPress: handleBankDepositPress,
// isComingSoon: false,
// method: 'bank_transfer' as DepositMethod,
// },
],
[
// handleCreditCardPress,
// handleBankDepositPress
],
);

return (
<View className="gap-y-2.5">
{/* {buyCryptoOptions
{buyCryptoOptions
.filter(option => !option.method || depositConfig.methods.includes(option.method))
.map(option => (
<DepositOption
Expand All @@ -68,9 +19,9 @@ const DepositBuyCryptoOptions = () => {
subtitle={option.subtitle}
icon={option.icon}
onPress={option.onPress}
// isComingSoon={option.isComingSoon}
chipText={option.chipText}
/>
))} */}
))}
</View>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useCallback } from 'react';
import { View } from 'react-native';
import { useRouter } from 'expo-router';
import { Building2 } from 'lucide-react-native';

import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { DEPOSIT_MODAL } from '@/constants/modals';
import { path } from '@/constants/path';
import { useCardStatus } from '@/hooks/useCardStatus';
import { RainApplicationStatus } from '@/lib/types';
import { useDepositStore } from '@/store/useDepositStore';
import { useKycStore } from '@/store/useKycStore';

const BENEFITS = [
'A persistent virtual bank account in your name for ACH and Wire deposits.',
'Incoming USD is auto-converted to USDC and deposited as soUSD.',
'No fees from Solid — settlement typically in 1–3 business days.',
];

export const VirtualAccountApplyModal = () => {
const router = useRouter();
const setModal = useDepositStore(state => state.setModal);
const setKycFlow = useKycStore(state => state.setKycFlow);
const { data: cardStatus } = useCardStatus();
const isRainApproved =
cardStatus?.rainApplicationStatus === RainApplicationStatus.APPROVED;

const handleApply = useCallback(() => {
if (isRainApproved) {
setModal(DEPOSIT_MODAL.OPEN_VIRTUAL_ACCOUNT_TOS);
return;
}
setKycFlow('va');
setModal(DEPOSIT_MODAL.CLOSE);
router.push(path.KYC);
}, [isRainApproved, router, setKycFlow, setModal]);

return (
<View className="flex-1 gap-4">
<View className="items-center gap-3 px-2">
<View className="h-16 w-16 items-center justify-center rounded-full bg-[#1C1C1C]">
<Building2 size={28} color="#94F27F" />
</View>
<Text className="text-2xl font-bold text-white">Virtual Bank Account</Text>
<Text className="text-center text-base text-gray-400">
Get a US bank account in your name so you can deposit USD straight into soUSD.
</Text>
</View>

<View className="gap-3 rounded-2xl bg-[#1C1C1C] p-4">
{BENEFITS.map(item => (
<View key={item} className="flex-row items-start gap-3">
<View className="mt-1 h-1.5 w-1.5 rounded-full bg-[#94F27F]" />
<Text className="flex-1 text-sm leading-5 text-white">{item}</Text>
</View>
))}
</View>

<Button
className="mt-auto h-14 rounded-2xl sm:mt-8"
style={{ backgroundColor: '#94F27F' }}
onPress={handleApply}
>
<Text className="text-base font-bold text-black">Apply for Virtual Account</Text>
</Button>
</View>
);
};
Loading
Loading