mmazzarolo / react-native-modal-datetime-picker

A React-Native datetime-picker for Android and iOS
MIT License
2.96k stars 397 forks source link

App freezes on ios ocassionaly #702

Closed NKANGIJAFARI closed 1 year ago

NKANGIJAFARI commented 1 year ago

Environment

Platforms

IOS only

Versions

"expo": "^48.0.0", "@react-native-community/datetimepicker": "6.7.3", "react-native-modal-datetime-picker": "^15.0.0",

Description

App ocassionaly freezes on IOS. Sometimes it can let me use it for 4 bookings, meaning i pick the dates 4 times and then freeze, sometimes by the time the component renders, it freezes imediately. Its unpredictable when it will freezea and when it will work.

Reproducible Demo

import React, { useState } from 'react';
import { View, StyleSheet, Text, Pressable } from 'react-native';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import { Colors, Fonts } from '../../utils/constant/styles';

const DateTimePickerComp = ({ serviceType, pickupDate, pickupTime, handleDate, handleTime }) => {
  const [isDatePickerVisible, setDatePickerVisibility] = useState(false);

  const [pickerMode, setPickerMode] = useState('');

  const showDatePicker = () => {
    setTimeout(() => setDatePickerVisibility(true), 200);
  };

  const hideDatePicker = () => {
    setTimeout(() => setDatePickerVisibility(false), 100);
  };

  const handleConfirm = (date) => {
    if (pickerMode === 'date') {
      handleDate({ value: date, error: '' });
    } else {
      handleTime({ value: date, error: '' });
    }
    hideDatePicker();
  };

  return (
    <>
      <DateTimePickerModal
        isVisible={isDatePickerVisible}
        mode={pickerMode}
        onConfirm={handleConfirm}
        onCancel={hideDatePicker}
        onHide={() => hideDatePicker}
        date={new Date()}
      />

      <View style={styles.bookingFormRow}>
        <View style={[styles.combineInput]}>
          <View style={styles.dateOrTimePressable}>
            <Text style={styles.labelStyle}>
              {serviceType === 'pick' ? 'Pickup Date' : 'Drop Off Date'}
            </Text>
            <Pressable
              onPressIn={() => {
                setPickerMode('date');
                showDatePicker();
              }}
              style={[
                styles.inputContainerStyle,
                {
                  borderColor: pickupDate?.error !== '' ? Colors.RED : Colors.darkGray,
                  borderWidth: pickupDate?.error !== '' ? 2 : 1,
                },
              ]}
            >
              <View>
                <Text>{pickupDate?.value === '' ? '  YYYY-MM-DD' : pickupDate?.value}</Text>
              </View>
            </Pressable>

            <View>
              {pickupDate?.error !== '' && (
                <Text style={styles.errorText}>{pickupDate?.error}</Text>
              )}
            </View>
          </View>

          <View style={styles.dateOrTimePressable}>
            <Text style={styles.labelStyle}>
              {serviceType === 'pick' ? 'Pickup Time' : 'Drop Off Time'}
            </Text>

            <Pressable
              Pressable
              onPressIn={() => {
                setPickerMode('time');
                showDatePicker();
              }}
              style={[
                styles.inputContainerStyle,
                {
                  borderColor: pickupTime?.error !== '' ? Colors.RED : Colors.darkGray,
                  borderWidth: pickupTime?.error !== '' ? 1 : 1,
                },
              ]}
            >
              <View>
                <Text>{pickupTime?.value === '' ? '  hr : min' : pickupTime?.value}</Text>
              </View>
            </Pressable>

            <View>
              {pickupDate?.error !== '' && (
                <Text style={styles.errorText}>{pickupTime?.error}</Text>
              )}
            </View>
          </View>
        </View>
      </View>
    </>
  );
};

export default DateTimePickerComp;
const styles = StyleSheet.create({
  bookingFormRow: {
    width: '100%',
    marginTop: 20,
  },
  labelStyle: {
    ...Fonts.grayRegular,
    fontSize: 12,
    position: 'absolute',
    top: '-20%',
    left: 10,
    backgroundColor: 'white',
    paddingHorizontal: 3,
    zIndex: 10,
  },

  inputContainerStyle: {
    backgroundColor: 'white',
    borderRadius: 10,
    borderWidth: 1,
    borderColor: 'grey',
    height: 45,
    flexDirection: 'row',
    alignItems: 'center',
    width: '100%',
    paddingHorizontal: 15,
  },

  errorText: { color: Colors.RED, fontSize: 12 },

  combineInput: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%',
  },

  dateOrTimePressable: { width: '47%' },

  label: { color: Colors.BLACK, ...Fonts.black12Regular },

  row: { backgroundColor: 'red' },
});
import React, { useState, useRef } from 'react';
import {
  StyleSheet,
  View,
  TouchableOpacity,
  Text,
  ScrollView,
  ActionSheetIOS,
  Platform,
  TextInput,
} from 'react-native';
import { Feather, MaterialCommunityIcons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { useDispatch, useSelector } from 'react-redux';
import { Picker } from '@react-native-picker/picker';
import moment from 'moment';
// eslint-disable-next-line import/no-unresolved
import { Fonts, Colors, Sizes } from '../../utils/constant/styles';
import {
  setAirPortCoords,
  setDropOffPickUpValue,
  setIsModalVisible,
} from '../../utils/Redux/Slices/PickUPDropOffCoords';
import { setAirPortPickUpDropOffBookingDetails } from '../../utils/Redux/Slices/AirPortPickUpDropOffBookingDetailsSlice';
import DateTimePickerComp from '../../components/BookingForms/PickupFormField';

const isAndroid = Platform.OS === 'android';

const AIRPORTS = [
  {
    code: 'DXB',
    label: 'Dubai International Airport',
    value: 'Dubai International Airport',
    coords: { latitude: 25.253502221991734, longitude: 55.36569170277418 },
  },
  {
    code: 'AUH',
    label: 'Abu Dhabi International Airport',
    value: 'Abu Dhabi International Airport',
    coords: { latitude: 24.442094255368357, longitude: 54.650095054153475 },
  },

  {
    code: 'AAN',
    label: 'Al Ain International Airport',
    value: 'Al Ain International Airport',
    coords: { latitude: 24.26273511864855, longitude: 55.618968067413086 },
  },
  {
    code: 'AZI',
    label: 'Al Bateen Executive Airport',
    value: 'Al Bateen Executive Airport',
    coords: { latitude: 24.427698341314166, longitude: 54.45187292716937 },
  },

  {
    code: 'FJA',
    label: 'Fujairah International Airport',
    value: 'Fujairah International Airport',
    coords: { latitude: 25.111682598598456, longitude: 56.33064760357063 },
  },
  {
    code: 'RAK',
    label: 'Ras Al Khaimah Airport',
    value: 'Ras Al Khaimah Airport',
    coords: { latitude: 25.612213219380344, longitude: 55.93997109651468 },
  },
  {
    code: 'SHJ',
    label: 'Sharjah International Airport',
    value: 'Sharjah International Airport',
    coords: { latitude: 25.328619429541455, longitude: 55.512268425342526 },
  },
];

const AirportPickDropForm = ({ serviceType }) => {
  const [pickupDate, setPickupDate] = useState({ value: moment().format('YYYY-MM-DD'), error: '' });
  const [pickupTime, setPickupTime] = useState({ value: moment().format('hh:mm A'), error: '' });
  const [pickupAirport, setPickupAirport] = useState({ value: '', error: '' });
  const [numberOfAdults, setNumberOfAdults] = useState({ value: 1, error: '' });
  const [numberOfChildren, setNumberOfChildren] = useState({ value: 0, error: '' });
  const [childSeatRequired, setChildSeatRequired] = useState(false);

  const { airPortPickUpDropOffBookingDetails } = useSelector(
    (state) => state.airPortPickUpDropOffBookingDetailsState
  );

  const { dropOffPickUpValue } = useSelector((state) => state.dropOffPickUpCoordsState);

  const pickUpRef = useRef(null);

  const navigation = useNavigation();
  const dispatch = useDispatch();

  const handleSubmitForm = () => {
    const pickupAirportError = pickupAirport?.value === '' ? 'Please select pickup airport' : '';
    const pickupDateError = pickupDate?.value === '' ? 'Please select pickup date' : '';
    const pickupTimeError = pickupTime?.value === '' ? 'Please select pickup time' : '';
    const dropOffLocationError =
      Object.values(dropOffPickUpValue).length === 0 ? 'Please select drop off location' : '';
    const numberOfAdultsError = numberOfAdults?.value === 0 ? 'Please select number of adults' : '';

    if (
      pickupAirportError ||
      pickupDateError ||
      pickupTimeError ||
      numberOfAdultsError ||
      dropOffLocationError
    ) {
      setPickupAirport({ ...pickupAirport, error: pickupAirportError });
      dispatch(
        setDropOffPickUpValue({
          value: dropOffPickUpValue?.value || {},
          error: dropOffLocationError,
        })
      );
      setPickupDate({ ...pickupDate, error: pickupDateError });
      setPickupTime({ ...pickupTime, error: pickupTimeError });
      setNumberOfAdults({ ...numberOfAdults, error: numberOfAdultsError });
      return;
    }

    let bookingDetails;
    const { distanceInKm } = airPortPickUpDropOffBookingDetails;

    if (serviceType === 'pick') {
      bookingDetails = {
        isCarPerHour: false,
        pickupLocation: AIRPORTS.find((airportVal) => airportVal.value === pickupAirport.value),
        pickupDate: pickupDate.value,
        pickupTime: pickupTime.value,
        duration: '',
        noOfAdults: numberOfAdults.value,
        childSeatRequired: childSeatRequired ? 'Yes' : 'No',
        noOfChildren: numberOfChildren.value,
        dropOffLocation: dropOffPickUpValue.value,
        distanceInKm,
      };
    }
    if (serviceType === 'drop') {
      bookingDetails = {
        isCarPerHour: false,
        pickupLocation: dropOffPickUpValue.value,
        dropOffDate: pickupDate.value,
        dropOffTime: pickupTime.value,
        pickupTime: pickupTime.value,
        pickupDate: pickupDate.value,
        duration: '',
        noOfAdults: numberOfAdults.value,
        childSeatRequired: childSeatRequired ? 'Yes' : 'No',
        noOfChildren: numberOfChildren.value,
        dropOffLocation: AIRPORTS.find((airportVal) => airportVal.value === pickupAirport.value),
        distanceInKm,
      };
    }

    dispatch(setAirPortPickUpDropOffBookingDetails(bookingDetails));
    // console.log('bookingDetails', bookingDetails);

    const _DISPLAY_PRICE_TYPE = serviceType === 'drop' ? 'drop' : 'pick';
    navigation.navigate('SelectCar', { type: _DISPLAY_PRICE_TYPE });
    // navigation.navigate(
  };

  const handleTime = (date) => {
    setPickupTime({ value: moment(date).format('hh:mm A'), error: '' });
  };

  const handleDate = (date) => {
    setPickupDate({ value: moment(date).format('YYYY-MM-DD'), error: '' });
  };

  const PickupFormField = () => (
    <View style={[styles.bookingFormRow]}>
      <Text style={styles.labelStyle}>
        {serviceType === 'pick' ? 'Pickup Airport' : 'Drop Off Airport'}
      </Text>

      {isAndroid ? (
        <>
          <TouchableOpacity
            style={[
              styles.inputContainerStyle,
              {
                borderColor: pickupAirport.error !== '' ? Colors.RED : Colors.darkGray,
                borderWidth: pickupAirport.error !== '' ? 1 : 1,
              },
            ]}
            onPress={() => pickUpRef.current.focus()}
          >
            <Text>{pickupAirport.value || 'Pick Up Airport'}</Text>
          </TouchableOpacity>

          <Picker
            style={{ width: 0, backgroundColor: 'red', height: 0 }}
            selectedValue={pickupAirport.value}
            ref={pickUpRef}
            mode="dialog"
            onValueChange={(itemValue) => {
              const airport = AIRPORTS.find((airportVal) => airportVal.value === itemValue);
              dispatch(setAirPortCoords(airport.coords));
              setPickupAirport({ value: itemValue, error: '' });
            }}
          >
            {AIRPORTS.map((airport, index) => (
              // eslint-disable-next-line react/no-array-index-key
              <Picker.Item key={index} label={`${airport.label}`} value={`${airport.value}`} />
            ))}
          </Picker>
        </>
      ) : (
        // </View>
        <TouchableOpacity
          style={[
            styles.inputContainerStyle,
            {
              borderColor: pickupAirport.error !== '' ? Colors.RED : Colors.darkGray,
              borderWidth: pickupAirport.error !== '' ? 1 : 1,
            },
          ]}
          onPress={() =>
            ActionSheetIOS.showActionSheetWithOptions(
              {
                options: ['Cancel', ...Array.from(AIRPORTS, (airport) => airport.label)],
                // destructiveButtonIndex: ,
                cancelButtonIndex: 0,
                userInterfaceStyle: 'light',
              },
              (buttonIndex) => {
                if (buttonIndex !== 0) {
                  dispatch(setAirPortCoords(AIRPORTS[buttonIndex - 1].coords));
                  setPickupAirport({ value: AIRPORTS[buttonIndex - 1].label, error: '' });
                }
              }
            )
          }
        >
          <Text>{pickupAirport?.value}</Text>
        </TouchableOpacity>
      )}

      {/* {pickupAirport.error && <Text style={styles.errorText}>{pickupAirport.error}</Text>} */}
    </View>
  );

  const DropOffFormField = () => (
    <View style={styles.bookingFormRow}>
      <Text style={styles.labelStyle}>
        {serviceType === 'pick' ? 'Drop Off Location' : 'Pick Up Location'}
      </Text>

      <TouchableOpacity
        style={[
          styles.inputContainerStyle,
          {
            borderColor: !pickupAirport.error ? Colors.darkGray : Colors.RED,
            borderWidth: !pickupAirport.error ? 1 : 1,
          },
        ]}
        placeholder="Search Drop Off Location"
        label="Drop Off Location"
        onPressIn={() => dispatch(setIsModalVisible(true))}
      >
        <Text numberOfLines={2}>
          {dropOffPickUpValue?.value?.address ||
            `search ${serviceType === 'pick' ? 'Drop Off ' : 'Pick Up '}  Location ...`}{' '}
        </Text>
      </TouchableOpacity>
    </View>
  );

  const ChildSeatFormField = () => (
    <View style={styles.bookingFormRow}>
      <TouchableOpacity
        style={styles.inputContainerStyle}
        onPress={() => {
          setNumberOfChildren({ value: 0, error: '' });
          setChildSeatRequired(!childSeatRequired);
        }}
      >
        {childSeatRequired ? (
          <Feather name="check-square" size={24} color="black" />
        ) : (
          <MaterialCommunityIcons name="checkbox-blank-outline" size={27} color="black" />
        )}
        <Text> Select if you require a child seat </Text>
      </TouchableOpacity>
    </View>
  );

  const NumberOfAdultsFormField = () => (
    <View
      style={[
        styles.bookingFormRow,
        { width: childSeatRequired ? '48%' : '100%', alignSelf: 'flex-start' },
      ]}
    >
      <Text style={styles.labelStyle}>No. of Adults</Text>
      <TextInput
        style={[
          styles.inputContainerStyle,
          {
            borderColor: numberOfAdults.error !== '' ? Colors.RED : Colors.darkGray,
            borderWidth: numberOfAdults.error !== '' ? 1 : 1,
          },
        ]}
        keyboardType="numeric"
        value={numberOfAdults.value.toString()}
        onChangeText={(text) => setNumberOfAdults({ value: text, error: '' })}
      />
    </View>
  );

  const NumberOfChildren = () => (
    <View style={[styles.bookingFormRow, { alignSelf: 'flex-end', width: '48%' }]}>
      <Text style={styles.labelStyle}>No. of Children</Text>

      <TextInput
        style={[
          styles.inputContainerStyle,
          {
            borderColor: pickupTime.error !== '' ? Colors.RED : Colors.darkGray,
            borderWidth: pickupTime.error !== '' ? 1 : 1,
          },
        ]}
        placeholder="No. Of Children"
        label="No. Of Children"
        keyboardType="numeric"
        value={numberOfChildren.value.toString()}
        onChangeText={(value) => setNumberOfChildren({ value, error: '' })}
      />
    </View>
  );

  return (
    <>
      <ScrollView style={styles.scrollViewStyle} showsVerticalScrollIndicator={false}>
        <DropOffFormField />

        <PickupFormField />

        <DateTimePickerComp
          serviceType={serviceType}
          pickupDate={pickupDate}
          pickupTime={pickupTime}
          handleDate={handleDate}
          handleTime={handleTime}
        />

        <ChildSeatFormField />

        <View
          style={{
            flexDirection: 'row',
            justifyContent: 'space-between',
            marginBottom: '40%',
          }}
        >
          <NumberOfAdultsFormField />
          {childSeatRequired ? <NumberOfChildren /> : null}
        </View>
      </ScrollView>

      <View style={styles.buttonContainer}>
        <TouchableOpacity onPress={() => handleSubmitForm()}>
          <View style={styles.payButtonStyle}>
            <Text style={{ ...Fonts.white20Bold }}>Continue</Text>
          </View>
        </TouchableOpacity>
      </View>
    </>
  );
};

const styles = StyleSheet.create({
  payButtonStyle: {
    backgroundColor: Colors.primary,
    paddingVertical: Sizes.fixPadding + 3.0,
    width: '100%',
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: Sizes.fixPadding + 5.0,
  },
  contentContainer: { flexGrow: 1 },

  scrollViewStyle: { flex: 1, paddingHorizontal: 0, paddingVertical: 10, paddingTop: 10 },

  bookingFormRow: {
    width: '100%',
    marginTop: 20,
  },

  labelStyle: {
    ...Fonts.grayRegular,
    fontSize: 12,
    position: 'absolute',
    top: '-20%',
    left: 10,
    backgroundColor: 'white',
    paddingHorizontal: 3,
    zIndex: 10,
  },

  inputContainerStyle: {
    backgroundColor: 'white',
    borderRadius: 10,
    borderWidth: 1,
    borderColor: 'grey',
    height: 45,
    flexDirection: 'row',
    alignItems: 'center',
    width: '100%',
    paddingHorizontal: 15,
  },

  buttonContainer: {
    height: 75,

    backgroundColor: 'white',

    position: 'absolute',
    bottom: isAndroid ? 0 : 8.0,
    width: '100%',

    justifyContent: 'center',
    borderTopColor: Colors.lightGray,
    borderTopWidth: 0.8,
    left: 10,
  },

  buttonStyle: {
    height: 50,
    width: '85%',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: Colors.BLACK,
    borderRadius: 6,
    bottom: 0,
  },

  buttonTitleStyle: { ...Fonts.white17Bold, paddingHorizontal: 10 },

  errorContainer: { marginLeft: 10 },

  errorText: { color: Colors.RED, fontSize: 12 },

  iconStyle: { alignSelf: 'center', paddingRight: 5 },

  textInputContainer: {},

  textInput: { height: 45, color: '#5d5d5d', fontSize: 16, borderWidth: 1, borderRadius: 8 },

  combineInput: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%',
  },

  dateOrTimePressable: { width: '47%' },

  label: { color: Colors.BLACK, ...Fonts.black12Regular },

  separator: { height: 0.5 },

  listView: {
    paddingTop: 5,
    backgroundColor: 'white',
    borderWidth: 0.5,
    borderColor: '#ddd',
    borderRadius: 5,
    elevation: 5,
    // height: 300,
  },

  row: { backgroundColor: 'red' },

  poweredContainer: {
    alignItems: 'center',
    justifyContent: 'center',
    borderTopWidth: 0.5,
    borderColor: '#ddd',
  },

  powered: { paddingVertical: 15 },
});

export default AirportPickDropForm;
mmazzarolo commented 1 year ago

Please check the issue template before submitting new issues:

Let us know how to reproduce the issue. Include a code sample or share a project that reproduces the issue. Follow the guidelines for providing a minimal example: https://stackoverflow.com/help/mcve Please notice that WE WON'T SUPPORT ISSUE REPORTS THAT DON'T HAVE A MINIMAL REPRODUCIBLE EXAMPLE.

dumitru-buda commented 4 months ago

wondering why was this completed