Home > Software engineering >  How to create an onPress event that dismisses a callout?
How to create an onPress event that dismisses a callout?

Time:12-20

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:

enter image description here

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
  • Related