src_components_Map.tsx.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title> src/components/Map.tsx</title>
  7. <script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
  8. <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  9. <script src="./build/entry.js"></script>
  10. <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  11. <!--[if lt IE 9]>
  12. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  13. <![endif]-->
  14. <link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
  15. <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
  16. <link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
  17. <link type="text/css" rel="stylesheet" href="styles/app.min.css">
  18. <link type="text/css" rel="stylesheet" href="styles/iframe.css">
  19. <link type="text/css" rel="stylesheet" href="">
  20. <script async defer src="https://buttons.github.io/buttons.js"></script>
  21. </head>
  22. <body class="layout small-header">
  23. <div id="stickyNavbarOverlay"></div>
  24. <div class="top-nav">
  25. <div class="inner">
  26. <a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
  27. <span aria-hidden="true"></span>
  28. <span aria-hidden="true"></span>
  29. <span aria-hidden="true"></span>
  30. </a>
  31. <div class="logo">
  32. </div>
  33. <div class="menu">
  34. <div class="navigation">
  35. <a
  36. href="index.html"
  37. class="link"
  38. >
  39. API Documentation
  40. </a>
  41. </div>
  42. </div>
  43. </div>
  44. </div>
  45. <div id="main">
  46. <div
  47. class="sidebar "
  48. id="sidebarNav"
  49. >
  50. <nav>
  51. <h2><a href="index.html">Documentation</a></h2><div class="category"><h3>Interfaces</h3><ul><li><a href="AddLandmarkProps.html">AddLandmarkProps</a></li><li><a href="CommentProps.html">CommentProps</a></li><li><a href="IdToken.html">IdToken</a></li><li><a href="IntroProps.html">IntroProps</a></li><li><a href="Landmark.html">Landmark</a></li><li><a href="LandmarkDetailsProps.html">LandmarkDetailsProps</a></li><li><a href="LandmarkPinProps.html">LandmarkPinProps</a></li><li><a href="LMComment.html">LMComment</a></li><li><a href="ProfileProps.html">ProfileProps</a></li><li><a href="RegisterProps.html">RegisterProps</a></li><li><a href="UserProfile.html">UserProfile</a></li></ul><h3>Components</h3><ul><li><a href="App.html">App</a></li><li><a href="Atlas.html">Atlas</a></li><li><a href="Comment.html">Comment</a></li><li><a href="PrimaryButton.html">PrimaryButton</a></li><li><a href="Profile.html">Profile</a></li><li><a href="SecondaryButton.html">SecondaryButton</a></li></ul><h3>Global</h3><ul><li><a href="global.html#IconStrings">IconStrings</a></li></ul></div><div class="category"><h2>Hooks</h2><h3>Namespaces</h3><ul><li><a href="useAuth.html">useAuth</a></li><li><a href="useLandmarks.html">useLandmarks</a></li><li><a href="useProfile.html">useProfile</a></li></ul></div><div class="category"><h2>Map</h2><h3>Interfaces</h3><ul><li><a href="UserLocation.html">UserLocation</a></li></ul><h3>Components</h3><ul><li><a href="AddLandmark.html">AddLandmark</a></li><li><a href="LandmarkDetails.html">LandmarkDetails</a></li><li><a href="LandmarkPin.html">LandmarkPin</a></li><li><a href="Map.html">Map</a></li></ul></div><div class="category"><h2>Navigation</h2><h3>Components</h3><ul><li><a href="AuthorizedNavigator.html">AuthorizedNavigator</a></li><li><a href="UnauthorizedNavigator.html">UnauthorizedNavigator</a></li></ul><h3><a href="global.html">Global</a></h3></div><div class="category"><h2>Stores</h2><h3>Classes</h3><ul><li><a href="AuthStore.html">AuthStore</a></li></ul></div><div class="category"><h2>Unauthorized</h2><h3>Components</h3><ul><li><a href="Intro.html">Intro</a></li><li><a href="UnauthorizedLayout.html">UnauthorizedLayout</a></li></ul><h3>Components / Registration</h3><ul><li><a href="RegisterMain.html">RegisterMain</a></li></ul></div>
  52. </nav>
  53. </div>
  54. <div class="core" id="main-content-wrapper">
  55. <div class="content">
  56. <header class="page-title">
  57. <p>Source</p>
  58. <h1>src/components/Map.tsx</h1>
  59. </header>
  60. <section>
  61. <article>
  62. <pre class="prettyprint source linenums"><code>import { FontAwesome } from "@expo/vector-icons";
  63. import * as Location from 'expo-location';
  64. import React, { useEffect, useState } from "react";
  65. import { Keyboard, KeyboardAvoidingView, TouchableOpacity, View } from "react-native";
  66. import MapView, { LatLng, Region } from "react-native-maps";
  67. import Modal from "react-native-modal";
  68. import { colors } from "../globals";
  69. import { Landmark, useLandmarks } from "../hooks/useLandmarks";
  70. import AddLandmark from "./AddLandmark";
  71. import LandmarkDetails from "./LandmarkDetails";
  72. import LandmarkPin from "./LandmarkPin";
  73. /**
  74. * An interface representing the user location retrieved from [expo-location]{@link https://docs.expo.dev/versions/latest/sdk/location/}.
  75. * @category Map
  76. */
  77. export interface UserLocation {
  78. latitude: number;
  79. longitude: number;
  80. heading?: number;
  81. }
  82. const directionsUrl = "https://maps.googleapis.com/maps/api/directions/son?mode=walking&amp;alternatives=true&amp;key=AIzaSyD06YUMazlb4Fu0Q81y_YNyEBz8PmtZyeY";
  83. /**
  84. * The screen component containing the [react-native-maps]{@link https://github.com/react-native-maps/react-native-maps} Map and all related functionality.
  85. * @category Map
  86. * @component
  87. */
  88. export const Map: React.FC = () => {
  89. /**
  90. * State that contains the new {@link Landmark} object which is passed down to the {@link AddLandmark} modal.
  91. */
  92. const newLandmarkState: Landmark = {};
  93. const [newLandmark, setNewLandmark] = useState&lt;Landmark>(newLandmarkState);
  94. /**
  95. * State that contains the selected {@link Landmark} object which is passed down to the {@link LandmarkDetails} modal.
  96. */
  97. const selectedLandmarkState: Landmark = {};
  98. const [selectedLandmark, setSelectedLandmark] = useState&lt;Landmark>(selectedLandmarkState);
  99. /**
  100. * Holds the visibility state of the {@link AddLandmark} modal.
  101. */
  102. const lmAddVisibleState = false;
  103. const [lmAddVisible, toggleLmAdd] = useState&lt;boolean>(false);
  104. /**
  105. * Holds the visibility state of the {@link LandmarkDetails} modal.
  106. */
  107. const lmDetailsVisibleState = false;
  108. const [lmDetailsVisible, toggleLmDetails] = useState&lt;boolean>(false);
  109. /**
  110. * Flag that toggles whether or not editing is enabled in the {@link LandmarkDetails} modal.
  111. * The parent Map component has access to it so that it can disable closing the modal on backdrop press when it is enabled.
  112. */
  113. const lmDetailsEditingState = false;
  114. const [lmDetailsEditing, toggleLmDetailsEditing] = useState&lt;boolean>(false);
  115. /**
  116. * State that holds a {@link UserLocation} object retrieved from location services.
  117. */
  118. const userLocationState: UserLocation | undefined = undefined;
  119. const [userLocation, setUserLocation] = useState&lt;UserLocation>(userLocationState);
  120. /**
  121. * Flag that determines whether the map should focus and follow the user's location.
  122. */
  123. const followUserState = false;
  124. const [followUser, toggleFollowUser] = useState&lt;boolean>(followUserState);
  125. //const [prevFetchedBounds, setFetchedBounds] = useState&lt;Region>();
  126. const { landmarks, getLandmarksStatus, refetchLandmarks } = useLandmarks(undefined);
  127. /**
  128. * Ref that holds the loaded [MapView]{@link https://github.com/react-native-maps/react-native-maps/blob/master/docs/mapview.md} instance.
  129. */
  130. const mapRef = React.createRef&lt;MapView>();
  131. useEffect(() => {
  132. /**
  133. * Prompts user to give permission to track their location using [expo-location]{@link https://docs.expo.dev/versions/latest/sdk/location/}.
  134. * If permission is granted, user location will be retrieved and stored in {@linkcode userLocationState}.
  135. * @memberOf Map
  136. */
  137. const requestLocationPermissions = async () => {
  138. let { status } = await Location.requestForegroundPermissionsAsync();
  139. if (status !== 'granted') {
  140. return;
  141. }
  142. const location = await Location.getCurrentPositionAsync();
  143. setUserLocation({latitude: location.coords.latitude, longitude: location.coords.longitude})
  144. // setFetchedBounds({
  145. // latitude: location.coords.latitude,
  146. // longitude: location.coords.longitude,
  147. // latitudeDelta: 0.01,
  148. // longitudeDelta: 0.01
  149. // });
  150. };
  151. requestLocationPermissions();
  152. }, []);
  153. /**
  154. * Triggered by long pressing on the map.
  155. * Sets {@linkcode newLandmarkState} to a skeleton {@link Landmark} object that only contains the pressed coordinates.
  156. * Triggers {@link openAddLandmark} via useEffect because the asyncronous nature of useState does not set the coordinates fast enough to toggle the modal directly through this method.
  157. */
  158. const promptAddLandmark = (coordinate: LatLng) => {
  159. setNewLandmark({latitude: coordinate.latitude, longitude: coordinate.longitude});
  160. }
  161. useEffect(() => {
  162. /**
  163. * Opens the {@link AddLandmark} modal when the user creates {@link newLandmarkState} by longpressing the map.
  164. * Embedded in a [useEffect]{@link https://reactjs.org/docs/hooks-effect.html} hook that listens to {@linkcode newLandmarkState}.
  165. * @memberOf Map
  166. */
  167. function openAddLandmark() {
  168. if (newLandmark) {
  169. toggleLmAdd(true)
  170. toggleLmDetails(false)
  171. console.log(newLandmark)
  172. }
  173. }
  174. openAddLandmark();
  175. }, [newLandmark]);
  176. // useEffect(() => {
  177. // if (prevFetchedBounds) {
  178. // refetchLandmarks();
  179. // }
  180. // }, [prevFetchedBounds]);
  181. /**
  182. * Triggered by on a {@link Landmark} displayed on the map.
  183. * Sets {@linkcode selectedLandmark} to the pressed {@link Landmark} object's value and toggles the {@link LandmarkDetails} modal.
  184. */
  185. const focusLandmark = (landmark: Landmark) => {
  186. setSelectedLandmark(landmark);
  187. toggleLmDetails(true)
  188. }
  189. /**
  190. * Animates the map to fly over to and focus on the user's location.
  191. */
  192. const flyToUser = () => {
  193. if (userLocation) {
  194. mapRef.current?.animateToRegion({latitude: userLocation.latitude, longitude: userLocation.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01})
  195. }
  196. }
  197. // const checkBounds = (bounds: Region) => {
  198. // if (prevFetchedBounds) {
  199. // if (
  200. // bounds.latitude &lt; prevFetchedBounds.latitude - prevFetchedBounds.latitudeDelta || // check if new lat exceeds old left bounds
  201. // bounds.latitude > prevFetchedBounds.latitude + prevFetchedBounds.latitudeDelta || // check if new lat exceeds old right bounds
  202. // bounds.longitude &lt; prevFetchedBounds.longitude - prevFetchedBounds.longitudeDelta || // check if new lat exceeds bottom bounds
  203. // bounds.longitude > prevFetchedBounds.longitude + prevFetchedBounds.longitudeDelta ||
  204. // bounds.latitudeDelta &lt; prevFetchedBounds.latitudeDelta / 2) // check if user zoomed in to atleast half scale of previous
  205. // {
  206. // console.log('new bounds')
  207. // setFetchedBounds(bounds);
  208. // }
  209. // }
  210. // else if (bounds.latitudeDelta &lt; 5) {
  211. // console.log('initialize bounds')
  212. // setFetchedBounds(bounds);
  213. // }
  214. // }
  215. return (
  216. &lt;View>
  217. &lt;MapView
  218. testID="mapView"
  219. ref={mapRef}
  220. style={{width: "100%", height: "100%"}}
  221. initialRegion={userLocation ? {latitude: userLocation.latitude, longitude: userLocation.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01} : undefined}
  222. onLongPress={e => promptAddLandmark(e.nativeEvent.coordinate)}
  223. // onRegionChangeComplete={bounds => checkBounds(bounds)}
  224. showsUserLocation={true}
  225. onUserLocationChange={e => setUserLocation(e.nativeEvent.coordinate)}
  226. followsUserLocation={followUser}>
  227. {landmarks?.map((landmark) => {
  228. return (
  229. &lt;LandmarkPin key={landmark.id} landmark={landmark} focusLandmark={focusLandmark}/>)})}
  230. &lt;/MapView>
  231. &lt;TouchableOpacity style={{position: 'absolute', bottom: 30, right: 30, backgroundColor: colors.red, height: 60, width: 60, borderRadius: 30, justifyContent: "center", alignItems: 'center'}} onPress={flyToUser}>
  232. &lt;FontAwesome name="location-arrow" size={20} color="white"/>
  233. &lt;/TouchableOpacity>
  234. {/* &lt;TouchableOpacity style={{position: 'absolute', bottom: 120, right: 30, backgroundColor: colors.red, height: 60, width: 60, borderRadius: 30, justifyContent: "center", alignItems: 'center'}}>
  235. &lt;FontAwesome name="" size={20} color="white"/>
  236. &lt;/TouchableOpacity> */}
  237. &lt;Modal
  238. testID="addLMModal"
  239. avoidKeyboard={true}
  240. onBackdropPress={() => toggleLmAdd(false)}
  241. style={{justifyContent: "flex-end", height: '100%', margin: 0}}
  242. isVisible={lmAddVisible} >
  243. &lt;KeyboardAvoidingView>
  244. &lt;AddLandmark setVisible={toggleLmAdd} landmark={newLandmark} />
  245. &lt;/KeyboardAvoidingView>
  246. &lt;/Modal>
  247. &lt;Modal
  248. avoidKeyboard={true}
  249. onBackdropPress={() => {
  250. if (lmDetailsEditing) {
  251. Keyboard.dismiss();
  252. } else {
  253. toggleLmDetails(false)
  254. }
  255. }}
  256. style={{justifyContent: "flex-end", height: '100%', margin: 0}}
  257. isVisible={lmDetailsVisible}>
  258. &lt;LandmarkDetails setVisible={toggleLmDetails} setEditing={toggleLmDetailsEditing} editingEnabled={lmDetailsEditing} landmark={selectedLandmark} />
  259. &lt;/Modal>
  260. &lt;/View> )
  261. }</code></pre>
  262. </article>
  263. </section>
  264. </div>
  265. <footer class="footer">
  266. <div class="content has-text-centered">
  267. <p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a></p>
  268. <p class="sidebar-created-by">
  269. <a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
  270. <a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
  271. </p>
  272. </div>
  273. </footer>
  274. </div>
  275. <div id="side-nav" class="side-nav">
  276. </div>
  277. </div>
  278. <script src="scripts/app.min.js"></script>
  279. <script>PR.prettyPrint();</script>
  280. <script src="scripts/linenumber.js"> </script>
  281. </body>
  282. </html>