Преглед на файлове

added support for routing

Eric Li преди 2 години
родител
ревизия
026536f191

BIN
assets/childfriendly.png


+ 1 - 0
package.json

@@ -83,6 +83,7 @@
     "react-native-gesture-handler": "~1.10.2",
     "react-native-get-random-values": "~1.7.0",
     "react-native-maps": "0.29.3",
+    "react-native-maps-directions": "^1.8.0",
     "react-native-material-menu": "^2.0.0",
     "react-native-modal": "^12.0.3",
     "react-native-multi-selectbox": "^1.5.0",

+ 5 - 3
src/components/Map/MainMapComponent/IndoorMap.tsx

@@ -70,7 +70,7 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
               await promptAddLandmark((evt.nativeEvent.locationX - imageDim / 2) / SVGdim[0], (evt.nativeEvent.locationY - imageDim / 2) / SVGdim[1], floor)
             }
             catch (err) {
-              Toast.show("An error has occured. Please ensure finger is not moving when holding down on screen.", { duration: Toast.durations.LONG, })
+              Toast.show("Please ensure finger is not moving when holding down on screen.", { duration: Toast.durations.LONG, })
 
               // Alert.alert("An error has occured." , "Please ensure thumb is not moving when holding down on screen.")
               // consider toast
@@ -192,7 +192,6 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
               panBoundaryPadding={100}
               // bindToBorders={false}
               bindToBorders={true}
-
               zoomStep={2.8}
               // initialZoom={2.2}
               maxZoom={2.8}
@@ -201,7 +200,10 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
               onLongPress={(event) => {
                 // serialize()
                 addLandmark(event)
-              }}>
+              }}
+              movementSensibility={3}
+              longPressDuration={200}
+              >
 
               <Svg onLayout={event => {
                 console.log("OFFICIAL: " + event.nativeEvent.layout.width + " , " + event.nativeEvent.layout.height)

+ 185 - 163
src/components/Map/MainMapComponent/OutdoorMap.tsx

@@ -10,9 +10,9 @@ import { RouteProp, useNavigationState } from "@react-navigation/native";
 import { booleanPointInPolygon, circle } from '@turf/turf';
 import * as Notifications from 'expo-notifications';
 import { observer } from "mobx-react";
-import React, { useEffect, useRef } from "react";
+import React, { useEffect, useRef, useState } from "react";
 import { AppState, Image, Keyboard, Modal, Platform, TouchableOpacity, TouchableWithoutFeedback, Text, View, ActivityIndicator } from "react-native";
-import MapView, { LatLng, Marker, Polygon, Region } from "react-native-maps";
+import MapView, { LatLng, Marker, Polygon, Region, Polyline } from "react-native-maps";
 import { PERMISSIONS } from "react-native-permissions";
 import Spokestack from 'react-native-spokestack';
 import { Landmark } from '../../../data/landmarks';
@@ -27,6 +27,9 @@ import { VoicePanel } from "../Panels/VoicePanel";
 import mapStyles from "./Map.styles";
 import { useOutdoorMapState } from "./useMapState";
 
+import MapViewDirections from 'react-native-maps-directions';
+
+
 /**
  * An interface representing the user location retrieved from [expo-location]{@link https://docs.expo.dev/versions/latest/sdk/location/}.
  * @category Map
@@ -48,8 +51,8 @@ export type AuthTabsMapRouteProp = RouteProp<MainTabsParamList, 'Map'>;
  */
 
 interface OutdoorMapProps {
-    mapNavigation: MapStackNavigationProp, 
-    authNavigation: MainTabsNavigationProp, 
+    mapNavigation: MapStackNavigationProp,
+    authNavigation: MainTabsNavigationProp,
     authNavIndex: number,
     route: AuthTabsMapRouteProp,
     focusLandmark: (landmark: Landmark) => void,
@@ -69,11 +72,16 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
 
     const mapNavIndex = useNavigationState(state => state)
 
+    const [coordinates] = useState([
+        { latitude: 53.527086340019856, longitude: -113.52358410971608, }, // Cameron library
+        { latitude: 53.52516024715472, longitude: -113.52154139033108, }, // University station
+    ]);
+
     /**
      * If the ReactNavigation route prop changes, check if it contains incoming selected landmarks, display them if there are. This will be triggered by incoming notifcations 
      * (See the AuthorizedNavigator page for the useEffect that will trigger this)
      */
-     useEffect(() => {
+    useEffect(() => {
         if (props.route?.params?.selectedLandmark) {
             props.setSelectedLandmarkId(props.route?.params?.selectedLandmark)
         }
@@ -96,34 +104,34 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
     /**
      * Toggle the lm details panel when a new selected landmark is detected (triggered by pressing on a map marker, or from the list of nearby landmarks)
      */
-     useEffect(() => {
+    useEffect(() => {
         console.log("[LandmarkDetails]: Landmark selected - " + props.selectedLandmarkId)
         if (props.selectedLandmarkId) {
             const landmark = props.landmarks.find(lm => lm.id == props.selectedLandmarkId)
-            mapState.mapRef.current.animateToRegion({latitude: landmark.latitude, longitude: landmark.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01})
+            mapState.mapRef.current.animateToRegion({ latitude: landmark.latitude, longitude: landmark.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01 })
             props.toggleLmDetails(true)
         }
-        
+
     }, [props.selectedLandmarkId])
 
     /**
      * Move to pressed location when newlandmark changes
      */
-     useEffect(() => {
+    useEffect(() => {
         if (props.selectedLandmarkId) {
-            mapState.mapRef.current.animateToRegion({latitude: props.newLandmark?.latitude, longitude: props.newLandmark?.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01})
+            mapState.mapRef.current.animateToRegion({ latitude: props.newLandmark?.latitude, longitude: props.newLandmark?.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01 })
         }
     }, [props.newLandmark])
 
     /**
      * Gets speech permissions from user, runs every time app is brought to foreground
      */
-     useEffect(() => {
+    useEffect(() => {
         const getSpeechPermissions = async () => {
             if (AppState.currentState == 'active') {
                 await getMapPermissions()
                 console.log('[Permissions]: Checking voice permissions...')
-                if (Platform.OS == 'android') {        
+                if (Platform.OS == 'android') {
                     const permitted = await checkVoicePermissions([PERMISSIONS.ANDROID.RECORD_AUDIO])
                     mapState.toggleVoicePermission(permitted)
                     if (permitted) console.log('[Permissions]: Voice permission granted')
@@ -135,86 +143,86 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
                     if (permitted) console.log('[Permissions]: Voice permission granted')
                     else console.log('[Permissions]: Voice permission denied')
                 }
-            }   
+            }
         }
         getSpeechPermissions()
-        }, [AppState.currentState])
-    
-        /**
-         * Gets foreground location permissions from user, runs every time app is brought to foreground
-         */
-         useEffect(() => {
-            const checkForegroundLocationPermissions = async () =>  {
-                if (AppState.currentState == 'active') {
-                    console.log('[Permissions]: Checking location permissions...')
-                    if (Platform.OS == 'android') {        
-                        const permitted = await checkVoicePermissions([PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION, PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION])
-                        mapState.toggleFgroundLocationPermission(permitted)
-                        if (permitted) console.log('[Permissions]: Location permission granted')
-                        else console.log('[Permissions]: Location permission denied')
-                    }
-                    else if (Platform.OS == 'ios') {
-                        const permitted = await checkVoicePermissions([PERMISSIONS.IOS.LOCATION_WHEN_IN_USE])
-                        mapState.toggleFgroundLocationPermission(permitted)
-                        if (permitted) console.log('[Permissions]: Location permission granted')
-                        else console.log('[Permissions]: Location permission denied')
-                    }
+    }, [AppState.currentState])
+
+    /**
+     * Gets foreground location permissions from user, runs every time app is brought to foreground
+     */
+    useEffect(() => {
+        const checkForegroundLocationPermissions = async () => {
+            if (AppState.currentState == 'active') {
+                console.log('[Permissions]: Checking location permissions...')
+                if (Platform.OS == 'android') {
+                    const permitted = await checkVoicePermissions([PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION, PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION])
+                    mapState.toggleFgroundLocationPermission(permitted)
+                    if (permitted) console.log('[Permissions]: Location permission granted')
+                    else console.log('[Permissions]: Location permission denied')
                 }
-            }
-            checkForegroundLocationPermissions();
-        }, [AppState.currentState])
-    
-        /**
-         * Gets background location permissions from user, runs every time app is brought to foreground
-         */
-        useEffect(() => {
-            const checkBackgroundLocationPermissions = async () =>  {
-                if (AppState.currentState == 'active') {
-                    if (Platform.OS == 'android') {        
-                        const permitted = await checkVoicePermissions([PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION])
-                        mapState.toggleBgroundLocationPermission(permitted)
-                        if (permitted) console.log('[Permissions]: Background location permission granted')
-                        else console.log('[Permissions]: Background location permission denied')
-                    }
-                    else if (Platform.OS == 'ios') {
-                        const permitted = await checkVoicePermissions([PERMISSIONS.IOS.LOCATION_ALWAYS])
-                        mapState.toggleBgroundLocationPermission(permitted)
-                        if (permitted) console.log('[Permissions]: Background location permission granted')
-                        else console.log('[Permissions]: Background location permission denied')
-                    }
+                else if (Platform.OS == 'ios') {
+                    const permitted = await checkVoicePermissions([PERMISSIONS.IOS.LOCATION_WHEN_IN_USE])
+                    mapState.toggleFgroundLocationPermission(permitted)
+                    if (permitted) console.log('[Permissions]: Location permission granted')
+                    else console.log('[Permissions]: Location permission denied')
                 }
             }
-            checkBackgroundLocationPermissions();
-        }, [AppState.currentState])
-    
-        /**
-         * Gets net location permission the existing location permission states. It will check foreground and background permissions and AppState, 
-         * then from that it will decide if location-enabled features should be activated (through the mapState state values).
-         */
-         useEffect(() => {
-            const updateLocationPermissionOnAppStateChange = async () => {
-                const netLocationPermissions = mapState.bgroundLocationPermission || (mapState.fgroundLocationPermission && AppState.currentState == 'active')
-                console.log('[Permissions]: Appstate, or location permissions changed, net location permissions found to be: ' + netLocationPermissions)
-                mapState.toggleLocationPermitted(netLocationPermissions)
+        }
+        checkForegroundLocationPermissions();
+    }, [AppState.currentState])
+
+    /**
+     * Gets background location permissions from user, runs every time app is brought to foreground
+     */
+    useEffect(() => {
+        const checkBackgroundLocationPermissions = async () => {
+            if (AppState.currentState == 'active') {
+                if (Platform.OS == 'android') {
+                    const permitted = await checkVoicePermissions([PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION])
+                    mapState.toggleBgroundLocationPermission(permitted)
+                    if (permitted) console.log('[Permissions]: Background location permission granted')
+                    else console.log('[Permissions]: Background location permission denied')
+                }
+                else if (Platform.OS == 'ios') {
+                    const permitted = await checkVoicePermissions([PERMISSIONS.IOS.LOCATION_ALWAYS])
+                    mapState.toggleBgroundLocationPermission(permitted)
+                    if (permitted) console.log('[Permissions]: Background location permission granted')
+                    else console.log('[Permissions]: Background location permission denied')
+                }
             }
-            updateLocationPermissionOnAppStateChange()
-        }, [AppState.currentState, mapState.bgroundLocationPermission, mapState.fgroundLocationPermission]) 
+        }
+        checkBackgroundLocationPermissions();
+    }, [AppState.currentState])
+
+    /**
+     * Gets net location permission the existing location permission states. It will check foreground and background permissions and AppState, 
+     * then from that it will decide if location-enabled features should be activated (through the mapState state values).
+     */
+    useEffect(() => {
+        const updateLocationPermissionOnAppStateChange = async () => {
+            const netLocationPermissions = mapState.bgroundLocationPermission || (mapState.fgroundLocationPermission && AppState.currentState == 'active')
+            console.log('[Permissions]: Appstate, or location permissions changed, net location permissions found to be: ' + netLocationPermissions)
+            mapState.toggleLocationPermitted(netLocationPermissions)
+        }
+        updateLocationPermissionOnAppStateChange()
+    }, [AppState.currentState, mapState.bgroundLocationPermission, mapState.fgroundLocationPermission])
 
 
     /**
      * Animates the map to fly over to and focus on the user's location.
      */
-     const flyToUser = () => {
+    const flyToUser = () => {
         console.log('[Map]: Centering on user')
         if (mapState.userLocation) {
-            mapState.mapRef.current?.animateToRegion({latitude: mapState.userLocation.latitude, longitude: mapState.userLocation.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01})
+            mapState.mapRef.current?.animateToRegion({ latitude: mapState.userLocation.latitude, longitude: mapState.userLocation.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01 })
         }
     }
 
     /**
      * Activates speech recognition and opens the voice panel
      */
-     const startSpeech = () => {
+    const startSpeech = () => {
         props.toggleLmDetails(false);
         props.toggleLmAdd(false);
         Spokestack.activate()
@@ -225,7 +233,7 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
      */
     const getInitialRegion = () => {
         if (mapState.userLocation) {
-            return {latitude: mapState.userLocation.latitude, longitude: mapState.userLocation.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01}
+            return { latitude: mapState.userLocation.latitude, longitude: mapState.userLocation.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01 }
         }
     }
 
@@ -233,10 +241,10 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
      * Method that runs every time user location changes, updates user location state in memory and checks if any landmarks are nearby
      */
     const updateLocation = async (coord: LatLng) => {
-        
+
         mapState.setUserLocation(coord)
         // get 10m radius around user
-        const userAlertRadius = circle([coord.longitude, coord.latitude], 10, {units: 'meters'})
+        const userAlertRadius = circle([coord.longitude, coord.latitude], 10, { units: 'meters' })
 
         // check each landmark to see if its inside user radius. if it is, and it isn't already in the list of notified landmarks, add it
         const newLandmarksNearUser = props.landmarks?.filter(lm => {
@@ -255,15 +263,15 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
         if (newLandmarksNotPreviouslyNearUser?.length > 0) {
             const body = newLandmarksNotPreviouslyNearUser.length > 1 ? "There are new landmarks near by. Tap here to view" : "There is a new landmark close by. Tap here to view"
             const notifType: NotifType = newLandmarksNotPreviouslyNearUser.length > 1 ? 'near-landmarks' : 'near-landmark'
-            const data = {notif_type: notifType, landmarks: newLandmarksNotPreviouslyNearUser.length == 1 ? newLandmarksNearUser : null}
+            const data = { notif_type: notifType, landmarks: newLandmarksNotPreviouslyNearUser.length == 1 ? newLandmarksNearUser : null }
             await Notifications.scheduleNotificationAsync({
                 content: {
-                  title: "⚠ Landmarks close by ⚠",
-                  body: body,
-                  data: data
+                    title: "⚠ Landmarks close by ⚠",
+                    body: body,
+                    data: data
                 },
                 trigger: { seconds: 2 },
-              });
+            });
         }
     }
 
@@ -271,7 +279,7 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
         if (mapState.landmarksNearUser?.length > 1) {
             mapState.toggleNearbyLmPanel(true)
         }
-        else if (mapState.landmarksNearUser?.length === 1) { 
+        else if (mapState.landmarksNearUser?.length === 1) {
             props.setSelectedLandmarkId(mapState.landmarksNearUser[0].id)
         }
     }
@@ -279,91 +287,105 @@ const OutdoorMap: React.FC<OutdoorMapProps> = (props) => {
     return (
         <TouchableWithoutFeedback>
             <>
-            {/*Main map component*/}
-            <Modal transparent={true} animationType="fade" visible={mapState.loading}>
-                <View style={{flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.3)'}}>
-                    <View style={{width: '60%', height: '30%', backgroundColor: colors.red, justifyContent: 'center', alignItems: 'center', borderRadius: 20}}>
-                        <ActivityIndicator size="large" color="white" style={{marginBottom: 20}}/>
-                        <Text style={{fontSize: 15, color: 'white'}}>Refreshing</Text>
+                {/*Main map component*/}
+                <Modal transparent={true} animationType="fade" visible={mapState.loading}>
+                    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.3)' }}>
+                        <View style={{ width: '60%', height: '30%', backgroundColor: colors.red, justifyContent: 'center', alignItems: 'center', borderRadius: 20 }}>
+                            <ActivityIndicator size="large" color="white" style={{ marginBottom: 20 }} />
+                            <Text style={{ fontSize: 15, color: 'white' }}>Refreshing</Text>
+                        </View>
                     </View>
-                </View>
-            </Modal>
-            <MapView 
-                key={mapState.refreshKey}
-                toolbarEnabled={false}
-                onPress={() => Keyboard.dismiss()}
-                testID="mapView"
-                ref={mapState.mapRef} 
-                style={{width: '100%', height: '100%'}}
-                initialRegion={getInitialRegion()} 
-                onLongPress={async (e) => await props.promptAddLandmark(e.nativeEvent.coordinate.longitude, e.nativeEvent.coordinate.latitude)} 
-                showsUserLocation={mapState.locationPermitted} 
-                onUserLocationChange={e => updateLocation(e.nativeEvent.coordinate)}
-                followsUserLocation={mapState.followUser}
-                showsMyLocationButton={false}>
-                <Polygon // polygon for cameron library
-                coordinates={[
-                    {latitude: 53.527190, longitude: -113.524205 },
-                    {latitude: 53.526510, longitude: -113.524205 },
-                    {latitude: 53.526510, longitude: -113.523452 },
-                    {latitude: 53.527190, longitude: -113.523452 },
-                    // { name: "5", latitude: 60, longitude: -105 },
-                ]}
-                fillColor={`rgba(100,100,200,0.3)`}
-                strokeWidth={2.5}
-                tappable={true}
-                onPress={() => props.mapNavigation.navigate("Indoor")}
-                />
-
-                {props.applyFilters(props.landmarks)?.map((landmark) => {
-                    if(landmark.floor == null){
-                        let trackChanges = false;
-                        if (landmark?.id == props.selectedLandmarkId) {
-                            trackChanges = true;
+                </Modal>
+                <MapView
+                    key={mapState.refreshKey}
+                    toolbarEnabled={false}
+                    onPress={() => Keyboard.dismiss()}
+                    testID="mapView"
+                    ref={mapState.mapRef}
+                    style={{ width: '100%', height: '100%' }}
+                    initialRegion={getInitialRegion()}
+                    onLongPress={async (e) => await props.promptAddLandmark(e.nativeEvent.coordinate.longitude, e.nativeEvent.coordinate.latitude)}
+                    showsUserLocation={mapState.locationPermitted}
+                    onUserLocationChange={e => updateLocation(e.nativeEvent.coordinate)}
+                    followsUserLocation={mapState.followUser}
+                    showsMyLocationButton={false}>
+                    <Polygon // polygon for cameron library
+                        coordinates={[
+                            { latitude: 53.527190, longitude: -113.524205 },
+                            { latitude: 53.526510, longitude: -113.524205 },
+                            { latitude: 53.526510, longitude: -113.523452 },
+                            { latitude: 53.527190, longitude: -113.523452 },
+                            // { name: "5", latitude: 60, longitude: -105 },
+                        ]}
+                        fillColor={`rgba(100,100,200,0.3)`}
+                        strokeWidth={2.5}
+                        tappable={true}
+                        onPress={() => props.mapNavigation.navigate("Indoor")}
+                    />
+
+                    {props.applyFilters(props.landmarks)?.map((landmark) => {
+                        if (landmark.floor == null) {
+                            let trackChanges = false;
+                            if (landmark?.id == props.selectedLandmarkId) {
+                                trackChanges = true;
+                            }
+                            return (
+                                <Marker
+                                    tracksViewChanges={trackChanges}
+                                    onPress={() => props.focusLandmark(landmark)}
+                                    key={landmark.id}
+                                    coordinate={{ latitude: landmark.latitude as number, longitude: landmark.longitude as number }} >
+                                    {landmark.landmark_type ? <Image style={{ height: 35, width: 25 }} source={lmTypes[landmark.landmark_type].image} /> : null}
+                                </Marker>)
                         }
-                        return (
-                            <Marker 
-                            tracksViewChanges={trackChanges}
-                            onPress={() => props.focusLandmark(landmark)}
-                            key={landmark.id} 
-                            coordinate={{latitude: landmark.latitude as number, longitude: landmark.longitude as number}} >
-                            { landmark.landmark_type ? <Image style={{height: 35, width: 25}} source={lmTypes[landmark.landmark_type].image} /> : null}
-                        </Marker>)}
                     }
-                )}
-
-            </MapView>
-            {/*Map buttons*/}
-            {mapState.landmarksNearUser?.length > 0 ? 
-            <TouchableOpacity style={[mapStyles.lowerMapButton, mapStyles.alertButton]} onPress={focusNearbyLandmarks}>
-                <FontAwesome name='exclamation-triangle' size={20} color='white' />
-                <Badge positioning={{bottom: 7, right: 4}} value={mapState.landmarksNearUser.length}/>
-            </TouchableOpacity> : null}
-            {mapState.locationPermitted && mapState.voicePermission ? 
-            <IconButton size={20} color='white' style={[mapStyles.lowerMapButton, mapStyles.voiceButton]} icon="microphone" onPress={startSpeech}/>: null}
-            <IconButton size={20} color='white' style={[mapStyles.lowerMapButton, mapStyles.addLandmarkButton]} icon="plus" onPress={async () => await props.promptAddLandmark(mapState.userLocation.longitude, mapState.userLocation.latitude)}/>
-            <IconButton size={20} color='white' style={[mapStyles.lowerMapButton, mapStyles.userLocationButton]} icon="location-arrow" onPress={flyToUser}/>
-            <NearbyLandmarksPanel 
-                focusLandmark={props.focusLandmark}
-                nearbyLmPanelVisible={mapState.nearbyLmPanelVisible}
-                toggleAlertedLmPanel={mapState.toggleNearbyLmPanel}
-                nearbyLandmarks={mapState.landmarksNearUser}/>
-            {/*Map Panels*/}
-            {mapState.voicePermission && mapState.locationPermitted ? 
-            <VoicePanel 
-                landmarksNearby={mapState.landmarksNearUser?.length > 0} 
-                toggleAlertedLandmarksVisible={mapState.toggleNearbyLmPanel}
-                navigation={props.authNavigation}
-                userCoords={{longitude: mapState.userLocation?.longitude, latitude: mapState.userLocation?.latitude}}
-                toggleVoiceVisible={mapState.toggleVoiceVisible} 
-                toggleLmDetails={props.toggleLmDetails}
-                setSelectedLandmarkId={props.setSelectedLandmarkId}
-                voiceVisible={mapState.voiceVisible}
-                newLandmark={props.newLandmark} 
-                setNewLandmark={props.setNewLandmark}
-                /> : null }
+                    )}
+
+
+                    <MapViewDirections
+                        origin={coordinates[0]}
+                        destination={coordinates[1]}
+                        apikey={""} // insert your API Key here
+                        strokeWidth={4}
+                        strokeColor="#111111"
+                    />
+                    <Marker coordinate={coordinates[1]} />
+
+
+
+
+                </MapView>
+                {/*Map buttons*/}
+                {mapState.landmarksNearUser?.length > 0 ?
+                    <TouchableOpacity style={[mapStyles.lowerMapButton, mapStyles.alertButton]} onPress={focusNearbyLandmarks}>
+                        <FontAwesome name='exclamation-triangle' size={20} color='white' />
+                        <Badge positioning={{ bottom: 7, right: 4 }} value={mapState.landmarksNearUser.length} />
+                    </TouchableOpacity> : null}
+                {mapState.locationPermitted && mapState.voicePermission ?
+                    <IconButton size={20} color='white' style={[mapStyles.lowerMapButton, mapStyles.voiceButton]} icon="microphone" onPress={startSpeech} /> : null}
+                <IconButton size={20} color='white' style={[mapStyles.lowerMapButton, mapStyles.addLandmarkButton]} icon="plus" onPress={async () => await props.promptAddLandmark(mapState.userLocation.longitude, mapState.userLocation.latitude)} />
+                <IconButton size={20} color='white' style={[mapStyles.lowerMapButton, mapStyles.userLocationButton]} icon="location-arrow" onPress={flyToUser} />
+                <NearbyLandmarksPanel
+                    focusLandmark={props.focusLandmark}
+                    nearbyLmPanelVisible={mapState.nearbyLmPanelVisible}
+                    toggleAlertedLmPanel={mapState.toggleNearbyLmPanel}
+                    nearbyLandmarks={mapState.landmarksNearUser} />
+                {/*Map Panels*/}
+                {mapState.voicePermission && mapState.locationPermitted ?
+                    <VoicePanel
+                        landmarksNearby={mapState.landmarksNearUser?.length > 0}
+                        toggleAlertedLandmarksVisible={mapState.toggleNearbyLmPanel}
+                        navigation={props.authNavigation}
+                        userCoords={{ longitude: mapState.userLocation?.longitude, latitude: mapState.userLocation?.latitude }}
+                        toggleVoiceVisible={mapState.toggleVoiceVisible}
+                        toggleLmDetails={props.toggleLmDetails}
+                        setSelectedLandmarkId={props.setSelectedLandmarkId}
+                        voiceVisible={mapState.voiceVisible}
+                        newLandmark={props.newLandmark}
+                        setNewLandmark={props.setNewLandmark}
+                    /> : null}
             </>
-        </TouchableWithoutFeedback> )
+        </TouchableWithoutFeedback>)
 }
 
 export default observer(OutdoorMap);

+ 1 - 1
src/components/Map/Panels/AddLandmarkPanel.tsx

@@ -182,7 +182,7 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
             useNativeDriver={true}
             useNativeDriverForBackdrop={true}
             testID="addLMModal"
-            avoidKeyboard={false}
+            avoidKeyboard={photos?.length > 0}
             onBackdropPress={close}
             style={{ flex: 0, justifyContent: "flex-end", height: '100%', margin: 0 }}
             isVisible={visible} >

+ 12 - 5
src/navigation/MapNavigator.tsx

@@ -35,7 +35,7 @@ export type MapStackParamList = {
 export type MapStackNavigationProp = StackNavigationProp<MapStackParamList>
 
 const MapNavigator: React.FC = ({ }) => {
-    const {userId, anonUserId} = useAuth()
+    const { userId, anonUserId } = useAuth()
     const mapState = useMapState()
     const authNavigation = useNavigation() as MainTabsNavigationProp
     const authRoute = useRoute() as AuthTabsMapRouteProp
@@ -112,7 +112,7 @@ const MapNavigator: React.FC = ({ }) => {
  */
     const promptAddLandmark = async (longitude?: number, latitude?: number, floor?: number) => {
         console.log('[Map]: Opening add landmark panel...')
-        mapState.setNewLandmark({ latitude: latitude, longitude: longitude, floor: floor});
+        mapState.setNewLandmark({ latitude: latitude, longitude: longitude, floor: floor });
         mapState.toggleLmAdd(true)
         mapState.toggleLmDetails(false)
     }
@@ -158,7 +158,7 @@ const MapNavigator: React.FC = ({ }) => {
 
             {/* Filter chips and button*/}
             {!mapState.filterVisible && currentRoute == 'Indoor' ?
-                <View style={{ top: 60, right: 7.5 , position: 'absolute', flexDirection: "row-reverse", justifyContent: 'flex-end' }}>
+                <View style={{ top: 60, right: 7.5, position: 'absolute', flexDirection: "row-reverse", justifyContent: 'flex-end' }}>
                     <IconButton size={16} color={colors.red} style={[mapStyles.filterButtonIndoor]} icon="filter" onPress={() => mapState.toggleFilter(true)} />
                     <ScrollView horizontal={true} contentContainerStyle={{ alignItems: 'center' }} style={{ marginHorizontal: 10, flexDirection: 'row' }}>
                         {mapState.onlyOwned ? <Chip avatar={(<FontAwesome name="user" size={20} color='gray' style={{ textAlign: 'center', textAlignVertical: 'center' }} />)} style={{ borderWidth: 1, borderColor: 'lightgray', marginRight: 5, marginLeft: 10 }} onClose={() => mapState.toggleOnlyOwned(false)}>My landmarks</Chip> : null}
@@ -187,7 +187,7 @@ const MapNavigator: React.FC = ({ }) => {
 
             {/* Create Hamburger icon */}
             {currentRoute == 'Indoor' ?
-                <View style={{ top: 100, right: 7.5 , position: 'absolute', }}>
+                <View style={{ top: 100, right: 7.5, position: 'absolute', }}>
                     <Menu
                         visible={visible}
                         anchor={<IconButton size={16} color={colors.red} style={[mapStyles.filterButtonIndoor]} icon="bars" onPress={() => setVisible(true)} />}
@@ -203,6 +203,12 @@ const MapNavigator: React.FC = ({ }) => {
                             setVisible(false)
                             Linking.openURL('https://www.ualberta.ca/facilities-operations/portfolio/emergency-management-office/emergency-procedures/alarms-evacuation.html')
                             // Alert.alert("Cameron Library")
+                        }}>Emergency Procedures</MenuItem>
+
+                        <MenuItem onPress={() => {
+                            setVisible(false)
+                            Linking.openURL('https://www.library.ualberta.ca/')
+                            // Alert.alert("Cameron Library")
                         }}>Resources</MenuItem>
 
                     </Menu>
@@ -214,9 +220,10 @@ const MapNavigator: React.FC = ({ }) => {
                         anchor={<IconButton size={20} color={colors.red} style={[mapStyles.filterButtonOutdoor]} icon="bars" onPress={() => setVisible(true)} />}
                         onRequestClose={() => setVisible(false)}
                     >
-                        <MenuItem disabled={true} disabledTextColor='black' style={{alignItems: 'center', borderColor: 'black', borderBottomWidth: 1, marginHorizontal: 10, opacity: .7}}>
+                        <MenuItem disabled={true} disabledTextColor='black' style={{ alignItems: 'center', borderColor: 'black', borderBottomWidth: 1, marginHorizontal: 10, opacity: .7 }}>
                             <Text>Indoor buildings</Text>
                         </MenuItem>
+                        
                         <MenuItem onPress={() => {
                             setVisible(false)
                             navigate("Indoor")

+ 1 - 0
src/utils/GlobalUtils.ts

@@ -34,6 +34,7 @@ export const lmTypes: {[key: number]: {image: ImageURISource, label: string}} =
     13: {image: require('../../assets/monitor.png'), label: "monitor"},
     14: {image: require('../../assets/ramp.png'), label: "ramp"},
     15: {image: require('../../assets/water.png'), label: "water"},
+    16: {image: require('../../assets/childfriendly.png'), label: "child friendly area"},
 } 
 
 export const GlobalStyles = StyleSheet.create({

Файловите разлики са ограничени, защото са твърде много
+ 338 - 345
yarn.lock


Някои файлове не бяха показани, защото твърде много файлове са промени