Files
info-target-mobile/screens/auth/confirm/ConfirmScreen.tsx
Samandar Turgunboyev a34cf75c57 bug fixed
2026-03-05 11:49:56 +05:00

345 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useAuth } from '@/components/AuthProvider';
import { registerForPushNotificationsAsync } from '@/components/NotificationProvider';
import AuthHeader from '@/components/ui/AuthHeader';
import { commonRequests } from '@/hooks/useNotifications';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import * as Haptics from 'expo-haptics';
import { LinearGradient } from 'expo-linear-gradient';
import { Redirect, useRouter } from 'expo-router';
import { ArrowLeft, MessageCircle, ShieldCheck } from 'lucide-react-native';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
Alert,
Linking,
Platform,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { Toast } from 'toastify-react-native';
import { auth_api } from '../login/lib/api';
import useTokenStore from '../login/lib/hook';
import ConfirmForm from './ConfirmForm';
const ConfirmScreen = () => {
const router = useRouter();
const queryClient = useQueryClient();
const [phoneOTP, setPhone] = useState<string | null>('');
const [error, setError] = useState<string>('');
const { login } = useAuth();
const { savedToken } = useTokenStore();
const [resendTimer, setResendTimer] = useState<number>(60);
const { t } = useTranslation();
useEffect(() => {
const loadPhone = async () => {
try {
const storedPhone = await AsyncStorage.getItem('phone');
if (storedPhone) setPhone(storedPhone);
else setPhone(null);
} catch (error) {
console.log('AsyncStorage error:', error);
}
};
loadPhone();
}, []);
useEffect(() => {
if (resendTimer === 0) return;
const timer = setTimeout(() => {
setResendTimer((prev) => prev - 1);
}, 1000);
return () => clearTimeout(timer);
}, [resendTimer]);
const { mutate, isPending } = useMutation({
mutationFn: (body: { code: string; phone: string }) => auth_api.verify_otp(body),
onSuccess: async (res) => {
// Tokenlarni saqlash
await AsyncStorage.removeItem('phone');
await AsyncStorage.setItem('access_token', res.data.data.token.access);
savedToken(res.data.data.token.access);
await AsyncStorage.setItem('refresh_token', res.data.data.token.refresh);
await login(res.data.data.token.access);
// **Push tokenni qayta serverga yuborish**
const pushToken = await registerForPushNotificationsAsync();
if (pushToken) {
await commonRequests.registerDevice({
token: pushToken,
platform: Platform.OS,
});
}
// Notification querylarni refetch
queryClient.refetchQueries({ queryKey: ['notification-list'] });
queryClient.refetchQueries({ queryKey: ['notifications-list'] });
// Dashboardga yonaltirish
router.replace('/(dashboard)');
},
onError: (err: any) => {
const errorMessage = err?.response?.data?.data?.detail || t("Kod noto'g'ri kiritildi");
Alert.alert(t('Xatolik yuz berdi'), errorMessage);
setError(errorMessage);
},
});
const resendMutation = useMutation({
mutationFn: async (body: { phone: string }) => auth_api.resend_otp(body),
onSuccess: () => {
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
setResendTimer(60);
if (Platform.OS === 'android') {
Toast.info(t('Kod qayta yuborildi'));
}
},
onError: () => {
Toast.error(t('Kodni qayta yuborishda xatolik yuz berdi'));
},
});
const openBotLink = () => {
const linkApp = `tg://resolve?domain=infotargetbot&start=register_${phoneOTP}`;
Linking.openURL(linkApp).catch(() => {
const webLink = `https://t.me/infotargetbot?start=register_${phoneOTP}`;
Linking.openURL(webLink);
});
};
if (phoneOTP === null) {
return <Redirect href={'/'} />;
}
return (
<View style={styles.container}>
<LinearGradient
colors={['#0f172a', '#1e293b', '#334155']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={StyleSheet.absoluteFill}
/>
<View style={styles.decorCircle1} />
<View style={styles.decorCircle2} />
<AuthHeader />
<KeyboardAwareScrollView
contentContainerStyle={styles.scrollContent}
enableOnAndroid
extraScrollHeight={120}
keyboardShouldPersistTaps="handled"
>
<View style={styles.header}>
<View style={styles.iconContainer}>
<LinearGradient colors={['#3b82f6', '#2563eb']} style={styles.iconGradient}>
<ShieldCheck size={32} color="#ffffff" strokeWidth={2.2} />
</LinearGradient>
</View>
<Text style={styles.title}>{t('Kodni tasdiqlash')}</Text>
<Text style={styles.subtitle}>
{t("Tasdiqlash kodi sizning Telegram botingizga yuboriladi. Botni ko'rish")}
</Text>
<View style={styles.phoneBadge}>
<Text style={styles.phoneText}>+{phoneOTP}</Text>
</View>
{/* Telegram Button */}
<TouchableOpacity
style={styles.telegramBanner}
onPress={openBotLink}
activeOpacity={0.8}
>
<LinearGradient
colors={['#0088cc', '#00a2ed']}
style={styles.telegramGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<View style={styles.botIconCircle}>
<MessageCircle size={20} color="#0088cc" fill="#fff" />
</View>
<View style={{ flex: 1 }}>
<Text style={styles.telegramTitle}>{t('Botni ochish')}</Text>
<Text style={styles.telegramSub}>
{t('Telegram botni ochish uchun tugmani bosing va kodni oling')}
</Text>
</View>
<ArrowLeft size={20} color="#fff" style={{ transform: [{ rotate: '180deg' }] }} />
</LinearGradient>
</TouchableOpacity>
</View>
<View style={styles.card}>
<ConfirmForm
onSubmit={(otp) => mutate({ code: otp, phone: phoneOTP || '' })}
isLoading={isPending}
error={error}
onResendPress={() => resendMutation.mutate({ phone: phoneOTP || '' })}
resendTimer={resendTimer}
/>
</View>
{/* <View style={styles.infoBox}>
<Text style={styles.infoText}>
<Text style={{ fontWeight: '700' }}>Eslatma:</Text> Kod SMS orqali kelmaydi. Agar
botni ishga tushirmagan bo'lsangiz, yuqoridagi tugmani bosing.
</Text>
</View> */}
</KeyboardAwareScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0f172a' },
scrollContent: {
paddingHorizontal: 24,
paddingBottom: 40,
flexGrow: 1,
justifyContent: 'center',
},
languageHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 24,
paddingVertical: 12,
zIndex: 1000,
},
backButton: {
width: 44,
height: 44,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.15)',
},
languageButton: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 12,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.15)',
},
languageText: { fontSize: 14, fontWeight: '600', color: '#94a3b8' },
header: { alignItems: 'center', marginBottom: 24 },
iconContainer: { marginBottom: 20 },
iconGradient: {
width: 72,
height: 72,
borderRadius: 22,
alignItems: 'center',
justifyContent: 'center',
elevation: 8,
shadowColor: '#3b82f6',
shadowOpacity: 0.4,
shadowRadius: 12,
},
title: { fontSize: 28, fontWeight: '800', color: '#ffffff', marginBottom: 8 },
subtitle: {
fontSize: 15,
color: '#94a3b8',
textAlign: 'center',
lineHeight: 22,
paddingHorizontal: 20,
},
highlightText: { color: '#38bdf8', fontWeight: '700' },
phoneBadge: {
marginTop: 16,
backgroundColor: 'rgba(59, 130, 246, 0.1)',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 12,
borderWidth: 1,
borderColor: 'rgba(59, 130, 246, 0.2)',
},
phoneText: { color: '#60a5fa', fontWeight: '700', fontSize: 16 },
telegramBanner: {
marginTop: 24,
width: '100%',
borderRadius: 18,
overflow: 'hidden',
elevation: 6,
shadowColor: '#0088cc',
shadowOpacity: 0.3,
shadowRadius: 10,
},
telegramGradient: { flexDirection: 'row', alignItems: 'center', padding: 14, gap: 12 },
botIconCircle: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
telegramTitle: { color: '#ffffff', fontSize: 16, fontWeight: '700' },
telegramSub: { color: 'rgba(255, 255, 255, 0.8)', fontSize: 12 },
card: {
backgroundColor: '#ffffff',
borderRadius: 28,
padding: 24,
elevation: 10,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 15,
},
infoBox: {
marginTop: 20,
padding: 16,
backgroundColor: 'rgba(245, 158, 11, 0.08)',
borderRadius: 16,
borderWidth: 1,
borderColor: 'rgba(245, 158, 11, 0.2)',
},
infoText: { fontSize: 13, color: '#fbbf24', textAlign: 'center', lineHeight: 20 },
dropdown: {
position: 'absolute',
top: 55,
right: 0,
backgroundColor: '#fff',
borderRadius: 16,
padding: 8,
minWidth: 170,
elevation: 10,
zIndex: 2000,
},
dropdownOption: { padding: 12, borderRadius: 10 },
dropdownOptionActive: { backgroundColor: '#eff6ff' },
dropdownOptionText: { fontSize: 14, color: '#475569', fontWeight: '600' },
dropdownOptionTextActive: { color: '#3b82f6' },
decorCircle1: {
position: 'absolute',
top: -100,
right: -80,
width: 300,
height: 300,
borderRadius: 150,
backgroundColor: 'rgba(59, 130, 246, 0.1)',
},
decorCircle2: {
position: 'absolute',
bottom: -50,
left: -100,
width: 250,
height: 250,
borderRadius: 125,
backgroundColor: 'rgba(59, 130, 246, 0.05)',
},
});
export default ConfirmScreen;