keyboard scroll

This commit is contained in:
Samandar Turgunboyev
2026-03-02 15:46:12 +05:00
parent 4d5cc84850
commit c71651ec4b
2 changed files with 216 additions and 209 deletions

View File

@@ -25,15 +25,16 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
ActivityIndicator, ActivityIndicator,
KeyboardAvoidingView, Keyboard,
Platform,
ScrollView, ScrollView,
StyleSheet, StyleSheet,
Text, Text,
TextInput, TextInput,
TouchableOpacity, TouchableOpacity,
View, TouchableWithoutFeedback,
View
} from 'react-native'; } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
import { auth_api } from '../login/lib/api'; import { auth_api } from '../login/lib/api';
import PhonePrefix from '../login/ui/PhonePrefix'; import PhonePrefix from '../login/ui/PhonePrefix';
@@ -360,27 +361,27 @@ export default function RegisterFormScreen() {
}; };
return ( return (
<KeyboardAvoidingView <KeyboardAwareScrollView
behavior="padding" showsVerticalScrollIndicator={false}
style={{ flex: 1 }} keyboardShouldPersistTaps="handled"
enableOnAndroid
extraScrollHeight={120}
> >
<View style={styles.container}> <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<LinearGradient <View style={styles.container}>
colors={['#0f172a', '#1e293b', '#334155']} <LinearGradient
start={{ x: 0, y: 0 }} colors={['#0f172a', '#1e293b', '#334155']}
end={{ x: 1, y: 1 }} start={{ x: 0, y: 0 }}
style={StyleSheet.absoluteFill} end={{ x: 1, y: 1 }}
/> style={StyleSheet.absoluteFill}
<View style={styles.decorCircle1} /> />
<View style={styles.decorCircle2} /> <View style={styles.decorCircle1} />
<View style={styles.decorCircle2} />
<AuthHeader /> <AuthHeader />
<SafeAreaView style={{ flex: 1 }} edges={['bottom']}>
<SafeAreaView style={{ flex: 1 }} edges={['bottom']}>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
>
<ScrollView <ScrollView
contentContainerStyle={styles.scrollContent} contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
@@ -647,90 +648,90 @@ export default function RegisterFormScreen() {
</View> </View>
</View> </View>
</ScrollView> </ScrollView>
</KeyboardAvoidingView> </SafeAreaView>
</SafeAreaView>
{/* ---- Country BottomSheet ---- */} {/* ---- Country BottomSheet ---- */}
<BottomSheet <BottomSheet
ref={countrySheetRef} ref={countrySheetRef}
index={-1} index={-1}
snapPoints={snapPoints} snapPoints={snapPoints}
enablePanDownToClose={true} enablePanDownToClose={true}
enableDynamicSizing={false} enableDynamicSizing={false}
enableOverDrag={false} enableOverDrag={false}
backdropComponent={renderBackdrop} backdropComponent={renderBackdrop}
backgroundStyle={styles.bottomSheetBg} backgroundStyle={styles.bottomSheetBg}
handleIndicatorStyle={styles.handleIndicator} handleIndicatorStyle={styles.handleIndicator}
android_keyboardInputMode="adjustResize" android_keyboardInputMode="adjustResize"
keyboardBehavior="interactive" keyboardBehavior="interactive"
keyboardBlurBehavior="restore" keyboardBlurBehavior="restore"
> >
<View style={styles.sheetHeader}> <View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>{t('Davlat')}</Text> <Text style={styles.sheetTitle}>{t('Davlat')}</Text>
</View> </View>
{/* Search input */} {/* Search input */}
<View style={styles.searchContainer}> <View style={styles.searchContainer}>
<Search size={16} color="#94a3b8" /> <Search size={16} color="#94a3b8" />
<BottomSheetTextInput <BottomSheetTextInput
value={countrySearch} value={countrySearch}
onChangeText={setCountrySearch} onChangeText={setCountrySearch}
placeholder={t('Qidirish...')} placeholder={t('Qidirish...')}
placeholderTextColor="#94a3b8" placeholderTextColor="#94a3b8"
style={styles.searchInput} style={styles.searchInput}
clearButtonMode="while-editing" clearButtonMode="while-editing"
autoCorrect={false} autoCorrect={false}
/> />
</View> </View>
<BottomSheetFlatList <BottomSheetFlatList
data={filteredCountries} data={filteredCountries}
keyExtractor={(item: any) => item.id?.toString()} keyExtractor={(item: any) => item.id?.toString()}
contentContainerStyle={styles.listContainer} contentContainerStyle={styles.listContainer}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
ListEmptyComponent={ ListEmptyComponent={
<View style={styles.emptyList}> <View style={styles.emptyList}>
<Text style={styles.emptyListText}>{t('Natija topilmadi')}</Text> <Text style={styles.emptyListText}>{t('Natija topilmadi')}</Text>
</View> </View>
} }
renderItem={({ item }: { item: any }) => { renderItem={({ item }: { item: any }) => {
const isSelected = item.flag?.toUpperCase() === selectedCountry; const isSelected = item.flag?.toUpperCase() === selectedCountry;
const flagCode = item.flag ? item.flag.toLowerCase() : ''; const flagCode = item.flag ? item.flag.toLowerCase() : '';
return ( return (
<TouchableOpacity <TouchableOpacity
style={[styles.listItem, isSelected && styles.selectedListItem]} style={[styles.listItem, isSelected && styles.selectedListItem]}
onPress={() => { onPress={() => {
setSelectedCountry(item.flag?.toUpperCase()); setSelectedCountry(item.flag?.toUpperCase());
closeCountrySheet(); closeCountrySheet();
}} }}
activeOpacity={0.7} activeOpacity={0.7}
> >
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 10, flex: 1 }}> <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10, flex: 1 }}>
{flagCode ? ( {flagCode ? (
<Image <Image
source={{ uri: `https://flagcdn.com/w320/${flagCode}.png` }} source={{ uri: `https://flagcdn.com/w320/${flagCode}.png` }}
style={{ width: 34, height: 22, borderRadius: 2 }} style={{ width: 34, height: 22, borderRadius: 2 }}
/> />
) : ( ) : (
<Globe size={20} color={isSelected ? '#2563eb' : '#94a3b8'} /> <Globe size={20} color={isSelected ? '#2563eb' : '#94a3b8'} />
)} )}
<Text style={[styles.listItemText, isSelected && styles.selectedListItemText]}> <Text style={[styles.listItemText, isSelected && styles.selectedListItemText]}>
{item.name} {item.name}
</Text> </Text>
</View>
{isSelected && (
<View style={styles.checkmark}>
<CheckIcon color="#3b82f6" strokeWidth={2.5} size={16} />
</View> </View>
)} {isSelected && (
</TouchableOpacity> <View style={styles.checkmark}>
); <CheckIcon color="#3b82f6" strokeWidth={2.5} size={16} />
}} </View>
/> )}
</BottomSheet> </TouchableOpacity>
</View> );
</KeyboardAvoidingView> }}
/>
</BottomSheet>
</View>
</TouchableWithoutFeedback>
</KeyboardAwareScrollView>
); );
} }

View File

@@ -1,5 +1,5 @@
import { useTheme } from '@/components/ThemeContext'; import { useTheme } from '@/components/ThemeContext';
import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet'; import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView, TouchableWithoutFeedback } from '@gorhom/bottom-sheet';
import { useMutation, useQuery } from '@tanstack/react-query'; import { useMutation, useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { router, useFocusEffect } from 'expo-router'; import { router, useFocusEffect } from 'expo-router';
@@ -7,18 +7,19 @@ import React, { useCallback, useRef, useState } from 'react';
import { import {
Alert, Alert,
Image, Image,
KeyboardAvoidingView, Keyboard,
Linking, Linking,
ScrollView, ScrollView,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,
View, View
} from 'react-native'; } from 'react-native';
import OneClick from '@/assets/images/one_click.png'; import OneClick from '@/assets/images/one_click.png';
import PAYME from '@/assets/images/Payme_NEW.png'; import PAYME from '@/assets/images/Payme_NEW.png';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { price_calculation } from '../lib/api'; import { price_calculation } from '../lib/api';
import { CreateAdsResponse } from '../lib/types'; import { CreateAdsResponse } from '../lib/types';
import StepFour from './StepFour'; import StepFour from './StepFour';
@@ -209,122 +210,127 @@ export default function CreateAdsScreens() {
}; };
return ( return (
<KeyboardAvoidingView <KeyboardAwareScrollView
behavior="padding" showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
enableOnAndroid
extraScrollHeight={120}
style={[styles.safeArea, isDark ? styles.darkBg : styles.lightBg]} style={[styles.safeArea, isDark ? styles.darkBg : styles.lightBg]}
> >
<ScrollView contentContainerStyle={[styles.container, { paddingBottom: 90 }]}> <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<Image <ScrollView contentContainerStyle={[styles.container, { paddingBottom: 90 }]}>
source={OneClick} <Image
style={{ width: 180, height: 56, marginBottom: 10 }} source={OneClick}
resizeMode="contain" style={{ width: 180, height: 56, marginBottom: 10 }}
/> resizeMode="contain"
<Text style={[styles.title, isDark ? styles.darkText : styles.lightText]}>
{t("Bir Zumda Jonatish")}
</Text>
{currentStep === 1 && (
<StepOne ref={stepOneRef} formData={formData} updateForm={updateForm} />
)}
{currentStep === 2 && (
<StepTwo ref={stepTwoRef} formData={formData} updateForm={updateForm} />
)}
{currentStep === 3 && (
<StepThree
ref={stepThreeRef}
formData={formData}
updateForm={updateForm}
data={data?.data}
/> />
)} <Text style={[styles.title, isDark ? styles.darkText : styles.lightText]}>
{currentStep === 4 && <StepFour data={ads} setPayment={setPaymentType} />} {t("Bir Zumda Jonatish")}
<View style={styles.footer}> </Text>
{currentStep > 1 && currentStep !== 4 && (
<TouchableOpacity {currentStep === 1 && (
style={[styles.back, isDark ? styles.darkBack : styles.lightBack]} <StepOne ref={stepOneRef} formData={formData} updateForm={updateForm} />
onPress={() => setCurrentStep((s) => s - 1)}
>
<Text style={[styles.btnText, isDark ? styles.darkBtnText : styles.lightBtnText]}>
{t('Orqaga')}
</Text>
</TouchableOpacity>
)} )}
{currentStep === 2 && (
<TouchableOpacity <StepTwo ref={stepTwoRef} formData={formData} updateForm={updateForm} />
style={styles.next} )}
disabled={isPending} {currentStep === 3 && (
onPress={() => { <StepThree
let isValid = true; ref={stepThreeRef}
formData={formData}
if (currentStep === 1) isValid = stepOneRef.current?.validate() ?? false; updateForm={updateForm}
if (currentStep === 2) isValid = stepTwoRef.current?.validate() ?? false; data={data?.data}
if (currentStep === 3) isValid = stepThreeRef.current?.validate() ?? false; />
)}
if (!isValid) return; {currentStep === 4 && <StepFour data={ads} setPayment={setPaymentType} />}
<View style={styles.footer}>
if (currentStep < 3) setCurrentStep((s) => s + 1); {currentStep > 1 && currentStep !== 4 && (
if (currentStep === 3) handleSubmit(); <TouchableOpacity
if (currentStep === 4) handlePresentModalPress(); style={[styles.back, isDark ? styles.darkBack : styles.lightBack]}
}} onPress={() => setCurrentStep((s) => s - 1)}
> >
<Text style={styles.btnText}> <Text style={[styles.btnText, isDark ? styles.darkBtnText : styles.lightBtnText]}>
{currentStep === 3 {t('Orqaga')}
? t('Yaratish') </Text>
: currentStep === 4 </TouchableOpacity>
? t("To'lash") )}
: t('Keyingisi')}
</Text>
</TouchableOpacity>
</View>
</ScrollView>
{/* FOOTER */}
{/* PAYMENT BOTTOM SHEET */}
<BottomSheetModal
ref={bottomSheetModalRef}
index={0}
snapPoints={['70%', '95%']}
backdropComponent={renderBackdrop}
handleIndicatorStyle={{ backgroundColor: '#94a3b8', width: 50 }}
backgroundStyle={{ backgroundColor: isDark ? '#0f172a' : '#ffffff' }}
enablePanDownToClose
>
<BottomSheetScrollView
style={styles.sheetContent}
contentContainerStyle={styles.sheetContentContainer}
>
<View style={{ padding: 20 }}>
<Text style={[styles.sheetTitle, isDark ? styles.darkText : styles.lightText]}>
{t("To'lov turini tanlang")}
</Text>
<TouchableOpacity <TouchableOpacity
style={[ style={styles.next}
styles.paymentItem, disabled={isPending}
isDark ? styles.darkPaymentItem : styles.lightPaymentItem, onPress={() => {
{ flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' }, let isValid = true;
]}
onPress={() => sendPayment('payme')}
>
<Image source={PAYME} style={{ width: 80, height: 80 }} />
</TouchableOpacity>
<TouchableOpacity if (currentStep === 1) isValid = stepOneRef.current?.validate() ?? false;
style={[ if (currentStep === 2) isValid = stepTwoRef.current?.validate() ?? false;
styles.paymentItem, if (currentStep === 3) isValid = stepThreeRef.current?.validate() ?? false;
isDark ? styles.darkPaymentItem : styles.lightPaymentItem,
]} if (!isValid) return;
onPress={() => sendPayment('referral')}
if (currentStep < 3) setCurrentStep((s) => s + 1);
if (currentStep === 3) handleSubmit();
if (currentStep === 4) handlePresentModalPress();
}}
> >
<Text style={[styles.paymentText, isDark ? styles.darkText : styles.lightText]}> <Text style={styles.btnText}>
{t('Referal orqali')} {currentStep === 3
? t('Yaratish')
: currentStep === 4
? t("To'lash")
: t('Keyingisi')}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</BottomSheetScrollView> </ScrollView>
</BottomSheetModal>
</KeyboardAvoidingView> {/* FOOTER */}
{/* PAYMENT BOTTOM SHEET */}
<BottomSheetModal
ref={bottomSheetModalRef}
index={0}
snapPoints={['70%', '95%']}
backdropComponent={renderBackdrop}
handleIndicatorStyle={{ backgroundColor: '#94a3b8', width: 50 }}
backgroundStyle={{ backgroundColor: isDark ? '#0f172a' : '#ffffff' }}
enablePanDownToClose
>
<BottomSheetScrollView
style={styles.sheetContent}
contentContainerStyle={styles.sheetContentContainer}
>
<View style={{ padding: 20 }}>
<Text style={[styles.sheetTitle, isDark ? styles.darkText : styles.lightText]}>
{t("To'lov turini tanlang")}
</Text>
<TouchableOpacity
style={[
styles.paymentItem,
isDark ? styles.darkPaymentItem : styles.lightPaymentItem,
{ flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' },
]}
onPress={() => sendPayment('payme')}
>
<Image source={PAYME} style={{ width: 80, height: 80 }} />
</TouchableOpacity>
<TouchableOpacity
style={[
styles.paymentItem,
isDark ? styles.darkPaymentItem : styles.lightPaymentItem,
]}
onPress={() => sendPayment('referral')}
>
<Text style={[styles.paymentText, isDark ? styles.darkText : styles.lightText]}>
{t('Referal orqali')}
</Text>
</TouchableOpacity>
</View>
</BottomSheetScrollView>
</BottomSheetModal>
</TouchableWithoutFeedback>
</KeyboardAwareScrollView>
); );
} }