This commit is contained in:
shafi54 2026-02-01 14:01:22 +05:30
parent ca8297af9b
commit 001dd62aa5
2 changed files with 75 additions and 16 deletions

View file

@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { View, Alert, ScrollView } from 'react-native'; import { View, ScrollView } from 'react-native';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import { Formik } from 'formik'; import { Formik } from 'formik';
import * as Yup from 'yup'; import * as Yup from 'yup';
@ -30,39 +30,44 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
const [locationLoading, setLocationLoading] = useState(false); const [locationLoading, setLocationLoading] = useState(false);
const [locationError, setLocationError] = useState<string | null>(null); const [locationError, setLocationError] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null);
const [showGoogleMapsField, setShowGoogleMapsField] = useState(!!initialValues?.googleMapsUrl); const [showGoogleMapsField, setShowGoogleMapsField] = useState(!!initialValues?.googleMapsUrl);
const [currentLocation, setCurrentLocation] = useState<{ latitude: number; longitude: number } | null>( const [currentLocation, setCurrentLocation] = useState<{ latitude: number; longitude: number } | null>(
initialValues?.latitude && initialValues?.longitude initialValues?.latitude && initialValues?.longitude
? { latitude: initialValues.latitude, longitude: initialValues.longitude } ? { latitude: initialValues.latitude, longitude: initialValues.longitude }
: null : null
); );
const [locationSuccess, setLocationSuccess] = useState(false);
const createAddressMutation = trpc.user.address.createAddress.useMutation({ const createAddressMutation = trpc.user.address.createAddress.useMutation({
onSuccess: (data) => { onSuccess: (data) => {
setIsSubmitting(false); setIsSubmitting(false);
const addressId = data?.data?.id; const addressId = data?.data?.id;
setTimeout(() => onSuccess(addressId), 100); // Delay to allow modal to close animation
setTimeout(() => onSuccess(addressId), 350);
}, },
onError: (error: any) => { onError: (error: any) => {
setIsSubmitting(false); setIsSubmitting(false);
Alert.alert('Error', error.message || 'Failed to save address'); setSubmitError(error.message || 'Failed to save address');
}, },
}); });
const updateAddressMutation = trpc.user.address.updateAddress.useMutation({ const updateAddressMutation = trpc.user.address.updateAddress.useMutation({
onSuccess: () => { onSuccess: () => {
setIsSubmitting(false); setIsSubmitting(false);
setTimeout(() => onSuccess(), 100); // Delay to allow modal to close animation
setTimeout(() => onSuccess(), 350);
}, },
onError: (error: any) => { onError: (error: any) => {
setIsSubmitting(false); setIsSubmitting(false);
Alert.alert('Error', error.message || 'Failed to update address'); setSubmitError(error.message || 'Failed to update address');
}, },
}); });
const attachCurrentLocation = async () => { const attachCurrentLocation = async () => {
setLocationLoading(true); setLocationLoading(true);
setLocationError(null); setLocationError(null);
setLocationSuccess(false);
try { try {
const { status } = await Location.requestForegroundPermissionsAsync(); const { status } = await Location.requestForegroundPermissionsAsync();
@ -78,7 +83,9 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
const { latitude, longitude } = location.coords; const { latitude, longitude } = location.coords;
setCurrentLocation({ latitude, longitude }); setCurrentLocation({ latitude, longitude });
Alert.alert('Success', 'Location attached successfully'); setLocationSuccess(true);
// Clear success message after 3 seconds
setTimeout(() => setLocationSuccess(false), 3000);
} catch (error) { } catch (error) {
setLocationError('Unable to fetch location. Please check your GPS settings.'); setLocationError('Unable to fetch location. Please check your GPS settings.');
} finally { } finally {
@ -98,8 +105,19 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
}); });
return ( return (
<ScrollView style={tw`p-4`}> <ScrollView
style={tw`p-4`}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"
>
<MyText style={tw`text-xl font-bold mb-4`}>{isEdit ? 'Edit Address' : 'Add Address'}</MyText> <MyText style={tw`text-xl font-bold mb-4`}>{isEdit ? 'Edit Address' : 'Add Address'}</MyText>
{/* Submit Error Message */}
{submitError && (
<View style={tw`bg-red-50 border border-red-200 rounded-lg p-3 mb-4`}>
<MyText style={tw`text-red-600 text-sm`}>{submitError}</MyText>
</View>
)}
<Formik <Formik
initialValues={initialValues || { initialValues={initialValues || {
name: '', name: '',
@ -116,9 +134,10 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
}} }}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={(values) => { onSubmit={(values) => {
setIsSubmitting(true); setIsSubmitting(true);
const payload = { setSubmitError(null);
...values, const payload = {
...values,
latitude: currentLocation?.latitude, latitude: currentLocation?.latitude,
longitude: currentLocation?.longitude, longitude: currentLocation?.longitude,
googleMapsUrl: values.googleMapsUrl || undefined, googleMapsUrl: values.googleMapsUrl || undefined,
@ -203,9 +222,24 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
<MyText style={tw`text-blue-500 text-sm mb-2`}>Fetching location...</MyText> <MyText style={tw`text-blue-500 text-sm mb-2`}>Fetching location...</MyText>
) : locationError ? ( ) : locationError ? (
<MyText style={tw`text-red-500 text-sm mb-2`}>{locationError}</MyText> <MyText style={tw`text-red-500 text-sm mb-2`}>{locationError}</MyText>
) : locationSuccess ? (
<View style={tw`flex-row items-center mb-4`}>
<View style={tw`bg-green-100 px-3 py-1 rounded-full`}>
<MyText style={tw`text-green-600 text-sm font-medium`}> Location Attached</MyText>
</View>
<MyTouchableOpacity
onPress={() => attachCurrentLocation()}
disabled={locationLoading}
style={tw`ml-4`}
>
<MyText style={tw`text-blue-500 text-sm font-medium`}>Attach Current</MyText>
</MyTouchableOpacity>
</View>
) : currentLocation ? ( ) : currentLocation ? (
<View style={tw`flex-row items-center mb-4`}> <View style={tw`flex-row items-center mb-4`}>
<MyText style={tw`text-green-600 text-sm font-medium`}>Location Attached</MyText> <View style={tw`bg-green-100 px-3 py-1 rounded-full`}>
<MyText style={tw`text-green-600 text-sm font-medium`}> Location Attached</MyText>
</View>
<MyTouchableOpacity <MyTouchableOpacity
onPress={() => attachCurrentLocation()} onPress={() => attachCurrentLocation()}
disabled={locationLoading} disabled={locationLoading}

View file

@ -1,5 +1,5 @@
import React, { ReactNode, useState } from 'react'; import React, { ReactNode, useState } from 'react';
import { Modal, View, TouchableOpacity, StyleSheet, Animated, Easing, Dimensions, TextInput } from 'react-native'; import { Modal, View, TouchableOpacity, StyleSheet, Animated, Easing, Dimensions, TextInput, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
import MyText from './text'; import MyText from './text';
import { MyButton } from 'common-ui'; import { MyButton } from 'common-ui';
@ -53,7 +53,20 @@ export const BottomDialog: React.FC<DialogProps> = ({ open, onClose, children, e
]} ]}
> >
<View style={styles.handle} /> <View style={styles.handle} />
{children} <KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.keyboardAvoidingContainer}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
>
<ScrollView
style={styles.scrollContainer}
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={true}
>
{children}
</ScrollView>
</KeyboardAvoidingView>
</Animated.View> </Animated.View>
</Modal> </Modal>
); );
@ -71,19 +84,18 @@ const styles = StyleSheet.create({
position: 'absolute', position: 'absolute',
left: 0, left: 0,
right: 0, right: 0,
// top: SCREEN_HEIGHT * 0.3,
bottom: 0, bottom: 0,
backgroundColor: '#fff', backgroundColor: '#fff',
borderTopLeftRadius: 18, borderTopLeftRadius: 18,
borderTopRightRadius: 18, borderTopRightRadius: 18,
paddingHorizontal: 20, paddingHorizontal: 20,
// paddingTop: 16,
paddingBottom: 0, paddingBottom: 0,
elevation: 8, elevation: 8,
shadowColor: '#000', shadowColor: '#000',
shadowOffset: { width: 0, height: -2 }, shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.2, shadowOpacity: 0.2,
shadowRadius: 8, shadowRadius: 8,
maxHeight: SCREEN_HEIGHT * 0.85, // Limit max height so it doesn't cover entire screen
}, },
handle: { handle: {
width: 40, width: 40,
@ -91,7 +103,20 @@ const styles = StyleSheet.create({
borderRadius: 3, borderRadius: 3,
backgroundColor: '#ccc', backgroundColor: '#ccc',
alignSelf: 'center', alignSelf: 'center',
// marginBottom: 12, marginTop: 12,
marginBottom: 8,
},
keyboardAvoidingContainer: {
flex: 1,
width: '100%',
},
scrollContainer: {
flex: 1,
width: '100%',
},
scrollContent: {
flexGrow: 1,
paddingBottom: Platform.OS === 'ios' ? 40 : 20, // Extra padding for iOS keyboard
}, },
}); });