theme update latest project
This commit is contained in:
@@ -1,396 +1,254 @@
|
||||
import { CommonActions, RouteProp, useNavigation, useRoute } from "@react-navigation/native";
|
||||
import axios from "axios";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
TextInput,
|
||||
Pressable,
|
||||
Animated,
|
||||
Platform,
|
||||
NativeSyntheticEvent,
|
||||
TextInputKeyPressEventData,
|
||||
KeyboardAvoidingView,
|
||||
StatusBar,
|
||||
Dimensions,
|
||||
} from "react-native";
|
||||
import { RootStackParamList } from "../navigation/AppNavigator";
|
||||
import * as Keychain from "react-native-keychain";
|
||||
import { LOGIN_URL, VARIFY_OTP_URL } from "../utils/ApiUrls";
|
||||
import colors from "../components/colors";
|
||||
import CustomAlert from "../components/CostomAlart";
|
||||
import { getFcmToken } from "../utils/firebase/firebaseServices";
|
||||
import { useTheme } from "../theme/ThemeProvider";
|
||||
import FontsFamily from "../constents/FontsFamily";
|
||||
ToastAndroid,
|
||||
TouchableOpacity,
|
||||
} from 'react-native';
|
||||
import { useNavigation, useRoute, RouteProp, CommonActions } from '@react-navigation/native';
|
||||
import { responsiveFontSize } from 'react-native-responsive-dimensions';
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
import { RootStackParamList } from '../navigation/AppNavigator';
|
||||
import { useTheme } from '../hooks/ui/useTheme';
|
||||
import { useCreate } from '../hooks/core/useCreate';
|
||||
import { LOGIN_URL, VARIFY_OTP_URL } from '../utils/ApiUrls';
|
||||
import { getFcmToken } from '../utils/firebase/firebaseServices';
|
||||
import { CustomButton } from '../components/common/Custom';
|
||||
import AppView from '../components/AppView';
|
||||
import FontsFamily from '../constents/FontsFamily';
|
||||
|
||||
const CELL_COUNT = 6;
|
||||
const { width } = Dimensions.get('window');
|
||||
const BLOCK_SIZE = width / 3;
|
||||
type OtpVerifyRouteProp = RouteProp<RootStackParamList, "OtpVarify">;
|
||||
|
||||
interface AlertButton {
|
||||
text: string;
|
||||
onPress: () => void;
|
||||
style?: "cancel" | "destructive" | "default";
|
||||
}
|
||||
type OtpVerifyRouteProp = RouteProp<RootStackParamList, 'OtpVarify'>;
|
||||
|
||||
export default function OtpVerificationScreen() {
|
||||
const [code, setCode] = useState<string[]>(Array(CELL_COUNT).fill(""));
|
||||
const [timer, setTimer] = useState(30);
|
||||
const [isResending, setIsResending] = useState(false);
|
||||
const [alertConfig, setAlertConfig] = useState<{
|
||||
visible: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
buttons: AlertButton[];
|
||||
alertType: 'error' | 'success' | 'info';
|
||||
}>({
|
||||
visible: false,
|
||||
title: "",
|
||||
message: "",
|
||||
buttons: [],
|
||||
alertType: "info",
|
||||
});
|
||||
const { theme } = useTheme();
|
||||
|
||||
const inputs = useRef<Array<TextInput | null>>([]);
|
||||
const theme = useTheme();
|
||||
const navigation = useNavigation<any>();
|
||||
const route = useRoute<OtpVerifyRouteProp>();
|
||||
const { mobile } = route.params;
|
||||
const navigation = useNavigation<any>();
|
||||
|
||||
// Refs for auto-closing logic
|
||||
const successActionRef = useRef<(() => void) | null>(null);
|
||||
const successTimerRef = useRef<number | null>(null);
|
||||
const [code, setCode] = useState<string[]>(Array(CELL_COUNT).fill(''));
|
||||
const [timer, setTimer] = useState(30);
|
||||
const inputs = useRef<Array<TextInput | null>>([]);
|
||||
|
||||
const fade = useRef(new Animated.Value(0)).current;
|
||||
const slide = useRef(new Animated.Value(30)).current;
|
||||
const ctaScale = useRef(new Animated.Value(1)).current;
|
||||
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||
const slideAnim = useRef(new Animated.Value(20)).current;
|
||||
|
||||
useEffect(() => {
|
||||
Animated.parallel([
|
||||
Animated.timing(fade, { toValue: 1, duration: 800, useNativeDriver: true }),
|
||||
Animated.spring(slide, { toValue: 0, friction: 5, tension: 40, useNativeDriver: true }),
|
||||
Animated.timing(fadeAnim, {
|
||||
toValue: 1,
|
||||
duration: 800,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.spring(slideAnim, {
|
||||
toValue: 0,
|
||||
tension: 20,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
]).start();
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setTimer((prev) => (prev > 0 ? prev - 1 : 0));
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (timer <= 0) return;
|
||||
const interval = setInterval(() => setTimer((prev) => prev - 1), 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [timer]);
|
||||
const { mutate: verifyOtp, isPending: verifying } = useCreate<any, any>(VARIFY_OTP_URL, {
|
||||
onSuccess: async (res) => {
|
||||
if (res?.statusCode === 200) {
|
||||
const { jwt, userId } = res?.data || {};
|
||||
await Keychain.setGenericPassword('auth', jwt, {
|
||||
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
|
||||
});
|
||||
await getFcmToken(userId);
|
||||
|
||||
const hideAlert = () => {
|
||||
if (successTimerRef.current) {
|
||||
clearTimeout(successTimerRef.current);
|
||||
successTimerRef.current = null;
|
||||
}
|
||||
setAlertConfig(prev => ({ ...prev, visible: false }));
|
||||
};
|
||||
|
||||
const triggerAlert = (
|
||||
title: string,
|
||||
message: string,
|
||||
buttons: AlertButton[],
|
||||
alertType: 'error' | 'success' | 'info' = 'info',
|
||||
autoCloseAction?: () => void
|
||||
) => {
|
||||
setAlertConfig({ visible: true, title, message, buttons, alertType });
|
||||
|
||||
if (alertType === 'success' && autoCloseAction) {
|
||||
successActionRef.current = autoCloseAction;
|
||||
successTimerRef.current = setTimeout(() => {
|
||||
successActionRef.current?.();
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
|
||||
const saveToken = async (token: string) => {
|
||||
try {
|
||||
await Keychain.setGenericPassword("auth", token, { accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED });
|
||||
console.log("✅ Token saved securely");
|
||||
} catch (error) {
|
||||
console.error("❌ Error saving token:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async (otp: string, mobile: string) => {
|
||||
if (otp.length !== CELL_COUNT) {
|
||||
triggerAlert(
|
||||
'Incomplete Code',
|
||||
'Please enter all 6 digits of the OTP.',
|
||||
[{ text: 'OK', onPress: hideAlert }],
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await axios.post(VARIFY_OTP_URL, { mobile, otp }, {
|
||||
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
||||
});
|
||||
|
||||
if (res.data?.statusCode === 200) {
|
||||
const { jwt } = res.data?.data || {};
|
||||
console.log("✅ OTP Verified:", res.data);
|
||||
await saveToken(jwt);
|
||||
await getFcmToken(res.data?.data?.userId);
|
||||
|
||||
let hasActionRun = false;
|
||||
const successAction = () => {
|
||||
if (hasActionRun) return;
|
||||
hasActionRun = true;
|
||||
hideAlert();
|
||||
navigation.dispatch(
|
||||
CommonActions.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Home' }],
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
triggerAlert(
|
||||
'Success!',
|
||||
'OTP verified successfully. Redirecting...',
|
||||
[{ text: 'OK', onPress: successAction }],
|
||||
'success',
|
||||
successAction
|
||||
ToastAndroid.show('Verified successfully', ToastAndroid.SHORT);
|
||||
navigation.dispatch(
|
||||
CommonActions.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Home' }],
|
||||
})
|
||||
);
|
||||
} else {
|
||||
ToastAndroid.show(res?.message || 'Invalid OTP', ToastAndroid.SHORT);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("❌ OTP Verification Error:", err?.response?.data || err.message);
|
||||
triggerAlert(
|
||||
'Verification Failed',
|
||||
err?.response?.data?.message || "Invalid OTP. Please try again.",
|
||||
[{ text: 'Try Again', onPress: hideAlert, style: 'cancel' }],
|
||||
'error'
|
||||
);
|
||||
}
|
||||
};
|
||||
},
|
||||
onError: () => {
|
||||
ToastAndroid.show('Verification failed. Please try again.', ToastAndroid.SHORT);
|
||||
},
|
||||
});
|
||||
|
||||
const resendOtp = async () => {
|
||||
try {
|
||||
setIsResending(true);
|
||||
const { mutate: resendOtp, isPending: resending } = useCreate<any, any>(LOGIN_URL, {
|
||||
onSuccess: () => {
|
||||
setTimer(30);
|
||||
await axios.get(LOGIN_URL, { params: { mobile } })
|
||||
|
||||
setCode(Array(CELL_COUNT).fill(""));
|
||||
setCode(Array(CELL_COUNT).fill(''));
|
||||
inputs.current[0]?.focus();
|
||||
ToastAndroid.show('OTP Resent', ToastAndroid.SHORT);
|
||||
},
|
||||
onError: () => {
|
||||
ToastAndroid.show('Failed to resend OTP', ToastAndroid.SHORT);
|
||||
},
|
||||
});
|
||||
|
||||
let hasActionRun = false;
|
||||
const successAction = () => {
|
||||
if (hasActionRun) return;
|
||||
hasActionRun = true;
|
||||
hideAlert();
|
||||
};
|
||||
const handleTextChange = (text: string, index: number) => {
|
||||
const newCode = [...code];
|
||||
newCode[index] = text.slice(-1);
|
||||
setCode(newCode);
|
||||
|
||||
triggerAlert(
|
||||
'OTP Sent',
|
||||
'A new code has been sent to your mobile number.',
|
||||
[{ text: 'OK', onPress: successAction }],
|
||||
'success',
|
||||
successAction
|
||||
);
|
||||
} catch (err: any) {
|
||||
console.error("❌ Resend OTP Error:", err?.response?.data || err.message);
|
||||
triggerAlert(
|
||||
'Error',
|
||||
'Failed to resend OTP. Try again later.',
|
||||
[{ text: 'OK', onPress: hideAlert, style: 'cancel' }],
|
||||
'error'
|
||||
);
|
||||
} finally {
|
||||
setIsResending(false);
|
||||
if (text && index < CELL_COUNT - 1) {
|
||||
inputs.current[index + 1]?.focus();
|
||||
}
|
||||
|
||||
if (newCode.every((cell) => cell !== '')) {
|
||||
verifyOtp({ mobile, otp: newCode.join('') });
|
||||
}
|
||||
};
|
||||
|
||||
const submitIfComplete = (arr: string[]) => {
|
||||
if (arr.join("").length === CELL_COUNT) {
|
||||
onSubmit(arr.join(""), mobile);
|
||||
const handleKeyPress = (e: any, index: number) => {
|
||||
if (e.nativeEvent.key === 'Backspace' && !code[index] && index > 0) {
|
||||
inputs.current[index - 1]?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const updateIndex = (idx: number, val: string) => {
|
||||
const v = val.replace(/[^0-9]/g, "");
|
||||
const next = [...code];
|
||||
next[idx] = v.slice(-1);
|
||||
setCode(next);
|
||||
if (v && idx < CELL_COUNT - 1) inputs.current[idx + 1]?.focus();
|
||||
submitIfComplete(next);
|
||||
};
|
||||
|
||||
const handleFirstCellChange = (val: string) => {
|
||||
const digits = val.replace(/\D/g, "");
|
||||
if (digits.length <= 1) {
|
||||
updateIndex(0, digits);
|
||||
return;
|
||||
}
|
||||
const next = Array(CELL_COUNT).fill("").map((_, i) => digits[i] ?? "");
|
||||
setCode(next);
|
||||
const firstEmpty = next.findIndex((c) => !c);
|
||||
if (firstEmpty >= 0) {
|
||||
inputs.current[firstEmpty]?.focus();
|
||||
} else {
|
||||
inputs.current[CELL_COUNT - 1]?.blur();
|
||||
}
|
||||
submitIfComplete(next);
|
||||
};
|
||||
|
||||
const handleKeyPress = (idx: number, e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
|
||||
if (e.nativeEvent.key === "Backspace" && !code[idx] && idx > 0) {
|
||||
inputs.current[idx - 1]?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const colorsBlocks = [
|
||||
theme.colors.logincard,
|
||||
theme.colors.logincard1,
|
||||
theme.colors.logincard1,
|
||||
theme.colors.logincard,
|
||||
theme.colors.logincard,
|
||||
theme.colors.logincard1,
|
||||
theme.colors.logincard1,
|
||||
theme.colors.logincard,
|
||||
theme.colors.logincard,
|
||||
];
|
||||
|
||||
return (
|
||||
<View style={[styles.container, {backgroundColor: theme.colors.background }]}>
|
||||
<StatusBar barStyle={theme.mode === "dark" ? "light-content" : "dark-content"} backgroundColor={colors.pastelOrange} />
|
||||
|
||||
<View style={styles.gridContainer}>
|
||||
{colorsBlocks.map((bgColor, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[
|
||||
styles.gridBlock,
|
||||
{ backgroundColor: bgColor, borderRadius: 16 },
|
||||
index % 2 === 0 ? styles.elevatedBlock : {},
|
||||
]}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<KeyboardAvoidingView style={styles.keyboardAvoidingContainer} behavior={Platform.OS === "ios" ? "padding" : undefined}>
|
||||
<CustomAlert {...alertConfig} />
|
||||
|
||||
<View style={[styles.contentPanel, { backgroundColor: theme.colors.reverseBackground }]}>
|
||||
<View style={styles.topSection}>
|
||||
<Text style={[styles.title, { color: theme.colors.reverseText }]}>Verify Your Code</Text>
|
||||
<Text style={[styles.subtitle, { color: theme.colors.reverseText }]}>We’ve sent a 6-digit verification code to +91 {mobile}.</Text>
|
||||
|
||||
<Animated.View style={[styles.otpRow, { transform: [{ translateY: slide }] }]}>
|
||||
{Array.from({ length: CELL_COUNT }).map((_, i) => (
|
||||
<TextInput
|
||||
key={`otp-${i}`}
|
||||
ref={(r) => { inputs.current[i] = r; }}
|
||||
// UPDATED CELL STYLE
|
||||
style={[
|
||||
styles.cell,
|
||||
code[i] ? styles.cellFilled : null,
|
||||
{
|
||||
backgroundColor: theme.colors.optCard,
|
||||
color: theme.colors.reverseText,
|
||||
}
|
||||
]}
|
||||
|
||||
keyboardType="number-pad"
|
||||
maxLength={i === 0 ? CELL_COUNT : 1}
|
||||
value={code[i]}
|
||||
onChangeText={(t) => (i === 0 ? handleFirstCellChange(t) : updateIndex(i, t))}
|
||||
onKeyPress={(e) => handleKeyPress(i, e)}
|
||||
placeholder="*"
|
||||
placeholderTextColor="#BDBDBD"
|
||||
textContentType="oneTimeCode"
|
||||
/>
|
||||
))}
|
||||
</Animated.View>
|
||||
<AppView keyboardSafe={false} style={styles.container}>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardView}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.content,
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ translateY: slideAnim }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.headerSection}>
|
||||
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>Verification</Text>
|
||||
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>
|
||||
Enter the 6-digit code sent to {'\n'}
|
||||
<Text style={{ color: theme.colors.textPrimary, fontWeight: '700' }}>+91 {mobile}</Text>
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Animated.View style={[styles.bottomSection, { transform: [{ scale: ctaScale }] }]}>
|
||||
<Pressable
|
||||
onPressIn={() => Animated.spring(ctaScale, { toValue: 0.97, useNativeDriver: true }).start()}
|
||||
onPressOut={() => Animated.spring(ctaScale, { toValue: 1, useNativeDriver: true }).start()}
|
||||
onPress={() => onSubmit(code.join(""), mobile)}
|
||||
style={[styles.button, { backgroundColor: theme.colors.background }]}
|
||||
>
|
||||
<Text style={[styles.buttonText, { color: theme.colors.textPrimary }]}>Verify & Continue</Text>
|
||||
</Pressable>
|
||||
<View style={styles.otpSection}>
|
||||
{code.map((digit, index) => (
|
||||
<TextInput
|
||||
key={index}
|
||||
ref={(ref) => { inputs.current[index] = ref }}
|
||||
style={[
|
||||
styles.otpInput,
|
||||
{
|
||||
backgroundColor: theme.colors.surface,
|
||||
color: theme.colors.textPrimary,
|
||||
borderColor: digit ? theme.colors.primary : theme.colors.border,
|
||||
},
|
||||
]}
|
||||
keyboardType="number-pad"
|
||||
maxLength={1}
|
||||
value={digit}
|
||||
placeholder="0"
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
onChangeText={(text) => handleTextChange(text, index)}
|
||||
onKeyPress={(e) => handleKeyPress(e, index)}
|
||||
selectionColor={theme.colors.primary}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{timer > 0 ? (
|
||||
<Text style={[styles.footerText, { color: theme.colors.reverseText }]}>Resend code in 00:{timer.toString().padStart(2, "0")}</Text>
|
||||
) : (
|
||||
<Pressable onPress={resendOtp} disabled={isResending}>
|
||||
<Text style={[styles.footerText, { color: theme.colors.reverseText, fontWeight: '600' }]}>
|
||||
{isResending ? "Resending..." : "Resend Code"}
|
||||
<View style={styles.actionSection}>
|
||||
<CustomButton
|
||||
title="Verify Code"
|
||||
onPress={() => verifyOtp({ mobile, otp: code.join('') })}
|
||||
loading={verifying}
|
||||
style={styles.button}
|
||||
/>
|
||||
|
||||
<View style={styles.resendContainer}>
|
||||
{timer > 0 ? (
|
||||
<Text style={[styles.timerText, { color: theme.colors.textMuted }]}>
|
||||
Resend code in <Text style={{ fontWeight: '700' }}>00:{timer.toString().padStart(2, '0')}</Text>
|
||||
</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
</Animated.View>
|
||||
</View>
|
||||
) : (
|
||||
<TouchableOpacity onPress={() => resendOtp({ params: { mobile } })} disabled={resending}>
|
||||
<Text style={[styles.resendText, { color: theme.colors.primary }]}>
|
||||
{resending ? 'Sending...' : 'Resend Code'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Animated.View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</AppView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
gridContainer: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
keyboardView: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
content: {
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
headerSection: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 48,
|
||||
},
|
||||
title: {
|
||||
fontSize: responsiveFontSize(3.5),
|
||||
fontFamily: FontsFamily.nunitoBold,
|
||||
marginBottom: 12,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: responsiveFontSize(1.6),
|
||||
fontFamily: FontsFamily.nunitoRegular,
|
||||
textAlign: 'center',
|
||||
lineHeight: 24,
|
||||
},
|
||||
otpSection: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 40,
|
||||
},
|
||||
gridBlock: {
|
||||
width: BLOCK_SIZE,
|
||||
height: BLOCK_SIZE,
|
||||
margin: 2,
|
||||
borderRadius: 16,
|
||||
otpInput: {
|
||||
width: 48,
|
||||
height: 56,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1.5,
|
||||
textAlign: 'center',
|
||||
fontSize: responsiveFontSize(2.2),
|
||||
fontFamily: FontsFamily.nunitoBold,
|
||||
},
|
||||
elevatedBlock: {
|
||||
shadowColor: '#ffffffff',
|
||||
shadowOffset: { width: 1, height: 5 },
|
||||
shadowOpacity: 1,
|
||||
shadowRadius: 15,
|
||||
elevation: 10,
|
||||
actionSection: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
keyboardAvoidingContainer: { flex: 1, justifyContent: 'center' },
|
||||
contentPanel: {
|
||||
marginHorizontal: 24,
|
||||
borderRadius: 32,
|
||||
padding: 28,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
shadowOpacity: 0.14,
|
||||
shadowRadius: 24,
|
||||
elevation: 12,
|
||||
button: {
|
||||
width: '100%',
|
||||
marginBottom: 24,
|
||||
},
|
||||
topSection: { alignItems: 'center', marginBottom: 24 },
|
||||
title: { fontSize: 28, color: colors.textDark, textAlign: 'center', marginBottom: 12 ,fontFamily: FontsFamily.nunitoExtraBold},
|
||||
subtitle: { fontSize: 16, color: colors.textLight, textAlign: 'center', marginBottom: 32, lineHeight: 24, maxWidth: 300 ,fontFamily: FontsFamily.nunitoMedium},
|
||||
|
||||
otpRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
width: '100%',
|
||||
paddingHorizontal: 0, // Removed padding to use full width
|
||||
resendContainer: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
// FIX: Updated Cell Styling for clean smooth radius
|
||||
cell: {
|
||||
width: 45,
|
||||
height: 52,
|
||||
borderRadius: 14, // Smoother radius
|
||||
textAlign: "center",
|
||||
fontSize: 20,
|
||||
fontWeight: "600",
|
||||
elevation: 0, // Removed elevation for flat look (or keep if you want shadow)
|
||||
overflow: 'hidden',
|
||||
timerText: {
|
||||
fontSize: responsiveFontSize(1.4),
|
||||
fontFamily: FontsFamily.nunitoRegular,
|
||||
},
|
||||
cellFilled: {
|
||||
// Optional: Add specific style when filled if needed, handled inline above
|
||||
resendText: {
|
||||
fontSize: responsiveFontSize(1.5),
|
||||
fontFamily: FontsFamily.nunitoBold,
|
||||
textDecorationLine: 'underline',
|
||||
},
|
||||
|
||||
bottomSection: { paddingBottom: Platform.OS === "ios" ? 20 : 10, paddingTop: 10 },
|
||||
button: { backgroundColor: colors.pastelPink, paddingVertical: 18, borderRadius: 30, alignItems: "center", justifyContent: "center",},
|
||||
buttonText: { color: colors.white, fontSize: 16, fontFamily: FontsFamily.nunitoBold },
|
||||
footerText: { textAlign: "center", fontFamily: FontsFamily.nunitoBold, fontSize: 14, marginTop: 20 },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user