123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- import React, { useEffect, useRef, useState } from 'react';
- import {
- View,
- Text,
- TouchableOpacity,
- KeyboardAvoidingView,
- TouchableWithoutFeedback,
- Keyboard,
- ScrollView
- } from 'react-native';
- import { SafeAreaView } from 'react-native-safe-area-context';
- import MapView, { Marker } from 'react-native-maps';
- import ReactModal from 'react-native-modal';
- import axios from 'axios';
- import "react-native-get-random-values";
- import {
- GooglePlaceData,
- GooglePlaceDetail,
- GooglePlacesAutocomplete
- } from 'react-native-google-places-autocomplete';
- import { FlashList } from '@shopify/flash-list';
- import { Formik } from 'formik';
- import * as yup from 'yup';
- import { styles } from './styles';
- import { Header, Input, Button, WarningModal } from 'src/components';
- import { GOOGLE_MAP_PLACES_APIKEY } from 'src/constants';
- import { Colors } from 'src/theme';
- import {
- SubmitSuggestionTypes,
- useGetDataFromPoint,
- useGetSuggestionData,
- useSubmitSuggestionMutation
- } from '@api/series';
- import { StoreType, storage } from 'src/storage';
- import { ButtonVariants } from 'src/types/components';
- import SeriesSelector from './SeriesSelector';
- import SearchIcon from 'assets/icons/search.svg';
- interface Series {
- id: number;
- name: string;
- group_name: string | null;
- }
- const SuggestionSchema = yup.object({
- comment: yup.string().required('comment is required')
- });
- const SuggestSeriesScreen = ({ navigation }: { navigation: any }) => {
- const token = storage.get('token', StoreType.STRING) as string;
- const [isModalVisible, setIsModalVisible] = useState(false);
- const [seriesVisible, setSeriesVisible] = useState(false);
- const [marker, setMarker] = useState<any>(null);
- const [coordinates, setCoordinates] = useState<any>(null);
- const [groupedSeries, setGroupedSeries] = useState<any>(null);
- const [region, setRegion] = useState<any>({ nmRegion: null, dareRegion: null });
- const [selectedSeries, setSelectedSeries] = useState<Series | null>(null);
- const [submitedModalVisible, setSubmitedModalVisible] = useState(false);
- const [keyboardVisible, setKeyboardVisible] = useState(false);
- const { data: suggestionData } = useGetSuggestionData();
- const { data } = useGetDataFromPoint(
- token,
- coordinates?.lat,
- coordinates?.lng,
- coordinates ? true : false
- );
- const { mutateAsync: submitSuggestion } = useSubmitSuggestionMutation();
- const mapRef = useRef<MapView>(null);
- useEffect(() => {
- const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
- setKeyboardVisible(true);
- });
- const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
- setKeyboardVisible(false);
- });
- return () => {
- keyboardDidHideListener.remove();
- keyboardDidShowListener.remove();
- };
- }, []);
- useEffect(() => {
- if (data && data.result === 'OK') {
- const nmRegion = data.nm ? suggestionData?.nm.find((item) => item.id === data.nm.id) : null;
- const dareRegion = data.dare
- ? suggestionData?.dare.find((item) => item.id === data.dare.id)
- : null;
- setRegion({ nmRegion, dareRegion });
- setSelectedSeries(null);
- setIsModalVisible(true);
- }
- }, [data]);
- useEffect(() => {
- if (suggestionData && suggestionData.result === 'OK') {
- const groupedData = Object.keys(suggestionData.grouped).map((key) => ({
- title: key,
- data: suggestionData.grouped[key]
- }));
- setGroupedSeries(groupedData);
- }
- }, [suggestionData]);
- const findPlace = async (placeId: string) => {
- const response = await axios.get(
- `https://maps.googleapis.com/maps/api/place/details/json?place_id=${placeId}&key=${GOOGLE_MAP_PLACES_APIKEY}`
- );
- return { url: response.data.result.url, name: response.data.result.name };
- };
- const animateMapToRegion = (latitude: number, longitude: number) => {
- const region = {
- latitude,
- longitude,
- latitudeDelta: 0.015,
- longitudeDelta: 0.0121
- };
- mapRef.current?.animateToRegion(region, 500);
- };
- const handlePoiClick = async (event: any) => {
- const { placeId, coordinate } = event.nativeEvent;
- const { url, name } = await findPlace(placeId);
- setMarker({
- placeId,
- name,
- coordinate,
- url
- });
- setCoordinates({ lat: coordinate.latitude, lng: coordinate.longitude });
- animateMapToRegion(coordinate.latitude, coordinate.longitude);
- };
- const handlePlaceSelection = (data: GooglePlaceData, details: GooglePlaceDetail | null) => {
- if (details) {
- const { geometry } = details;
- setMarker({
- placeId: data.place_id,
- name: data.structured_formatting.main_text,
- coordinate: {
- latitude: geometry.location.lat,
- longitude: geometry.location.lng
- },
- url: details.url
- });
- setCoordinates({ lat: geometry.location.lat, lng: geometry.location.lng });
- animateMapToRegion(geometry.location.lat, geometry.location.lng);
- }
- };
- const renderGroup = ({ item }: { item: { title: string; data: Series[] } }) => {
- return (
- <View>
- {item.title !== '-' && <Text style={styles.groupTitle}>{item.title}</Text>}
- {item.data.map((series: Series) => (
- <TouchableOpacity
- key={series.id}
- style={styles.seriesItem}
- onPress={() => {
- setSelectedSeries(series);
- setSeriesVisible(false);
- }}
- >
- <Text style={styles.seriesText}>{series.name}</Text>
- </TouchableOpacity>
- ))}
- </View>
- );
- };
- const handleClose = () => {
- setSubmitedModalVisible(false);
- setIsModalVisible(false);
- navigation.goBack();
- };
- return (
- <SafeAreaView
- style={{
- height: '100%'
- }}
- >
- <View style={styles.wrapper}>
- <Header label={'Suggest new Series item'} />
- <View style={styles.searchContainer}>
- <GooglePlacesAutocomplete
- placeholder="Add a landmark"
- onPress={handlePlaceSelection}
- query={{
- key: GOOGLE_MAP_PLACES_APIKEY,
- language: 'en',
- types: 'establishment'
- }}
- nearbyPlacesAPI="GooglePlacesSearch"
- fetchDetails={true}
- styles={{
- textInput: styles.searchInput
- }}
- isRowScrollable={true}
- renderLeftButton={() => (
- <View style={styles.searchIcon}>
- <SearchIcon fill={Colors.LIGHT_GRAY} />
- </View>
- )}
- />
- </View>
- </View>
- <View style={styles.container}>
- <MapView
- ref={mapRef}
- style={styles.map}
- showsMyLocationButton={true}
- showsUserLocation={true}
- showsCompass={false}
- zoomControlEnabled={false}
- mapType={'standard'}
- maxZoomLevel={18}
- minZoomLevel={0}
- initialRegion={{
- latitude: 0,
- longitude: 0,
- latitudeDelta: 180,
- longitudeDelta: 180
- }}
- provider="google"
- onPoiClick={handlePoiClick}
- >
- {marker && (
- <Marker coordinate={marker.coordinate} onPress={() => setIsModalVisible(true)} />
- )}
- </MapView>
- </View>
- <ReactModal
- isVisible={isModalVisible}
- onBackdropPress={() => setIsModalVisible(false)}
- style={styles.modal}
- statusBarTranslucent={true}
- presentationStyle="overFullScreen"
- >
- <Formik
- initialValues={{
- comment: '',
- nm: region.nmRegion ? region.nmRegion.region_name : '-',
- dare: region.dareRegion ? region.dareRegion.name : '-',
- series: selectedSeries ? selectedSeries.id : null,
- name: marker?.name,
- link: marker?.url,
- lat: coordinates?.lat,
- lng: coordinates?.lng,
- item: -1
- }}
- validationSchema={SuggestionSchema}
- onSubmit={(values) => {
- const { comment, name, link, lat, lng, item } = values;
- if (!selectedSeries) return;
- const submitData: SubmitSuggestionTypes = {
- token,
- comment,
- name,
- link,
- lat,
- lng,
- item,
- nm: region.nmRegion.id,
- dare: region.dareRegion ? region.dareRegion.id : 0,
- series: selectedSeries.id
- };
- submitSuggestion(submitData, {
- onSuccess: () => {
- setSubmitedModalVisible(true);
- }
- });
- }}
- >
- {(props) => (
- <KeyboardAvoidingView
- behavior={'padding'}
- style={[styles.modalContent, { maxHeight: keyboardVisible ? undefined : '90%' }]}
- >
- <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
- <ScrollView
- contentContainerStyle={{ gap: 16 }}
- showsVerticalScrollIndicator={false}
- keyboardShouldPersistTaps="handled"
- >
- <Input
- placeholder="NM region"
- value={props.values.nm}
- editable={false}
- header="NM region"
- height={40}
- />
- <Input
- placeholder="DARE place"
- value={props.values.dare}
- editable={false}
- header="DARE place"
- height={40}
- />
- <SeriesSelector
- selectedSeries={selectedSeries}
- setSeriesVisible={setSeriesVisible}
- props={props}
- />
- <Input
- placeholder="Name"
- value={props.values.name}
- editable={false}
- header="Name"
- height={40}
- />
- <Input
- placeholder="URL"
- value={props.values.link}
- editable={false}
- header="Google maps link"
- height={40}
- />
- <Input
- multiline={true}
- header="Comment"
- value={props.values.comment}
- onChange={props.handleChange('comment')}
- formikError={props.touched.comment && props.errors.comment}
- />
- <View style={{ paddingBottom: 24, gap: 16 }}>
- <Button children="Send" onPress={props.handleSubmit} />
- <Button
- children="Close"
- onPress={() => setIsModalVisible(false)}
- variant={ButtonVariants.OPACITY}
- containerStyles={styles.closeBtn}
- textStyles={{ color: Colors.DARK_BLUE }}
- />
- </View>
- </ScrollView>
- </TouchableWithoutFeedback>
- </KeyboardAvoidingView>
- )}
- </Formik>
- <ReactModal
- isVisible={seriesVisible}
- onBackdropPress={() => setSeriesVisible(false)}
- style={styles.modal}
- statusBarTranslucent={true}
- presentationStyle="overFullScreen"
- >
- <View style={styles.modalWrapper}>
- <ScrollView
- style={{ paddingBottom: 16 }}
- showsVerticalScrollIndicator={false}
- nestedScrollEnabled={true}
- >
- {groupedSeries ? (
- <View style={{ minHeight: 100, paddingBottom: 16, paddingTop: 8 }}>
- <TouchableOpacity
- style={styles.seriesItem}
- onPress={() => {
- setSelectedSeries(null);
- setSeriesVisible(false);
- }}
- >
- <Text style={styles.seriesText}>Select series</Text>
- </TouchableOpacity>
- <FlashList
- viewabilityConfig={{
- waitForInteraction: true,
- itemVisiblePercentThreshold: 50,
- minimumViewTime: 1000
- }}
- estimatedItemSize={40}
- data={groupedSeries}
- renderItem={renderGroup}
- keyExtractor={(item) => item.title}
- nestedScrollEnabled={true}
- />
- </View>
- ) : null}
- </ScrollView>
- </View>
- </ReactModal>
- <WarningModal
- isVisible={submitedModalVisible}
- onClose={handleClose}
- type="success"
- message="Thank you for your suggestion!"
- title="Success!"
- />
- </ReactModal>
- </SafeAreaView>
- );
- };
- export default SuggestSeriesScreen;
|