after i have selected a place from suggestions and it’s fetched data is from firestore collection or maps api (based on the search input box), it shows the place details in selected venue details correctly but for some reason it’s causing screen to freeze when i again tap in the search venue input field to re-type the query after selected venue details are fetched. and i get this warning :
WARN Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code: {“3190”:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3193″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3196″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3199″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3202″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3205″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3208″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3211″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3214″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3217″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3220″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3223″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3226″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3229″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3232″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3235″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3238″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3241″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3244″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3247″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3250″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3253″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”3256″:{“module”:”UIManager”,”method”:”configureNextLayoutAnimation”},”…(truncated keys)…”:451}
import React, { useCallback, useState, useReducer, useEffect, useMemo, useContext } from "react";
import { View, Text, TextInput, Button, StyleSheet, TouchableOpacity, Modal, Platform, ScrollView, FlatList, Image, ActivityIndicator, Alert, KeyboardAvoidingView } from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
import Icon from 'react-native-vector-icons/Feather';
import { COLORS, FONTS, SIZES, images } from "../../constants";
import { useDispatch, useSelector } from 'react-redux';
// more imports
const CreateTour = ({ navigation, route }) => {
const { currentUser, userData, updateUserData } = useContext(AuthContext);
const { selectedTour, setSelectedTour, tours, setTourDetails, setTours } = useContext(TourContext);
console.log('Current User:', currentUser);
console.log('User Data:', userData);
const { tourData, initialStep } = route.params || {};
const [currentStep, setCurrentStep] = useState(initialStep || 1);
// more
const handleSubmit = async () => {
if (!db) return; // Return if db is not initialized yet
setIsSubmitting(true); // Set isSubmitting to true when the form submission starts
// Store selected tour dates in the 'tourDates' subcollection
const tourDatesRef = collection(tourRef, 'tourDates');
// Get the existing tour dates from Firestore
const existingTourDatesSnapshot = await getDocs(tourDatesRef);
const existingTourDates = existingTourDatesSnapshot.docs.map(doc => doc.data().date);
// Delete removed tour dates from Firestore
const removedDates = existingTourDates.filter(date => !selectedDates.some(selectedDate => selectedDate.toISOString().split('T')[0] === date));
for (const removedDate of removedDates) {
const removedDateDoc = existingTourDatesSnapshot.docs.find(doc => doc.data().date === removedDate);
if (removedDateDoc) {
await deleteDoc(removedDateDoc.ref);
}
}
for (let i = 0; i < selectedDates.length; i++) {
const date = selectedDates[i];
const dateString = date.toISOString().split('T')[0];
const dateId = `date${i + 1}`;
const dateRef = doc(tourDatesRef, dateId);
await setDoc(dateRef, {
date: dateString,
event: {
venueDetails: {
venueData: venueDetails[dateString] || {},
},
},
});
// Store event details for the particular date
const eventData = {
venueDetails: {
venueData: {
id: venueDetails[dateString]?.id || '',
name: venueDetails[dateString]?.name || '',
displayName: venueDetails[dateString]?.displayName || '',
formattedAddress: venueDetails[dateString]?.formattedAddress || '',
placeCoverPhotoUrl: venueDetails[dateString]?.placeCoverPhotoUrl || '',
shortFormattedAddress: venueDetails[dateString]?.shortFormattedAddress || '',
adrFormatAddress: venueDetails[dateString]?.adrFormatAddress || '',
addressComponents: venueDetails[dateString]?.addressComponents || '',
location: venueDetails[dateString]?.location || '',
plusCode: venueDetails[dateString]?.plusCode || '',
types: venueDetails[dateString]?.types || [],
viewport: venueDetails[dateString]?.viewport || null,
accessibilityOptions: venueDetails[dateString]?.accessibilityOptions || {},
businessStatus: venueDetails[dateString]?.businessStatus || '',
googleMapsUri: venueDetails[dateString]?.googleMapsUri || '',
iconBackgroundColor: venueDetails[dateString]?.iconBackgroundColor || '',
iconMaskBaseUri: venueDetails[dateString]?.iconMaskBaseUri || '',
primaryType: venueDetails[dateString]?.primaryType || '',
primaryTypeDisplayName: venueDetails[dateString]?.primaryTypeDisplayName || '',
subDestinations: venueDetails[dateString]?.subDestinations || '',
utcOffsetMinutes: venueDetails[dateString]?.utcOffsetMinutes || '',
currentOpeningHours: venueDetails[dateString]?.currentOpeningHours || '',
currentSecondaryOpeningHours: venueDetails[dateString]?.currentSecondaryOpeningHours || '',
internationalPhoneNumber: venueDetails[dateString]?.internationalPhoneNumber || '',
nationalPhoneNumber: venueDetails[dateString]?.nationalPhoneNumber || '',
priceLevel: venueDetails[dateString]?.priceLevel || '',
rating: venueDetails[dateString]?.rating || '',
regularOpeningHours: venueDetails[dateString]?.regularOpeningHours || '',
regularSecondaryOpeningHours: venueDetails[dateString]?.regularSecondaryOpeningHours || '',
userRatingCount: venueDetails[dateString]?.userRatingCount || '',
websiteUri: venueDetails[dateString]?.websiteUri || '',
},
},
};
await setDoc(dateRef, { date: dateString, event: eventData }, { merge: true });
// Update the tours array in the TourContext
setTours(prevTours => {
const updatedTours = prevTours.map(tour => tour.id === updatedTourData.id ? updatedTourData : tour);
return updatedTours;
});
// Navigate back to the Dates screen
navigation.navigate('TourManagerDashboard', {
screen: 'Dates',
params: { updatedTour: updatedTourData },
});
} else {
console.error('Updated tour document not found');
}
} catch (error) {
console.error('Error submitting form:', error);
}
setIsSubmitting(false);
};
const renderDateTabs = () => {
return (
<View style={styles.dateTabsContainer}>
{selectedDates.map((date, index) => (
<TouchableOpacity
key={index}
style={[styles.dateTab, selectedDateIndex === index && styles.selectedDateTab]}
onPress={() => setSelectedDateIndex(index)}
>
<Text style={styles.dateTabDay}>{getDayOfWeek(date)}</Text>
<Text style={styles.dateTabText}>{getDateText(date)}</Text>
</TouchableOpacity>
))}
</View>
);
};
const fetchSuggestions = async (query) => {
try {
console.log('Fetching suggestions for query:', query);
console.log('Session token:', sessionToken);
// Default latitude and longitude for India
let latitude = 20.593684; // Latitude of India
let longitude = 78.96288; // Longitude of India
// Try to get the user's current location
if (navigator.geolocation) {
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
latitude = position.coords.latitude;
longitude = position.coords.longitude;
}
const response = await fetch('https://places.googleapis.com/v1/places:autocomplete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Goog-Api-Key': 'key',
},
body: JSON.stringify({
input: query,
locationBias: {
circle: {
center: {
latitude: latitude,
longitude: longitude,
},
radius: 5000,
},
},
sessionToken: sessionToken,
}),
});
const data = await response.json();
console.log('Autocomplete response:', data);
if (data.suggestions && data.suggestions.length > 0) {
setSuggestions(data.suggestions);
console.log('Suggestions set:', data.suggestions);
} else {
setSuggestions([]);
console.log('No suggestions found');
}
} catch (error) {
console.error('Error fetching suggestions:', error);
Alert.alert('Error', 'Failed to fetch suggestions.');
}
};
const handleSearchVenue = useCallback(
debounce((text) => {
setSearchQuery(text);
console.log('Search query changed:', text);
if (text.length > 0) {
fetchSuggestions(text);
} else {
setSuggestions([]);
console.log('Search query is empty, clearing suggestions');
}
}, 1500, { leading: false, trailing: true }),
[]
);
useEffect(() => {
const generateSessionToken = () => {
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
setSessionToken(uuid);
console.log('Session token generated:', uuid);
};
if (searchQuery) {
generateSessionToken();
} else {
setSessionToken('');
}
return () => {
setSessionToken('');
};
}, [searchQuery]);
const handleVenueSearch = async () => {
if (!db) return; // Return if db is not initialized yet
try {
const placesRef = collection(db, 'Places');
const venueQuery = query(
placesRef,
where('displayName', '>=', venueSearchQuery),
where('displayName', '<=', venueSearchQuery + 'uf8ff'),
limit(5)
);
const venueSnapshot = await getDocs(venueQuery);
if (venueSnapshot.empty) {
setVenueSearchResults([]);
} else {
const venueData = venueSnapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}));
setVenueSearchResults(venueData);
}
} catch (error) {
console.error('Error searching venues:', error);
}
};
const renderDateFields = () => {
const selectedDate = selectedDates[selectedDateIndex];
if (!selectedDate) {
return null; // Return null or a placeholder component when selectedDate is undefined
}
const dateString = selectedDate.toISOString().split('T')[0];
const handleVenueDetailsChange = (field, value) => {
setVenueDetails(prevDetails => ({
...prevDetails,
[dateString]: {
...prevDetails[dateString],
[field]: value,
},
}));
};
const handleSelectFirestoreSuggestion = async (venue) => {
setVenueSearchQuery(venue.displayName);
setVenueSearchResults([]);
try {
// Update the venue details with the fetched data
setVenueDetails((prevDetails) => ({
...prevDetails,
[dateString]: {
id: venue.id || '',
name: venue.name || '',
displayName: venue.displayName || '',
formattedAddress: venue.formattedAddress || '',
shortFormattedAddress: venue.shortFormattedAddress || '',
adrFormatAddress: venue.adrFormatAddress || '',
location: venue.location || null,
plusCode: venue.plusCode || '',
types: venue.types || [],
viewport: venue.viewport || null,
accessibilityOptions: venue.accessibilityOptions || {},
businessStatus: venue.businessStatus || '',
googleMapsUri: venue.googleMapsUri || '',
iconBackgroundColor: venue.iconBackgroundColor || '',
iconMaskBaseUri: venue.iconMaskBaseUri || '',
primaryType: venue.primaryType || '',
primaryTypeDisplayName: venue.primaryTypeDisplayName || '',
subDestinations: venue.subDestinations ? venue.subDestinations.map(destination => destination.name).join(', ') : '',
utcOffsetMinutes: venue.utcOffsetMinutes || '',
currentOpeningHours: venue.currentOpeningHours || '',
currentSecondaryOpeningHours: venue.currentSecondaryOpeningHours || '',
internationalPhoneNumber: venue.internationalPhoneNumber || '',
nationalPhoneNumber: venue.nationalPhoneNumber || '',
priceLevel: venue.priceLevel || '',
rating: venue.rating || '',
regularOpeningHours: venue.regularOpeningHours || '',
regularSecondaryOpeningHours: venue.regularSecondaryOpeningHours || '',
userRatingCount: venue.userRatingCount || '',
websiteUri: venue.websiteUri || '',
placeCoverPhotoUrl: venue.placeCoverPhotoUrl || '',
},
}));
} catch (error) {
console.error('Error fetching venue details:', error);
Alert.alert('Error', 'Failed to fetch venue details.');
}
};
const handleSelectSuggestion = async (suggestion) => {
setSearchQuery(suggestion.placePrediction.text.text);
setSuggestions([]);
try {
const response = await fetch(
`https://places.googleapis.com/v1/places/${suggestion.placePrediction.placeId}?sessionToken=${sessionToken}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Goog-Api-Key': 'key',
'X-Goog-FieldMask':
'id,name,photos,addressComponents,adrFormatAddress,formattedAddress,location,plusCode,shortFormattedAddress,types,viewport,accessibilityOptions,businessStatus,displayName,googleMapsUri,iconBackgroundColor,iconMaskBaseUri,primaryType,primaryTypeDisplayName,subDestinations,utcOffsetMinutes,currentOpeningHours,currentSecondaryOpeningHours,internationalPhoneNumber,nationalPhoneNumber,priceLevel,rating,regularOpeningHours,regularSecondaryOpeningHours,userRatingCount,websiteUri',
},
}
);
const data = await response.json();
console.log('Place Details:', data);
if (data.error) {
console.error('Error fetching place details:', data.error);
Alert.alert('Error', 'Failed to fetch place details.');
return;
}
const fetchPhotoUrl = async (placeId, photoName) => {
const apiKey = 'key'; // Replace with your actual API key
const maxWidth = 400; // Specify your desired photo width
const maxHeight = 400; // Specify your desired photo height
try {
console.log('Fetching photo URL for photoName:', photoName);
const requestUrl = `https://places.googleapis.com/v1/${photoName}/media?maxWidthPx=400&maxHeightPx=400&key=${apiKey}`;
console.log('Request URL:', requestUrl);
const response = await fetch(requestUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
console.log('Response status:', response.status);
console.log('Response headers:', response.headers);
if (response.ok) {
const contentType = response.headers.get('Content-Type');
console.log('Content-Type:', contentType);
if (contentType.includes('application/json')) {
const data = await response.json();
console.log('Response data:', data);
if (data.error) {
console.error('API error:', data.error);
return '';
}
return data.photoUri || '';
} else {
console.log('Response URL:', response.url);
return response.url;
}
} else {
console.error('Error fetching photo:', response.status);
const errorData = await response.json();
console.error('Error data:', errorData);
return '';
}
} catch (error) {
console.error('Error fetching photo:', error);
return '';
}
};
const photoName = data.photos && data.photos.length > 0 ? data.photos[0].name : '';
console.log('Extracted photoName:', photoName);
const placeCoverPhotoUrl = photoName ? await fetchPhotoUrl(data.id, photoName) : '';
// Update the venue details with the fetched data
setVenueDetails((prevDetails) => ({
...prevDetails,
[dateString]: {
id: data.id || '',
name: data.name || '',
displayName: data.displayName?.text || '',
formattedAddress: data.formattedAddress || '',
shortFormattedAddress: data.shortFormattedAddress || '',
adrFormatAddress: data.adrFormatAddress || '',
location: data.location ? { latitude: data.location.latitude, longitude: data.location.longitude } : null,
plusCode: data.plusCode?.globalCode || '',
types: data.types || [],
viewport: data.viewport
? {
northeast: {
latitude: data.viewport.high?.latitude || '',
longitude: data.viewport.high?.longitude || '',
},
southwest: {
latitude: data.viewport.low?.latitude || '',
longitude: data.viewport.low?.longitude || '',
},
}
: null,
accessibilityOptions: data.accessibilityOptions || {},
businessStatus: data.businessStatus || '',
googleMapsUri: data.googleMapsUri || '',
iconBackgroundColor: data.iconBackgroundColor || '',
iconMaskBaseUri: data.iconMaskBaseUri || '',
primaryType: data.primaryType || '',
primaryTypeDisplayName: data.primaryTypeDisplayName?.text || '',
subDestinations: data.subDestinations ? data.subDestinations.map(destination => destination.name).join(', ') : '',
utcOffsetMinutes: data.utcOffsetMinutes || '',
currentOpeningHours: data.currentOpeningHours?.periods
? data.currentOpeningHours.periods.map(
(period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}`
).join(', ')
: '',
currentSecondaryOpeningHours: data.currentSecondaryOpeningHours?.periods
? data.currentSecondaryOpeningHours.periods.map(
(period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}`
).join(', ')
: '',
internationalPhoneNumber: data.internationalPhoneNumber || '',
nationalPhoneNumber: data.nationalPhoneNumber || '',
priceLevel: data.priceLevel || '',
rating: data.rating || '',
regularOpeningHours: data.regularOpeningHours?.periods
? data.regularOpeningHours.periods.map(
(period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}`
).join(', ')
: '',
regularSecondaryOpeningHours: data.regularSecondaryOpeningHours?.periods
? data.regularSecondaryOpeningHours.periods.map(
(period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}`
).join(', ')
: '',
userRatingCount: data.userRatingCount || '',
websiteUri: data.websiteUri || '',
placeCoverPhotoUrl: placeCoverPhotoUrl || '',
},
}));
} catch (error) {
console.error('Error fetching place details:', error);
Alert.alert('Error', 'Failed to fetch place details.');
}
};
return (
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.container}>
<View key={dateString}>
<ScrollView contentContainerStyle={styles.scrollViewContent}>
<View style={styles.venueSearchContainer}>
<TextInput
style={styles.venueSearchInput}
placeholder="Search venue in database"
value={venueSearchQuery}
onChangeText={setVenueSearchQuery}
/>
<TouchableOpacity style={styles.venueSearchButton} onPress={handleVenueSearch}>
<Text style={styles.venueSearchButtonText}>Search</Text>
</TouchableOpacity>
</View>
{venueSearchResults.length > 0 ? (
<View style={styles.venueSearchResultsContainer}>
{venueSearchResults.map(venue => (
<TouchableOpacity
key={venue.id}
style={styles.venueSearchResult}
onPress={() => handleSelectFirestoreSuggestion(venue)}
>
<Text>{venue.displayName}</Text>
</TouchableOpacity>
))}
</View>
) : venueSearchQuery ? (
<Text style={styles.venueSearchMessage}>Couldn't find venue in our database.</Text>
) : null}
<Text style={styles.stepInputTitle}>Search Venue</Text>
<TextInput
style={styles.input}
placeholder="Search venue"
value={searchQuery}
onChangeText={handleSearchVenue}
/>
<ScrollView style={styles.suggestionsContainer}>
{suggestions.map((suggestion, index) => (
<TouchableOpacity
key={index}
style={styles.suggestionItem}
onPress={() => handleSelectSuggestion(suggestion)}
>
<Text>{suggestion.placePrediction.text.text}</Text>
</TouchableOpacity>
))}
</ScrollView>
<View key={dateString}>
<Text style={styles.stepInputTitle}>Selected Venue Details</Text>
{venueDetails[dateString] && (
<>
<View style={styles.venueDetailsContainer}>
<View style={styles.venueImageContainer}>
{venueDetails[dateString]?.placeCoverPhotoUrl ? (
<Image
source={{ uri: venueDetails[dateString]?.placeCoverPhotoUrl }}
style={styles.venueImage}
/>
) : (
<View style={styles.venueImagePlaceholder} />
)}
</View>
<View style={styles.venueTextContainer}>
<Text style={styles.venueDisplayName}>{venueDetails[dateString]?.displayName}</Text>
<Text style={styles.venueAddress}>{venueDetails[dateString]?.formattedAddress}</Text>
</View>
</View>
{venueDetails[dateString]?.location && (
<View style={styles.venueMapContainer}>
<WebView
style={styles.venueMap}
originWhitelist={['*']}
source={{
html: `
<iframe
width="100%"
height="100%"
frameborder="0" style="border:0"
referrerpolicy="no-referrer-when-downgrade"
src="https://www.google.com/maps/embed/v1/place?key=key&q=${encodeURIComponent(venueDetails[dateString]?.displayName)}&zoom=20&maptype=satellite&language=en"
maximum-zoom="21"
allowfullscreen>
</iframe>
`,
}}
/>
</View>
)}
</>
)}
</View>
</ScrollView>
</View>
</KeyboardAvoidingView>
);
};
return (
<View style={styles.container}>
<Text style={styles.title}>Create Tour</Text>
<View style={styles.stepsContainer}>
{[1, 2, 3, 4, 5].map((step) => (
<View key={step} style={[styles.step, currentStep === step && styles.activeStep]} />
))}
</View>
<ScrollView contentContainerStyle={styles.scrollView}>
<View style={styles.formContent}>
{renderFormStep()}
</View>
</ScrollView>
<View style={styles.navigation}>
{currentStep > 1 && (
<TouchableOpacity style={styles.navButton} onPress={handlePrevStep}>
<Text style={styles.navButtonText}>Previous</Text>
</TouchableOpacity>
)}
{currentStep < 5 ? (
<TouchableOpacity style={styles.navButton} onPress={handleNextStep}>
<Text style={styles.navButtonText}>Next</Text>
</TouchableOpacity>
) : (
<TouchableOpacity style={styles.navButton} onPress={handleSubmit}>
<Text style={styles.navButtonText}>Submit</Text>
</TouchableOpacity>
)}
</View>
{isSubmitting && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#E2384D" />
<Text style={styles.loadingText}>Submitting...</Text>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
paddingHorizontal: 20,
width: '100%',
},
// more style code
export default CreateTour;
I have tried usiing useCallback, usememo, debounce nothing is working. can some one please fix this isuue.