I'm building a gig guide in React Native, using react native maps.
Users are presented with a Map with markers indicating the location of gig. When these markers are tapped, a callout pops up with gig information:
When the user taps the "Next day's gigs" button, the map is rendered with markers showing the upcoming day's gigs. When the user hits this button, I want to make sure that any open callouts on the current day are dismissed when the button is hit. Any suggestions on how to do this?
Here's the code from the component rendering the map:
GigMap.js
import { useState, useMemo } from "react";
import {
StyleSheet,
Text,
View,
Pressable,
Image,
TouchableOpacity,
} from "react-native";
import MapView from "react-native-maps";
import { Marker, Callout } from "react-native-maps";
import CalloutView from "./CalloutView";
import { mapStyle } from "../util/mapStyle";
import { useGigs } from "../hooks/useGigs";
import { AntDesign } from "@expo/vector-icons";
const GigMap = ({ navigation }) => {
const [selectedDateMs, setSelectedDateMs] = useState(Date.now());
const gigs = useGigs();
//generates current date in format DD/MM/YYYY
const selectedDateString = useMemo(() => {
const date = new Date(selectedDateMs);
const dateToString = date.toString().slice(0,15)
return dateToString // returns in form 'Tue Dec 20 2022'
}, [selectedDateMs]);
//Filtering through gigs to return only current day's gigs
const gigsToday = gigs.filter((gig) => {
const gigDate1 = new Date(gig.dateAndTime.seconds*1000)
const gigDate2 = gigDate1.toString().slice(0,15) //return form 'Tue Dec 20 2022'
return gigDate2 === selectedDateString
})
//increments date by amount
const addDays = (amount) => {
setSelectedDateMs((curr) => curr 1000 * 60 * 60 * 24 * amount);
};
return (
<View style={styles.container}>
<Text style={styles.headerText}>{`Gigs on ${selectedDateString}`}</Text>
<View style={styles.imageText}>
<Text style = {styles.subHeader}>Tap on</Text>
<Image
style={styles.image}
source={require("../assets/Icon_Gold_48x48.png")}
/>
<Text style = {styles.subHeader}> to see gig info</Text>
</View>
<MapView
initialRegion={{
latitude: -41.29416,
longitude: 174.77782,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}
style={styles.map}
customMapStyle={mapStyle}
>
{gigsToday.map((gig, i) => (
<Marker
key={i}
coordinate={{
latitude: gig.location.latitude,
longitude: gig.location.longitude,
}}
image={require("../assets/Icon_Gold_48x48.png")}
description = 'test'
>
<Callout
style={styles.callout}
tooltip={true}
onPress={() =>
navigation.navigate("GigDetails", {
venue: gig.venue,
date: selectedDateString,
gigName: gig.gigName,
image: gig.image
})
}
>
<CalloutView
venue={gig.venue}
gigName={gig.gigName}
genre = {gig.genre}
/>
</Callout>
</Marker>
))}
</MapView>
<View style={styles.buttonOptions}>
<TouchableOpacity onPress={() => addDays(-1)} style = {styles.touchable}>
<AntDesign name="caretleft" size={36} color="#778899" />
<Text style = {{fontFamily:'Helvetica-Neue', color:'#778899'}}>Previous day's gigs</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => addDays(1)} style = {styles.touchable}>
<AntDesign name="caretright" size={36} color="#778899" />
<Text style = {{fontFamily:'Helvetica-Neue',color:'#778899'}}>Next day's gigs</Text>
</TouchableOpacity>
</View>
</View>
);
};
CalloutView.js
import { StyleSheet, Text, View } from 'react-native';
const CalloutView = ({ venue,gigName,genre }) => {
const title = gigName.substring(0,25)
return (
<View style = {styles.container}>
<Text style = {styles.header} >{`${title}`}</Text>
<Text style = {styles.details}>{`${venue} | ${genre}`}</Text>
<Text style = {styles.button}>Tap to see details</Text>
</View>
);
}
CodePudding user response:
Since you are already using a custom hook (useGigs
), I would suggest that you handle the whole logic inside this hook.
However, in essence we only need to update todaysGigs
whenever, the state for the current selected date changes.
export function useGigs() {
// however your hook is implemented
...
const [currentDate, setCurrentDate] = useState(Date.now())
const incrementCurrentDate = React.useCallback(() => {
setCurrentDate((curr) => curr 1000 * 60 * 60 * 24 * 1);
}, [setCurrentDate])
const decrementCurrentDate = React.useCallback(() => {
setCurrentDate((curr) => curr 1000 * 60 * 60 * 24 * (-1));
}, [setCurrentDate])
const selectedDateString = React.useMemo(() => {
const date = new Date(selectedDateMs);
const dateToString = date.toString().slice(0,15)
return dateToString // returns in form 'Tue Dec 20 2022'
}, [currentDate]);
const gigsToday = React.useMemo(() => {
return gigs.filter((gig) => {
const gigDate1 = new Date(gig.dateAndTime.seconds*1000)
const gigDate2 = gigDate1.toString().slice(0,15) //return form 'Tue Dec 20 2022'
return gigDate2 === selectedDateString
}, [currentDate])
return {
gigsToday,
gigs,
incrementCurrentDate,
decrementCurrentDate,
selectedDateString,
}
}
The UI logic.
const GigMap = ({ navigation }) => {
const {selectedDateString, incrementCurrentDate, decrementCurrentDate, gigsToday} = useGigs();
return (
<View style={styles.container}>
<Text style={styles.headerText}>{`Gigs on ${selectedDateString}`}</Text>
<View style={styles.imageText}>
<Text style = {styles.subHeader}>Tap on</Text>
<Image
style={styles.image}
source={require("../assets/Icon_Gold_48x48.png")}
/>
<Text style = {styles.subHeader}> to see gig info</Text>
</View>
<MapView
initialRegion={{
latitude: -41.29416,
longitude: 174.77782,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}
style={styles.map}
customMapStyle={mapStyle}
>
{gigsToday.map((gig, i) => (
<Marker
key={i}
coordinate={{
latitude: gig.location.latitude,
longitude: gig.location.longitude,
}}
image={require("../assets/Icon_Gold_48x48.png")}
description = 'test'
>
<Callout
style={styles.callout}
tooltip={true}
onPress={() =>
navigation.navigate("GigDetails", {
venue: gig.venue,
date: selectedDateString,
gigName: gig.gigName,
image: gig.image
})
}
>
<CalloutView
venue={gig.venue}
gigName={gig.gigName}
genre = {gig.genre}
/>
</Callout>
</Marker>
))}
</MapView>
<View style={styles.buttonOptions}>
<TouchableOpacity onPress={() => decrementCurrentDate()} style = {styles.touchable}>
<AntDesign name="caretleft" size={36} color="#778899" />
<Text style = {{fontFamily:'Helvetica-Neue', color:'#778899'}}>Previous day's gigs</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => incrementCurrentDate()} style = {styles.touchable}>
<AntDesign name="caretright" size={36} color="#778899" />
<Text style = {{fontFamily:'Helvetica-Neue',color:'#778899'}}>Next day's gigs</Text>
</TouchableOpacity>
</View>
</View>
);
};
CodePudding user response:
Try this
import React from "react";
import { IMAGES } from "theme";
import { View, Text, StyleSheet } from "react-native";
import MapView, { PROVIDER_GOOGLE, Marker, Callout } from "react-native-maps";
const GigMap = () => {
return (
<MapView
provider={PROVIDER_GOOGLE} // remove if not using Google Maps
style={styles.map}
region={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.015,
longitudeDelta: 0.0121,
}}
>
<Marker
coordinate={{
latitude: 37.78825,
longitude: -122.4324,
}}
image={IMAGES.MAP_MARKER}
title="Test Title"
description="This is the test description"
>
<Callout tooltip>
<View>
<View style={styles.bubble}>
<Text numberOfLines={1} style={styles.name}>Gig Title</Text>
<Text>A Gig description</Text>
</View>
<View style={styles.arrowBorder} />
<View style={styles.arrow} />
</View>
</Callout>
</Marker>
</MapView>
);
};
export default GigMap;
const styles = StyleSheet.create({
map: {
height: "100%",
},
// Callout bubble
bubble: {
flexDirection: "column",
alignSelf: "flex-start",
backgroundColor: "#fff",
borderRadius: 6,
borderColor: "#ccc",
borderWidth: 0.5,
padding: 15,
width: 150,
},
// Arrow below the bubble
arrow: {
backgroundColor: "transparent",
borderColor: "transparent",
borderTopColor: "#fff",
borderWidth: 16,
alignSelf: "center",
marginTop: -32,
},
arrowBorder: {
backgroundColor: "transparent",
borderColor: "transparent",
borderTopColor: "#007a87",
borderWidth: 16,
alignSelf: "center",
marginTop: -0.5,
},
// Gig Title
name: {
fontSize: 16,
marginBottom: 5,
},
// Gig button
button: {
width: "40%",
height: 80,
},
});
Here is some of the Suggestions for you
- use numberOfLines for title
- use Dayjs instead of manually doing date.toString().slice(0,15) also it will be solve your all dates and time related problems