refactor(native): central Button component + sweep across devices/plan flows
Replaces ad-hoc TouchableOpacity+styled-Text pairs with a single `<Button>` covering the four variants we actually use (primary, secondary, ghost, destructive), with size (sm/md/lg), loading, disabled, icon, iconPosition, and a style escape hatch. Migrated files: AddMacSheet, AddWindowsSheet, PlanChangeSheet, devices.tsx CTA, settings SubscriptionSheet CTA. Skipped (kept as-is to avoid hostile overrides): auth flow buttons (Google/Apple OAuth with custom SVGs), list-row Touchables, blocker & mail components (separate sweep when those screens come up). paddingVertical default 12 (md) — matches the slimmer-buttons direction we landed on in the devices-page redesign.
This commit is contained in:
parent
e8ea00568e
commit
e4ac3ae51c
@ -7,6 +7,7 @@ import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Button } from '../components/Button';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
@ -540,24 +541,12 @@ export default function DevicesScreen() {
|
||||
{/* CTA or Upgrade */}
|
||||
{isLegend ? (
|
||||
atDeviceLimit ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
style={{
|
||||
backgroundColor: colors.surfaceElevated,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
opacity: 0.5,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="add-circle-outline" size={20} color={colors.textMuted} />
|
||||
<Text style={{ fontSize: 16, color: colors.textMuted, fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.add_device')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Button
|
||||
title={t('devices.add_device')}
|
||||
icon="add-circle-outline"
|
||||
disabled
|
||||
style={{ backgroundColor: colors.surfaceElevated }}
|
||||
/>
|
||||
) : (
|
||||
<MenuView
|
||||
title={t('devices.add_device')}
|
||||
@ -571,23 +560,10 @@ export default function DevicesScreen() {
|
||||
}}
|
||||
shouldOpenOnLongPress={false}
|
||||
>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="add-circle-outline" size={20} color="#fff" />
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.add_device')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Button
|
||||
title={t('devices.add_device')}
|
||||
icon="add-circle-outline"
|
||||
/>
|
||||
</MenuView>
|
||||
)
|
||||
) : (
|
||||
@ -609,19 +585,7 @@ export default function DevicesScreen() {
|
||||
{t('devices.subtitle_legend')}
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 12,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 15, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.upgrade_cta')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Button title={t('devices.upgrade_cta')} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import { TrueSheet } from '@lodev09/react-native-true-sheet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { LanguageIcon } from '../components/icons/LanguageIcon';
|
||||
import { useColors } from '../lib/theme';
|
||||
import { Button } from '../components/Button';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { useAppLockStore } from '../stores/appLock';
|
||||
import { useThemeStore, type ThemeMode } from '../stores/theme';
|
||||
@ -105,32 +106,16 @@ function SubscriptionSheet({ plan, colors, t }: SubscriptionSheetProps) {
|
||||
{t('settings.subscription_sheet_body')}
|
||||
</Text>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
// TODO: für iOS-Submission ggf. zu nicht-tippbarem Text degradieren
|
||||
// (Apple Guideline 3.1.1: externe Abo-Links können Review-Ablehnung triggern,
|
||||
// wenn sie als Kauf-Umgehung gewertet werden. Standalone-URL ohne Preis-Info
|
||||
// sollte ok sein, ist aber ungeprüft — bei Submission erneut prüfen.)
|
||||
Linking.openURL('https://rebreak.org/account');
|
||||
}}
|
||||
activeOpacity={0.8}
|
||||
style={{
|
||||
backgroundColor: accentColor,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 14,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: '#ffffff',
|
||||
fontFamily: 'Nunito_700Bold',
|
||||
}}
|
||||
>
|
||||
{t('settings.subscription_sheet_cta')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{/* TODO: für iOS-Submission ggf. zu nicht-tippbarem Text degradieren
|
||||
(Apple Guideline 3.1.1: externe Abo-Links können Review-Ablehnung triggern,
|
||||
wenn sie als Kauf-Umgehung gewertet werden. Standalone-URL ohne Preis-Info
|
||||
sollte ok sein, ist aber ungeprüft — bei Submission erneut prüfen.) */}
|
||||
<Button
|
||||
title={t('settings.subscription_sheet_cta')}
|
||||
onPress={() => Linking.openURL('https://rebreak.org/account')}
|
||||
size="lg"
|
||||
style={{ backgroundColor: accentColor }}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,57 +1,110 @@
|
||||
import { ActivityIndicator, Pressable, Text } from 'react-native';
|
||||
import type { PressableProps } from 'react-native';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
type ViewStyle,
|
||||
} from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useColors } from '../lib/theme';
|
||||
|
||||
type Variant = 'primary' | 'secondary' | 'ghost' | 'danger';
|
||||
type Variant = 'primary' | 'secondary' | 'ghost' | 'destructive';
|
||||
type Size = 'sm' | 'md' | 'lg';
|
||||
|
||||
type Props = PressableProps & {
|
||||
children: React.ReactNode;
|
||||
type Props = {
|
||||
title: string;
|
||||
onPress?: () => void;
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
icon?: React.ComponentProps<typeof Ionicons>['name'];
|
||||
iconPosition?: 'left' | 'right';
|
||||
style?: ViewStyle;
|
||||
};
|
||||
|
||||
const variantStyles: Record<Variant, { container: string; text: string }> = {
|
||||
primary: {
|
||||
container: 'bg-rebreak-500 rounded-xl px-5 py-3.5 items-center justify-center',
|
||||
text: 'text-white text-base',
|
||||
},
|
||||
secondary: {
|
||||
container: 'bg-neutral-100 border border-neutral-200 rounded-xl px-5 py-3.5 items-center justify-center',
|
||||
text: 'text-neutral-800 text-base',
|
||||
},
|
||||
ghost: {
|
||||
container: 'rounded-xl px-5 py-3.5 items-center justify-center',
|
||||
text: 'text-neutral-600 text-base',
|
||||
},
|
||||
danger: {
|
||||
container: 'bg-red-50 border border-red-200 rounded-xl px-5 py-3.5 items-center justify-center',
|
||||
text: 'text-red-600 text-base',
|
||||
},
|
||||
};
|
||||
const PADDING: Record<Size, number> = { sm: 8, md: 12, lg: 16 };
|
||||
const FONT_SIZE: Record<Size, number> = { sm: 14, md: 15, lg: 16 };
|
||||
|
||||
export function Button({
|
||||
children,
|
||||
title,
|
||||
onPress,
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
loading = false,
|
||||
disabled = false,
|
||||
className = '',
|
||||
...rest
|
||||
icon,
|
||||
iconPosition = 'left',
|
||||
style,
|
||||
}: Props) {
|
||||
const styles = variantStyles[variant];
|
||||
const colors = useColors();
|
||||
const isDisabled = disabled || loading;
|
||||
const paddingVertical = PADDING[size];
|
||||
const fontSize = FONT_SIZE[size];
|
||||
|
||||
const containerBase: ViewStyle = {
|
||||
borderRadius: 14,
|
||||
paddingVertical,
|
||||
paddingHorizontal: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
gap: 8,
|
||||
opacity: isDisabled ? 0.5 : 1,
|
||||
};
|
||||
|
||||
let containerVariant: ViewStyle;
|
||||
let textColor: string;
|
||||
let indicatorColor: string;
|
||||
|
||||
switch (variant) {
|
||||
case 'secondary':
|
||||
containerVariant = {
|
||||
borderWidth: 1.5,
|
||||
borderColor: colors.brandOrange,
|
||||
};
|
||||
textColor = colors.brandOrange;
|
||||
indicatorColor = colors.brandOrange;
|
||||
break;
|
||||
case 'ghost':
|
||||
containerVariant = {};
|
||||
textColor = colors.brandOrange;
|
||||
indicatorColor = colors.brandOrange;
|
||||
break;
|
||||
case 'destructive':
|
||||
containerVariant = { backgroundColor: colors.error };
|
||||
textColor = '#ffffff';
|
||||
indicatorColor = '#ffffff';
|
||||
break;
|
||||
default:
|
||||
containerVariant = { backgroundColor: colors.brandOrange };
|
||||
textColor = '#ffffff';
|
||||
indicatorColor = '#ffffff';
|
||||
break;
|
||||
}
|
||||
|
||||
const iconEl = icon ? (
|
||||
<Ionicons name={icon} size={fontSize + 2} color={loading ? 'transparent' : textColor} />
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
className={`${styles.container} ${isDisabled ? 'opacity-50' : ''} ${className}`}
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
disabled={isDisabled}
|
||||
{...rest}
|
||||
activeOpacity={0.7}
|
||||
style={[containerBase, containerVariant, style]}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator color={variant === 'primary' ? '#fff' : '#f59e0b'} size="small" />
|
||||
<ActivityIndicator color={indicatorColor} size="small" />
|
||||
) : (
|
||||
<Text className={styles.text} style={{ fontFamily: 'Nunito_600SemiBold' }}>{children}</Text>
|
||||
<>
|
||||
{iconEl && iconPosition === 'left' ? iconEl : null}
|
||||
<Text style={{ fontSize, color: textColor, fontFamily: 'Nunito_600SemiBold' }}>
|
||||
{title}
|
||||
</Text>
|
||||
{iconEl && iconPosition === 'right' ? iconEl : null}
|
||||
</>
|
||||
)}
|
||||
</Pressable>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
Alert,
|
||||
Linking,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
@ -15,6 +14,7 @@ import * as Haptics from 'expo-haptics';
|
||||
import { useColors } from '../../lib/theme';
|
||||
import { FormSheet } from '../FormSheet';
|
||||
import { RiveAvatar } from '../RiveAvatar';
|
||||
import { Button } from '../Button';
|
||||
import { useProtectedDevicesStore } from '../../stores/protectedDevices';
|
||||
import { useProtectedDevicesRealtime } from '../../hooks/useProtectedDevicesRealtime';
|
||||
import { useRouter } from 'expo-router';
|
||||
@ -197,26 +197,12 @@ function Step1LabelContent({
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.prepare_profile')}
|
||||
onPress={onPrepare}
|
||||
loading={enrolling}
|
||||
disabled={enrolling}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
opacity: enrolling ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{enrolling ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.prepare_profile')}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@ -308,24 +294,11 @@ function Step2OnboardingContent({
|
||||
</View>
|
||||
|
||||
{/* Download button */}
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.download_button')}
|
||||
onPress={onDownload}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="download-outline" size={18} color="#fff" />
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.download_button')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
icon="download-outline"
|
||||
/>
|
||||
|
||||
{/* Pending auto-detect pill */}
|
||||
<View
|
||||
@ -359,22 +332,13 @@ function Step2OnboardingContent({
|
||||
</View>
|
||||
|
||||
{/* Need help */}
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.need_help')}
|
||||
onPress={onNeedHelp}
|
||||
activeOpacity={0.5}
|
||||
style={{ alignItems: 'center' }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: colors.textMuted,
|
||||
fontFamily: 'Nunito_400Regular',
|
||||
textDecorationLine: 'underline',
|
||||
}}
|
||||
>
|
||||
{t('devices.need_help')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
style={{ alignSelf: 'center' }}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
@ -424,22 +388,11 @@ function Step3SuccessContent({
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('common.ok')}
|
||||
onPress={onClose}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 40,
|
||||
alignItems: 'center',
|
||||
alignSelf: 'stretch',
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('common.ok')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
style={{ alignSelf: 'stretch' }}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
Linking,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
@ -15,6 +13,7 @@ import * as Haptics from 'expo-haptics';
|
||||
import { useColors } from '../../lib/theme';
|
||||
import { FormSheet } from '../FormSheet';
|
||||
import { RiveAvatar } from '../RiveAvatar';
|
||||
import { Button } from '../Button';
|
||||
import { useProtectedDevicesStore } from '../../stores/protectedDevices';
|
||||
import { useRouter } from 'expo-router';
|
||||
|
||||
@ -205,26 +204,12 @@ function WindowsStep1LabelContent({
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.prepare_profile')}
|
||||
onPress={onPrepare}
|
||||
loading={enrolling}
|
||||
disabled={enrolling}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
opacity: enrolling ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{enrolling ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.prepare_profile')}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@ -320,65 +305,29 @@ function WindowsStep2OnboardingContent({
|
||||
</View>
|
||||
|
||||
{/* Download button */}
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.windows_download_button')}
|
||||
onPress={onDownload}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="download-outline" size={18} color="#fff" />
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('devices.windows_download_button')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
icon="download-outline"
|
||||
/>
|
||||
|
||||
{/* Confirm installed */}
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.confirm_installed')}
|
||||
onPress={onConfirmInstalled}
|
||||
variant="secondary"
|
||||
loading={confirming}
|
||||
disabled={confirming}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
borderWidth: 1.5,
|
||||
borderColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
opacity: confirming ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{confirming ? (
|
||||
<ActivityIndicator color={colors.brandOrange} />
|
||||
) : (
|
||||
<Text style={{ fontSize: 15, color: colors.brandOrange, fontFamily: 'Nunito_600SemiBold' }}>
|
||||
{t('devices.confirm_installed')}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
/>
|
||||
|
||||
{/* Need help */}
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('devices.need_help')}
|
||||
onPress={onNeedHelp}
|
||||
activeOpacity={0.5}
|
||||
style={{ alignItems: 'center' }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: colors.textMuted,
|
||||
fontFamily: 'Nunito_400Regular',
|
||||
textDecorationLine: 'underline',
|
||||
}}
|
||||
>
|
||||
{t('devices.need_help')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
style={{ alignSelf: 'center' }}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
@ -428,22 +377,11 @@ function WindowsStep3SuccessContent({
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
<Button
|
||||
title={t('common.ok')}
|
||||
onPress={onClose}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: colors.brandOrange,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 40,
|
||||
alignItems: 'center',
|
||||
alignSelf: 'stretch',
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 16, color: '#fff', fontFamily: 'Nunito_700Bold' }}>
|
||||
{t('common.ok')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
style={{ alignSelf: 'stretch' }}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import {
|
||||
Modal,
|
||||
ScrollView,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
@ -13,6 +12,7 @@ import { Ionicons } from '@expo/vector-icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { apiFetch } from '../../lib/api';
|
||||
import { useColors } from '../../lib/theme';
|
||||
import { Button } from '../Button';
|
||||
import type { Plan } from '../../hooks/useMe';
|
||||
|
||||
export type ChangePreviewItem = {
|
||||
@ -138,15 +138,11 @@ export function PlanChangeSheet({ visible, targetPlan, onConfirm, onClose }: Pro
|
||||
<Text style={{ fontSize: 14, color: colors.error, fontFamily: 'Nunito_400Regular', textAlign: 'center' }}>
|
||||
{error}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
<Button
|
||||
title={t('common.back')}
|
||||
onPress={onClose}
|
||||
style={{ paddingVertical: 12, paddingHorizontal: 24, backgroundColor: colors.surfaceElevated, borderRadius: 12 }}
|
||||
>
|
||||
<Text style={{ fontSize: 14, color: colors.text, fontFamily: 'Nunito_600SemiBold' }}>
|
||||
{t('common.back')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
variant="ghost"
|
||||
/>
|
||||
</View>
|
||||
) : preview ? (
|
||||
<SheetContent
|
||||
@ -337,43 +333,26 @@ function SheetContent({ preview, targetPlan, isDowngrade, colors, onConfirm, onC
|
||||
|
||||
{/* CTAs */}
|
||||
<View style={{ gap: 10, marginTop: 4 }}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
<Button
|
||||
title={isDowngrade ? t('plan.change.cta_confirm_downgrade') : t('plan.change.cta_confirm_upgrade')}
|
||||
onPress={onConfirm}
|
||||
style={{
|
||||
backgroundColor: '#007AFF',
|
||||
borderRadius: 14,
|
||||
paddingVertical: 16,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 16, fontFamily: 'Nunito_700Bold', color: '#ffffff' }}>
|
||||
{isDowngrade ? t('plan.change.cta_confirm_downgrade') : t('plan.change.cta_confirm_upgrade')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
size="lg"
|
||||
/>
|
||||
|
||||
{isDowngrade && (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
<Button
|
||||
title={t('plan.change.cta_stay', { plan: PLAN_LABEL[preview.from] })}
|
||||
onPress={onClose}
|
||||
style={{ paddingVertical: 12, alignItems: 'center' }}
|
||||
>
|
||||
<Text style={{ fontSize: 14, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
|
||||
{t('plan.change.cta_stay', { plan: PLAN_LABEL[preview.from] })}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
variant="ghost"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isDowngrade && (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
<Button
|
||||
title={t('common.cancel')}
|
||||
onPress={onClose}
|
||||
style={{ paddingVertical: 12, alignItems: 'center' }}
|
||||
>
|
||||
<Text style={{ fontSize: 14, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
|
||||
{t('common.cancel')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
variant="ghost"
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user