diff --git a/app.json b/app.json index ff5900b..8b8ce6e 100644 --- a/app.json +++ b/app.json @@ -11,12 +11,16 @@ "ios": { "supportsTablet": true, "infoPlist": { - "UIBackgroundModes": ["remote-notification"] + "UIBackgroundModes": [ + "remote-notification" + ], + "UIViewControllerBasedStatusBarAppearance": true }, "bundleIdentifier": "com.felix.infotarget" }, "android": { "useNextNotificationsApi": true, + "softwareKeyboardLayoutMode": "resize", "adaptiveIcon": { "backgroundColor": "#E6F4FE", "foregroundImage": "./assets/images/logo.png" @@ -86,4 +90,4 @@ }, "owner": "samandar111" } -} +} \ No newline at end of file diff --git a/app/(dashboard)/_layout.tsx b/app/(dashboard)/_layout.tsx index 7f4c7c2..794efbe 100644 --- a/app/(dashboard)/_layout.tsx +++ b/app/(dashboard)/_layout.tsx @@ -5,18 +5,30 @@ import { useHomeStore } from '@/screens/home/lib/hook'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; import { router, Tabs } from 'expo-router'; import { Home, Megaphone, PlusCircle, User } from 'lucide-react-native'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Animated, Easing, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Animated, Easing, Keyboard, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; export default function TabsLayout() { const { isDark } = useTheme(); const { t } = useTranslation(); const { setShowFilter, setStep } = useHomeStore(); const rotateAnim = useRef(new Animated.Value(0)).current; - const insets = useSafeAreaInsets(); + const [keyboard, setKeyboard] = useState(false); + + useEffect(() => { + const showSub = Keyboard.addListener('keyboardDidShow', () => setKeyboard(true)); + const hideSub = Keyboard.addListener('keyboardDidHide', () => setKeyboard(false)); + + return () => { + showSub.remove(); + hideSub.remove(); + }; + }, []); + + console.log(keyboard); + useEffect(() => { Animated.loop( @@ -54,6 +66,7 @@ export default function TabsLayout() { headerShadowVisible: false, tabBarStyle: { position: 'absolute', + display: keyboard ? "none" : "flex", left: 16, right: 16, bottom: 8, diff --git a/app/(dashboard)/create-announcements.tsx b/app/(dashboard)/create-announcements.tsx index f0af7c1..2b0a888 100644 --- a/app/(dashboard)/create-announcements.tsx +++ b/app/(dashboard)/create-announcements.tsx @@ -1,10 +1,8 @@ -import { useTheme } from '@/components/ThemeContext'; import { FilterProvider } from '@/components/ui/FilterContext'; import { CustomHeader } from '@/components/ui/Header'; import CreateAdsScreens from '@/screens/create-ads/ui/CreateAdsScreens'; export default function CreateAnnouncements() { - const { isDark } = useTheme(); return ( diff --git a/app/_layout.tsx b/app/_layout.tsx index 50e1508..c40660f 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -15,17 +15,13 @@ function AppContent() { useNotifications(); return ( - - - + + ); } + export default function RootLayout() { return ( diff --git a/components/AuthProvider.tsx b/components/AuthProvider.tsx index 032798f..238bf67 100644 --- a/components/AuthProvider.tsx +++ b/components/AuthProvider.tsx @@ -1,4 +1,5 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; +import { router } from 'expo-router'; import { createContext, useContext, useEffect, useState } from 'react'; type AuthContextType = { @@ -32,6 +33,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { await AsyncStorage.removeItem('access_token'); await AsyncStorage.removeItem('refresh_token'); setIsAuthenticated(false); + router.replace('/(auth)'); }; return ( diff --git a/screens/auth/confirm/ConfirmScreen.tsx b/screens/auth/confirm/ConfirmScreen.tsx index d825a81..7e3142e 100644 --- a/screens/auth/confirm/ConfirmScreen.tsx +++ b/screens/auth/confirm/ConfirmScreen.tsx @@ -20,7 +20,6 @@ import { View } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; -import { SafeAreaView } from 'react-native-safe-area-context'; import { Toast } from 'toastify-react-native'; import { auth_api } from '../login/lib/api'; import useTokenStore from '../login/lib/hook'; @@ -130,72 +129,71 @@ const ConfirmScreen = () => { - - - - - - - - + + + + + + + - {t('Kodni tasdiqlash')} - - {t("Tasdiqlash kodi sizning Telegram botingizga yuboriladi. Botni ko'rish")} - + {t('Kodni tasdiqlash')} + + {t("Tasdiqlash kodi sizning Telegram botingizga yuboriladi. Botni ko'rish")} + - - +{phoneOTP} - + + +{phoneOTP} + - {/* Telegram Button */} - + - - - - - - {t('Botni ochish')} - - {t('Telegram botni ochish uchun tugmani bosing va kodni oling')} - - - - - - + + + + + {t('Botni ochish')} + + {t('Telegram botni ochish uchun tugmani bosing va kodni oling')} + + + + + + - - mutate({ code: otp, phone: phoneOTP || '' })} - isLoading={isPending} - error={error} - onResendPress={() => resendMutation.mutate({ phone: phoneOTP || '' })} - resendTimer={resendTimer} - /> - + + mutate({ code: otp, phone: phoneOTP || '' })} + isLoading={isPending} + error={error} + onResendPress={() => resendMutation.mutate({ phone: phoneOTP || '' })} + resendTimer={resendTimer} + /> + - {/* + {/* Eslatma: Kod SMS orqali kelmaydi. Agar botni ishga tushirmagan bo'lsangiz, yuqoridagi tugmani bosing. */} - - + ); }; diff --git a/screens/auth/register/RegisterForm.tsx b/screens/auth/register/RegisterForm.tsx index b2699bb..47a5415 100644 --- a/screens/auth/register/RegisterForm.tsx +++ b/screens/auth/register/RegisterForm.tsx @@ -26,12 +26,10 @@ import { useTranslation } from 'react-i18next'; import { ActivityIndicator, Keyboard, - ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, - TouchableWithoutFeedback, View } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; @@ -110,6 +108,7 @@ export default function RegisterFormScreen() { }); const openCountrySheet = useCallback(() => { + Keyboard.dismiss(); setTimeout(() => { countrySheetRef.current?.snapToIndex(0); }, 100); @@ -162,7 +161,6 @@ export default function RegisterFormScreen() { onSuccess: (res) => { setInfo(res.data.data); setLoading(false); - // INN o'zgarganda director ma'lumotlarini tozalash setDirectorJshshr(''); setDirectorInfo(null); setErrorDirectorInfo(null); @@ -180,26 +178,16 @@ export default function RegisterFormScreen() { mutationFn: (body: { value: string }) => auth_api.get_director_info(body), onSuccess: (res) => { const directorData = res.data; - - // ------------------------------------------------------- - // INN TEKSHIRUVI - // Director response strukturasi: - // data.entity.name.rows = [{ inn: "123456789", ... }, ...] - // Shu rows ichida joriy inn bor-yo'qligini tekshiramiz - // ------------------------------------------------------- const rows: Array<{ inn: string }> = directorData?.data?.entity?.name?.rows ?? []; - const innMatch = rows.some((row) => row.inn === inn); if (!innMatch) { - // Director bu tashkilotga tegishli emas setDirectorInfo(null); setErrorDirectorInfo(t("Bu direktor ko'rsatilgan tashkilotga tegishli emas")); setDirectorLoading(false); return; } - // INN mos keldi — director ma'lumotini saqlash setDirectorInfo(directorData); setDirectorLoading(false); setErrorDirectorInfo(null); @@ -326,7 +314,7 @@ export default function RegisterFormScreen() { inn.length === 9 && info && directorJshshr.length === 14 && - directorInfo // faqat INN mos kelganda to'ldiriladi + directorInfo ); } @@ -334,7 +322,6 @@ export default function RegisterFormScreen() { })(); const handleContinue = () => { - // Director to'liq ismi directorInfo dan olinadi const directorFullName = isLegal ? (directorInfo?.data?.entrepreneur?.rows[0]?.entrepreneur ?? '') : `${info?.firstname ?? ''} ${info?.lastname ?? ''} ${info?.middlename ?? ''}`.trim(); @@ -361,14 +348,22 @@ export default function RegisterFormScreen() { }; return ( - - - + // ✅ Fragment — BottomSheet tashqarida bo'lishi uchun + <> + + {/* ✅ TouchableWithoutFeedback yo'q — onStartShouldSetResponder ishlatildi */} + { + Keyboard.dismiss(); + return false; // false — child elementlar (buttonlar) ham ishlaydi + }} + > + {/* ✅ SafeAreaView va ichki ScrollView yo'q — to'g'ridan View */} - - + - {/* ---- Davlat (Country) ---- */} - {/* - {t('Davlat')} - - {countryLoading ? ( - - ) : ( - <> - - - {selectedCountryName} - - - )} - - - */} {/* ---- YATT / BAND ---- */} {isYattOrBand && ( <> @@ -544,7 +498,7 @@ export default function RegisterFormScreen() { ))} - {/* ---- LEGAL ENTITY: Direktor JSHSHR — faqat info kelganda ko'rinadi ---- */} + {/* ---- LEGAL ENTITY: Direktor JSHSHR ---- */} {isLegal && info && ( {t('Direktor JSHSHR')} @@ -565,7 +519,6 @@ export default function RegisterFormScreen() { )} - {/* ---- Direktor ma'lumoti (INN mos kelgan holda) ---- */} {directorInfo && ( {t('Direktor')} @@ -575,7 +528,6 @@ export default function RegisterFormScreen() { )} - {/* ---- Xato: INN mos kelmasa yoki server xatosi ---- */} {directorInfoError && ( {directorInfoError} @@ -648,98 +600,103 @@ export default function RegisterFormScreen() { - + - - {/* ---- Country BottomSheet ---- */} - - - {t('Davlat')} - - - {/* Search input */} - - - - - - item.id?.toString()} - contentContainerStyle={styles.listContainer} - showsVerticalScrollIndicator={false} - keyboardShouldPersistTaps="handled" - ListEmptyComponent={ - - {t('Natija topilmadi')} - - } - renderItem={({ item }: { item: any }) => { - const isSelected = item.flag?.toUpperCase() === selectedCountry; - const flagCode = item.flag ? item.flag.toLowerCase() : ''; - return ( - { - setSelectedCountry(item.flag?.toUpperCase()); - closeCountrySheet(); - }} - activeOpacity={0.7} - > - - {flagCode ? ( - - ) : ( - - )} - - {item.name} - - - {isSelected && ( - - - - )} - - ); - }} - /> - - - + + + {/* ✅ BottomSheet KeyboardAwareScrollView TASHQARISIDA */} + + + {t('Davlat')} + + + + + + + + item.id?.toString()} + contentContainerStyle={styles.listContainer} + showsVerticalScrollIndicator={false} + keyboardShouldPersistTaps="handled" + ListEmptyComponent={ + + {t('Natija topilmadi')} + + } + renderItem={({ item }: { item: any }) => { + const isSelected = item.flag?.toUpperCase() === selectedCountry; + const flagCode = item.flag ? item.flag.toLowerCase() : ''; + return ( + { + setSelectedCountry(item.flag?.toUpperCase()); + closeCountrySheet(); + }} + activeOpacity={0.7} + > + + {flagCode ? ( + + ) : ( + + )} + + {item.name} + + + {isSelected && ( + + + + )} + + ); + }} + /> + + ); } const styles = StyleSheet.create({ + // ✅ KeyboardAwareScrollView uchun style + keyboardScroll: { + flex: 1, + backgroundColor: '#0f172a', + }, container: { flex: 1, backgroundColor: '#0f172a', + minHeight: '100%', }, scrollContent: { flexGrow: 1, @@ -969,15 +926,6 @@ const styles = StyleSheet.create({ fontWeight: '800' as const, fontSize: 16, }, - footer: { - marginTop: 20, - alignItems: 'center', - }, - footerText: { - color: '#94a3b8', - fontSize: 14, - fontWeight: '600' as const, - }, decorCircle1: { position: 'absolute', top: -150, @@ -997,7 +945,7 @@ const styles = StyleSheet.create({ backgroundColor: 'rgba(16, 185, 129, 0.08)', }, inputDisabled: { - backgroundColor: '#f1f5f9', // disabled bo'lganda fon rangi - borderColor: '#e2e8f0', // disabled bo'lganda border rangi + backgroundColor: '#f1f5f9', + borderColor: '#e2e8f0', }, -}); +}); \ No newline at end of file diff --git a/screens/create-ads/ui/CreateAdsScreens.tsx b/screens/create-ads/ui/CreateAdsScreens.tsx index 20637c2..1f5170e 100644 --- a/screens/create-ads/ui/CreateAdsScreens.tsx +++ b/screens/create-ads/ui/CreateAdsScreens.tsx @@ -1,5 +1,5 @@ import { useTheme } from '@/components/ThemeContext'; -import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView, TouchableWithoutFeedback } from '@gorhom/bottom-sheet'; +import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet'; import { useMutation, useQuery } from '@tanstack/react-query'; import { AxiosError } from 'axios'; import { router, useFocusEffect } from 'expo-router'; @@ -9,17 +9,17 @@ import { Image, Keyboard, Linking, - ScrollView, StyleSheet, Text, TouchableOpacity, + TouchableWithoutFeedback, View } from 'react-native'; import OneClick from '@/assets/images/one_click.png'; import PAYME from '@/assets/images/Payme_NEW.png'; import { useTranslation } from 'react-i18next'; -import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import { KeyboardAwareScrollView, } from 'react-native-keyboard-aware-scroll-view'; import { price_calculation } from '../lib/api'; import { CreateAdsResponse } from '../lib/types'; import StepFour from './StepFour'; @@ -213,12 +213,16 @@ export default function CreateAdsScreens() { - + { + Keyboard.dismiss(); + return false; + }} + > - - - {/* FOOTER */} - - {/* PAYMENT BOTTOM SHEET */} - - - - - {t("To'lov turini tanlang")} - - - sendPayment('payme')} - > - - - - sendPayment('referral')} - > - - {t('Referal orqali')} - - - - - + + + + + + {t("To'lov turini tanlang")} + + + sendPayment('payme')} + > + + + + sendPayment('referral')} + > + + {t('Referal orqali')} + + + + + ); } diff --git a/screens/profile/ui/CreateReferrals.tsx b/screens/profile/ui/CreateReferrals.tsx index 94a6f7d..f08bfd7 100644 --- a/screens/profile/ui/CreateReferrals.tsx +++ b/screens/profile/ui/CreateReferrals.tsx @@ -60,8 +60,6 @@ export default function CreateReferrals() { const validate = () => { const e: any = {}; - if (!form.code || form.code.length !== 9) - e.code = 'Kod aynan 9 ta belgidan iborat bo‘lishi kerak'; if (!form.description || form.description.length < 5) e.description = 'Tavsif kamida 5 ta belgidan iborat bo‘lishi kerak'; diff --git a/screens/profile/ui/RefferallsTab.tsx b/screens/profile/ui/RefferallsTab.tsx index 72661a2..64c7df2 100644 --- a/screens/profile/ui/RefferallsTab.tsx +++ b/screens/profile/ui/RefferallsTab.tsx @@ -68,7 +68,7 @@ export function ReferralsTab() { // Clipboard + Share funksiyasi const handleCopyAndShare = async (code: string) => { - const referralLink = `https://t.me/infotargetbot/join?startapp=${code}`; + const referralLink = `${code}`; // Clipboard-ga nusxa olish await Clipboard.setStringAsync(referralLink);