705 lines
21 KiB
TypeScript
705 lines
21 KiB
TypeScript
import { useBalance } from '@/hooks/use-balance';
|
|
import { usePricing } from '@/hooks/use-pricing';
|
|
import Ionicons from '@expo/vector-icons/Ionicons';
|
|
import { useRouter } from 'expo-router';
|
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
import {
|
|
ActivityIndicator,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
TextInput,
|
|
TouchableOpacity,
|
|
View
|
|
} from 'react-native';
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
import { Alert } from '@/utils/alert';
|
|
|
|
type PurchaseTab = 'subscription' | 'pack';
|
|
|
|
type PointsBundle = {
|
|
id: string;
|
|
points: number;
|
|
};
|
|
|
|
const screenPalette = {
|
|
background: '#050505',
|
|
surface: '#101010',
|
|
surfaceRaised: '#1A1B1E',
|
|
primaryText: '#F5F5F5',
|
|
secondaryText: '#7F7F7F',
|
|
mutedText: '#4C4C4C',
|
|
accent: '#FEB840',
|
|
energyHalo: 'rgba(254, 184, 64, 0.22)',
|
|
divider: '#1C1C1C',
|
|
button: '#D1FE17',
|
|
buttonText: '#101010',
|
|
};
|
|
|
|
const SUBSCRIPTION_TAGLINE = 'No active subscription plans';
|
|
|
|
const TABS: { key: PurchaseTab; label: string }[] = [
|
|
{ key: 'subscription', label: 'Subscription' },
|
|
{ key: 'pack', label: 'points pack' },
|
|
];
|
|
|
|
const POINT_BUNDLES: PointsBundle[] = [
|
|
{ id: 'bundle_500', points: 500 },
|
|
{ id: 'bundle_1000', points: 1000 },
|
|
{ id: 'bundle_2000', points: 2000 },
|
|
{ id: 'bundle_5000', points: 5000 },
|
|
];
|
|
|
|
export default function PointsExchangeScreen() {
|
|
const router = useRouter();
|
|
const insets = useSafeAreaInsets();
|
|
const [activeTab, setActiveTab] = useState<PurchaseTab>('subscription');
|
|
const [selectedBundleId, setSelectedBundleId] = useState<string | null>(null);
|
|
const [selectedSubscriptionIndex, setSelectedSubscriptionIndex] = useState<number | null>(0);
|
|
const [customAmount, setCustomAmount] = useState<string>('500');
|
|
|
|
const { balance, isLoading: isBalanceLoading, refresh } = useBalance();
|
|
const {
|
|
stripePricingData,
|
|
isStripePricingLoading,
|
|
stripePricingError,
|
|
creditPlans,
|
|
hasActiveSubscription,
|
|
hasCanceledButActiveSubscription,
|
|
currentSubscriptionCredits,
|
|
activeAuthSubscription,
|
|
handleSubscriptionAction,
|
|
formatCredits,
|
|
createSubscriptionPending,
|
|
upgradeSubscriptionPending,
|
|
restoreSubscriptionPending,
|
|
rechargeToken,
|
|
rechargeTokenPending,
|
|
} = usePricing();
|
|
|
|
const visibleBundles = useMemo(
|
|
() => (activeTab === 'pack' ? POINT_BUNDLES : []),
|
|
[activeTab],
|
|
);
|
|
|
|
const changeTab = useCallback((nextTab: PurchaseTab) => {
|
|
setActiveTab(nextTab);
|
|
if (nextTab !== 'pack') {
|
|
setSelectedBundleId(null);
|
|
}
|
|
}, []);
|
|
|
|
const handleClose = useCallback(() => {
|
|
router.back();
|
|
}, [router]);
|
|
|
|
const openPointsDetails = useCallback(() => {
|
|
router.push('/points');
|
|
}, [router]);
|
|
|
|
const handleBundleSelect = useCallback((bundleId: string) => {
|
|
const bundle = POINT_BUNDLES.find(b => b.id === bundleId);
|
|
if (bundle) {
|
|
setSelectedBundleId(bundleId);
|
|
setCustomAmount(bundle.points.toString());
|
|
}
|
|
}, []);
|
|
|
|
const handleCustomAmountChange = useCallback((value: string) => {
|
|
setCustomAmount(value);
|
|
setSelectedBundleId(null);
|
|
}, []);
|
|
|
|
const handleSubscriptionSelect = useCallback((index: number) => {
|
|
setSelectedSubscriptionIndex(index);
|
|
}, []);
|
|
|
|
const handlePurchase = useCallback(() => {
|
|
if (activeTab === 'subscription') {
|
|
// 订阅购买
|
|
if (selectedSubscriptionIndex !== null) {
|
|
handleSubscriptionAction(selectedSubscriptionIndex);
|
|
} else {
|
|
Alert.alert('Select a plan', 'Please select a subscription plan first.');
|
|
}
|
|
} else {
|
|
// 积分包购买
|
|
const amount = parseInt(customAmount, 10);
|
|
if (isNaN(amount) || amount < 500) {
|
|
Alert.alert('Invalid Amount', 'Please enter a valid amount (minimum 500 points).');
|
|
return;
|
|
}
|
|
rechargeToken(amount);
|
|
}
|
|
}, [
|
|
activeTab,
|
|
selectedSubscriptionIndex,
|
|
customAmount,
|
|
handleSubscriptionAction,
|
|
rechargeToken,
|
|
]);
|
|
|
|
return (
|
|
<View style={[styles.screen, { paddingTop: insets.top + 12 }]}>
|
|
|
|
<View style={styles.headerRow}>
|
|
<TouchableOpacity
|
|
style={styles.iconButton}
|
|
accessibilityRole="button"
|
|
onPress={handleClose}
|
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
>
|
|
<Ionicons name="close" size={22} color={screenPalette.primaryText} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<ScrollView
|
|
style={styles.content}
|
|
contentContainerStyle={[
|
|
styles.contentContainer,
|
|
{ paddingBottom: Math.max(insets.bottom + 96, 160) },
|
|
]}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{isBalanceLoading ? (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color={screenPalette.accent} />
|
|
</View>
|
|
) : (
|
|
<>
|
|
<View style={styles.balanceCluster}>
|
|
<View style={styles.energyOrb}>
|
|
<Ionicons name="flash" size={22} color={screenPalette.accent} />
|
|
</View>
|
|
<Text style={styles.balanceValue}>{balance.remainingTokenBalance}</Text>
|
|
<Text style={styles.balanceSubtitle}>{SUBSCRIPTION_TAGLINE}</Text>
|
|
</View>
|
|
|
|
<View style={styles.tabBar}>
|
|
{TABS.map(tab => {
|
|
const isActive = tab.key === activeTab;
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
key={tab.key}
|
|
style={styles.tabButton}
|
|
onPress={() => changeTab(tab.key)}
|
|
accessibilityRole="tab"
|
|
accessibilityState={{ selected: isActive }}
|
|
activeOpacity={0.8}
|
|
>
|
|
<Text style={[styles.tabLabel, isActive && styles.tabLabelActive]}>
|
|
{tab.label}
|
|
</Text>
|
|
<View
|
|
style={[styles.tabIndicator, isActive && styles.tabIndicatorActive]}
|
|
/>
|
|
</TouchableOpacity>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
<View style={styles.tabDivider} />
|
|
|
|
{activeTab === 'subscription' ? (
|
|
<>
|
|
{/* 订阅套餐列表 */}
|
|
{isStripePricingLoading ? (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color={screenPalette.accent} />
|
|
<Text style={styles.loadingText}>Loading plans...</Text>
|
|
</View>
|
|
) : stripePricingError ? (
|
|
<View style={styles.subscriptionEmpty}>
|
|
<Text style={styles.emptyTitle}>Error</Text>
|
|
<Text style={styles.emptySubtitle}>{stripePricingError}</Text>
|
|
</View>
|
|
) : stripePricingData?.pricing_table_items && stripePricingData.pricing_table_items.length > 0 ? (
|
|
<View style={styles.subscriptionGrid}>
|
|
{stripePricingData.pricing_table_items
|
|
.filter((item: any) => item.recurring?.interval === 'month')
|
|
.map((item: any, index: number) => {
|
|
const plan = creditPlans[index];
|
|
const isSelected = selectedSubscriptionIndex === index;
|
|
const priceInDollars = parseInt(item.amount) / 100;
|
|
const grantToken = item.metadata?.grant_token || '0';
|
|
const isCurrentSubscription =
|
|
hasActiveSubscription &&
|
|
activeAuthSubscription?.plan?.toLowerCase() === item.name?.toLowerCase();
|
|
const isCanceledSubscription =
|
|
hasCanceledButActiveSubscription &&
|
|
activeAuthSubscription?.plan?.toLowerCase() === item.name?.toLowerCase();
|
|
const isHighlight = item.is_highlight || item.highlight_text === 'most_popular';
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
key={item.price_id}
|
|
style={[
|
|
styles.subscriptionCard,
|
|
isSelected && styles.subscriptionCardSelected,
|
|
isHighlight && styles.subscriptionCardHighlight,
|
|
]}
|
|
onPress={() => handleSubscriptionSelect(index)}
|
|
accessibilityRole="button"
|
|
accessibilityState={{ selected: isSelected }}
|
|
activeOpacity={0.88}
|
|
>
|
|
{/* 套餐头部 */}
|
|
<View style={styles.subscriptionHeader}>
|
|
<Text style={styles.subscriptionName}>{item.name}</Text>
|
|
{isHighlight && (
|
|
<View style={styles.popularBadge}>
|
|
<Text style={styles.popularBadgeText}>Popular</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* 价格 */}
|
|
<View style={styles.subscriptionPriceContainer}>
|
|
<Text style={styles.subscriptionPrice}>${priceInDollars}</Text>
|
|
<Text style={styles.subscriptionInterval}>/mo</Text>
|
|
</View>
|
|
|
|
{/* 积分信息 */}
|
|
<View style={styles.subscriptionCreditsBox}>
|
|
<View style={styles.creditsRow}>
|
|
<Ionicons name="flash" size={20} color={screenPalette.accent} />
|
|
<Text style={styles.subscriptionCreditsValue}>
|
|
{formatCredits(Number(grantToken))}
|
|
</Text>
|
|
</View>
|
|
<Text style={styles.creditsLabel}>credits per month</Text>
|
|
</View>
|
|
|
|
{/* 状态徽章 */}
|
|
{isCurrentSubscription && (
|
|
<View style={styles.currentBadge}>
|
|
<Ionicons name="checkmark-circle" size={14} color="#22c55e" />
|
|
<Text style={styles.currentBadgeText}>Current Plan</Text>
|
|
</View>
|
|
)}
|
|
|
|
{isCanceledSubscription && (
|
|
<View style={styles.canceledBadge}>
|
|
<Ionicons name="warning" size={14} color="#fb923c" />
|
|
<Text style={styles.canceledBadgeText}>Canceled</Text>
|
|
</View>
|
|
)}
|
|
</TouchableOpacity>
|
|
);
|
|
})}
|
|
</View>
|
|
) : (
|
|
<View style={styles.subscriptionEmpty}>
|
|
<Text style={styles.emptyTitle}>No Plans Available</Text>
|
|
<Text style={styles.emptySubtitle}>
|
|
No subscription tiers are available at the moment.
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</>
|
|
) : (
|
|
<>
|
|
{/* 预设套餐 */}
|
|
<View style={styles.packGrid}>
|
|
{visibleBundles.map(bundle => {
|
|
const isSelected = bundle.id === selectedBundleId;
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
key={bundle.id}
|
|
style={[
|
|
styles.packCard,
|
|
isSelected && styles.packCardSelected,
|
|
]}
|
|
onPress={() => handleBundleSelect(bundle.id)}
|
|
accessibilityRole="button"
|
|
accessibilityState={{ selected: isSelected }}
|
|
activeOpacity={0.88}
|
|
>
|
|
<View style={styles.packHeader}>
|
|
<Ionicons name="flash" size={18} color={screenPalette.accent} />
|
|
<Text style={styles.packPoints}>{bundle.points}</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
{/* 自定义金额输入 */}
|
|
<View style={[
|
|
styles.customAmountContainer,
|
|
!selectedBundleId && styles.customAmountContainerActive,
|
|
]}>
|
|
<TextInput
|
|
style={styles.customAmountInput}
|
|
value={customAmount}
|
|
onChangeText={handleCustomAmountChange}
|
|
keyboardType="numeric"
|
|
placeholder="Min. 500 points"
|
|
placeholderTextColor={screenPalette.mutedText}
|
|
/>
|
|
</View>
|
|
</>
|
|
)}
|
|
</>
|
|
)}
|
|
</ScrollView>
|
|
|
|
<View style={[styles.bottomBar, { paddingBottom: Math.max(insets.bottom, 16) }]}>
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.primaryButton,
|
|
(createSubscriptionPending || upgradeSubscriptionPending || restoreSubscriptionPending || rechargeTokenPending) &&
|
|
styles.primaryButtonDisabled,
|
|
]}
|
|
onPress={handlePurchase}
|
|
disabled={
|
|
createSubscriptionPending ||
|
|
upgradeSubscriptionPending ||
|
|
restoreSubscriptionPending ||
|
|
rechargeTokenPending
|
|
}
|
|
activeOpacity={0.85}
|
|
>
|
|
{createSubscriptionPending ||
|
|
upgradeSubscriptionPending ||
|
|
restoreSubscriptionPending ||
|
|
rechargeTokenPending ? (
|
|
<ActivityIndicator color={screenPalette.buttonText} />
|
|
) : (
|
|
<Text style={styles.primaryButtonLabel}>
|
|
{activeTab === 'subscription' ? 'Subscribe' : 'Purchase points'}
|
|
</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
screen: {
|
|
flex: 1,
|
|
backgroundColor: screenPalette.background,
|
|
},
|
|
headerRow: {
|
|
paddingHorizontal: 24,
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
iconButton: {
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 18,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
secondaryPill: {
|
|
paddingHorizontal: 16,
|
|
height: 34,
|
|
borderRadius: 17,
|
|
backgroundColor: screenPalette.surface,
|
|
borderWidth: StyleSheet.hairlineWidth,
|
|
borderColor: screenPalette.divider,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
secondaryPillLabel: {
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
color: screenPalette.primaryText,
|
|
letterSpacing: 0.2,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
contentContainer: {
|
|
paddingHorizontal: 24,
|
|
},
|
|
balanceCluster: {
|
|
alignItems: 'center',
|
|
marginTop: 32,
|
|
marginBottom: 36,
|
|
gap: 10,
|
|
},
|
|
energyOrb: {
|
|
width: 58,
|
|
height: 58,
|
|
borderRadius: 29,
|
|
backgroundColor: screenPalette.energyHalo,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
shadowColor: screenPalette.accent,
|
|
shadowOpacity: 0.28,
|
|
shadowRadius: 14,
|
|
shadowOffset: { width: 0, height: 8 },
|
|
elevation: 6,
|
|
},
|
|
balanceValue: {
|
|
fontSize: 44,
|
|
fontWeight: '700',
|
|
color: screenPalette.primaryText,
|
|
letterSpacing: 0.5,
|
|
fontVariant: ['tabular-nums'],
|
|
},
|
|
balanceSubtitle: {
|
|
fontSize: 13,
|
|
color: screenPalette.secondaryText,
|
|
letterSpacing: 0.2,
|
|
},
|
|
tabBar: {
|
|
flexDirection: 'row',
|
|
gap: 32,
|
|
paddingHorizontal: 4,
|
|
},
|
|
tabButton: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
paddingBottom: 12,
|
|
},
|
|
tabLabel: {
|
|
fontSize: 14,
|
|
color: screenPalette.mutedText,
|
|
fontWeight: '500',
|
|
letterSpacing: 0.2,
|
|
textTransform: 'capitalize',
|
|
},
|
|
tabLabelActive: {
|
|
color: screenPalette.primaryText,
|
|
},
|
|
tabIndicator: {
|
|
marginTop: 10,
|
|
height: 2,
|
|
width: '100%',
|
|
backgroundColor: 'transparent',
|
|
borderRadius: 1,
|
|
},
|
|
tabIndicatorActive: {
|
|
backgroundColor: screenPalette.accent,
|
|
},
|
|
tabDivider: {
|
|
marginTop: 12,
|
|
height: StyleSheet.hairlineWidth,
|
|
backgroundColor: screenPalette.divider,
|
|
},
|
|
packGrid: {
|
|
marginTop: 28,
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 18,
|
|
},
|
|
packCard: {
|
|
width: '47%',
|
|
backgroundColor: screenPalette.surfaceRaised,
|
|
borderRadius: 24,
|
|
paddingHorizontal: 18,
|
|
paddingVertical: 24,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderWidth: 2,
|
|
borderColor: 'transparent',
|
|
},
|
|
packCardSelected: {
|
|
borderColor: screenPalette.accent,
|
|
backgroundColor: 'rgba(254, 184, 64, 0.05)',
|
|
},
|
|
packHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
},
|
|
packPoints: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: screenPalette.primaryText,
|
|
fontVariant: ['tabular-nums'],
|
|
},
|
|
subscriptionEmpty: {
|
|
marginTop: 64,
|
|
backgroundColor: screenPalette.surface,
|
|
borderRadius: 22,
|
|
paddingVertical: 40,
|
|
paddingHorizontal: 32,
|
|
alignItems: 'center',
|
|
gap: 12,
|
|
},
|
|
emptyTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: screenPalette.primaryText,
|
|
},
|
|
emptySubtitle: {
|
|
fontSize: 13,
|
|
color: screenPalette.secondaryText,
|
|
textAlign: 'center',
|
|
lineHeight: 20,
|
|
},
|
|
loadingContainer: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
minHeight: 300,
|
|
},
|
|
loadingText: {
|
|
marginTop: 12,
|
|
fontSize: 14,
|
|
color: screenPalette.secondaryText,
|
|
},
|
|
subscriptionGrid: {
|
|
marginTop: 28,
|
|
gap: 16,
|
|
},
|
|
subscriptionCard: {
|
|
backgroundColor: screenPalette.surfaceRaised,
|
|
borderRadius: 20,
|
|
padding: 24,
|
|
borderWidth: 2,
|
|
borderColor: 'transparent',
|
|
position: 'relative',
|
|
},
|
|
subscriptionCardSelected: {
|
|
borderColor: screenPalette.accent,
|
|
backgroundColor: 'rgba(254, 184, 64, 0.05)',
|
|
},
|
|
subscriptionCardHighlight: {
|
|
borderColor: screenPalette.button,
|
|
},
|
|
subscriptionHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 16,
|
|
},
|
|
subscriptionName: {
|
|
fontSize: 24,
|
|
fontWeight: '700',
|
|
color: screenPalette.primaryText,
|
|
},
|
|
subscriptionPriceContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'baseline',
|
|
marginBottom: 20,
|
|
},
|
|
subscriptionPrice: {
|
|
fontSize: 40,
|
|
fontWeight: '700',
|
|
color: screenPalette.accent,
|
|
fontVariant: ['tabular-nums'],
|
|
},
|
|
subscriptionInterval: {
|
|
fontSize: 16,
|
|
color: screenPalette.secondaryText,
|
|
marginLeft: 4,
|
|
},
|
|
subscriptionCreditsBox: {
|
|
backgroundColor: 'rgba(254, 184, 64, 0.1)',
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
alignItems: 'center',
|
|
},
|
|
creditsRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
marginBottom: 4,
|
|
},
|
|
subscriptionCreditsValue: {
|
|
fontSize: 28,
|
|
fontWeight: '700',
|
|
color: screenPalette.accent,
|
|
fontVariant: ['tabular-nums'],
|
|
},
|
|
creditsLabel: {
|
|
fontSize: 13,
|
|
color: screenPalette.secondaryText,
|
|
fontWeight: '500',
|
|
},
|
|
popularBadge: {
|
|
backgroundColor: screenPalette.button,
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 6,
|
|
borderRadius: 12,
|
|
},
|
|
popularBadgeText: {
|
|
fontSize: 12,
|
|
fontWeight: '700',
|
|
color: screenPalette.buttonText,
|
|
},
|
|
currentBadge: {
|
|
marginTop: 16,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
backgroundColor: 'rgba(34, 197, 94, 0.15)',
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 8,
|
|
borderRadius: 10,
|
|
alignSelf: 'flex-start',
|
|
},
|
|
currentBadgeText: {
|
|
fontSize: 13,
|
|
fontWeight: '600',
|
|
color: '#22c55e',
|
|
},
|
|
canceledBadge: {
|
|
marginTop: 16,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
backgroundColor: 'rgba(251, 146, 60, 0.15)',
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 8,
|
|
borderRadius: 10,
|
|
alignSelf: 'flex-start',
|
|
},
|
|
canceledBadgeText: {
|
|
fontSize: 13,
|
|
fontWeight: '600',
|
|
color: '#fb923c',
|
|
},
|
|
customAmountContainer: {
|
|
marginTop: 24,
|
|
backgroundColor: screenPalette.surfaceRaised,
|
|
borderRadius: 22,
|
|
paddingVertical: 20,
|
|
paddingHorizontal: 24,
|
|
borderWidth: 2,
|
|
borderColor: 'transparent',
|
|
},
|
|
customAmountContainerActive: {
|
|
borderColor: screenPalette.accent,
|
|
backgroundColor: 'rgba(254, 184, 64, 0.05)',
|
|
},
|
|
customAmountInput: {
|
|
backgroundColor: screenPalette.surface,
|
|
borderRadius: 12,
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 14,
|
|
fontSize: 16,
|
|
color: screenPalette.primaryText,
|
|
borderWidth: 1,
|
|
borderColor: screenPalette.divider,
|
|
},
|
|
bottomBar: {
|
|
paddingHorizontal: 24,
|
|
},
|
|
primaryButton: {
|
|
height: 54,
|
|
borderRadius: 27,
|
|
backgroundColor: screenPalette.button,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
primaryButtonDisabled: {
|
|
opacity: 0.5,
|
|
},
|
|
primaryButtonLabel: {
|
|
fontSize: 16,
|
|
fontWeight: '700',
|
|
color: screenPalette.buttonText,
|
|
letterSpacing: 0.3,
|
|
},
|
|
});
|