Selaa lähdekoodia

merged indoor maps updates into nonaccounts

cdmoss 2 vuotta sitten
vanhempi
commit
ecac51f597

+ 1 - 1
.env

@@ -1,7 +1,7 @@
 SPOKESTACK_ID=c361ff3a-70c3-42e6-b0ee-0207edd03b18
 SPOKESTACK_TOKEN=1A8196594C401EB93035CC6D7D6328CF1855C2B359744E720953AC34B6F658CA
 
-API_URL=http://192.168.0.22
+API_URL=http://192.168.0.22:8000
 
 #API_URL=https://app.clicknpush.ca
 

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 23 - 231
assets/mapicon.svg


BIN
assets/rampinner.png


+ 64 - 0
package-lock.json

@@ -73,6 +73,7 @@
         "react-native-picker-select": "^7.0.0",
         "react-native-popup-menu": "^0.15.12",
         "react-native-reanimated": "~2.2.0",
+        "react-native-root-toast": "^3.3.0",
         "react-native-safe-area-context": "3.3.2",
         "react-native-screens": "~3.8.0",
         "react-native-sectioned-multi-select": "^0.8.1",
@@ -82,6 +83,8 @@
         "react-native-spokestack": "^6.1.4",
         "react-native-svg": "^12.1.1",
         "react-native-svg-transformer": "^1.0.0",
+        "react-native-toast-message": "^2.1.3",
+        "react-native-view-shot": "3.1.2",
         "react-native-web": "^0.13",
         "react-native-windows": "0.64.2",
         "react-query": "^3.19.0",
@@ -25169,6 +25172,23 @@
         "ua-parser-js": "^0.7.18"
       }
     },
+    "node_modules/react-native-root-siblings": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/react-native-root-siblings/-/react-native-root-siblings-4.1.1.tgz",
+      "integrity": "sha512-sdmLElNs5PDWqmZmj4/aNH4anyxreaPm61c4ZkRiR8SO/GzLg6KjAbb0e17RmMdnBdD0AIQbS38h/l55YKN4ZA=="
+    },
+    "node_modules/react-native-root-toast": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/react-native-root-toast/-/react-native-root-toast-3.3.0.tgz",
+      "integrity": "sha512-C4Pqu+Ae7kXsYJwTvz8NshyJ9SL5YJd+/vCkvgDAxxR8AYlPFggEcTCMNARIWXuRwthLbuwcakh4z9k6qg95dg==",
+      "dependencies": {
+        "prop-types": "^15.5.10",
+        "react-native-root-siblings": "^4.0.0"
+      },
+      "peerDependencies": {
+        "react-native": ">=0.47.0"
+      }
+    },
     "node_modules/react-native-safe-area-context": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
@@ -25313,6 +25333,15 @@
         "boolbase": "~1.0.0"
       }
     },
+    "node_modules/react-native-toast-message": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/react-native-toast-message/-/react-native-toast-message-2.1.3.tgz",
+      "integrity": "sha512-K3hHSWezWixxOZUDbxPSarEG+tPv2WcaJG4L7dvRUC1TOKNVCEKzXjsx+6ZMxllDrJ0sFczuYGqZibGpFe/ubA==",
+      "peerDependencies": {
+        "react": "*",
+        "react-native": "*"
+      }
+    },
     "node_modules/react-native-vector-icons": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-8.1.0.tgz",
@@ -25371,6 +25400,15 @@
         "node": ">=10"
       }
     },
+    "node_modules/react-native-view-shot": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz",
+      "integrity": "sha512-9u9fPtp6a52UMoZ/UCPrCjKZk8tnkI9To0Eh6yYnLKFEGkRZ7Chm6DqwDJbYJHeZrheCCopaD5oEOnRqhF4L2Q==",
+      "peerDependencies": {
+        "react": "*",
+        "react-native": "*"
+      }
+    },
     "node_modules/react-native-web": {
       "version": "0.13.18",
       "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.13.18.tgz",
@@ -51282,6 +51320,20 @@
         }
       }
     },
+    "react-native-root-siblings": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/react-native-root-siblings/-/react-native-root-siblings-4.1.1.tgz",
+      "integrity": "sha512-sdmLElNs5PDWqmZmj4/aNH4anyxreaPm61c4ZkRiR8SO/GzLg6KjAbb0e17RmMdnBdD0AIQbS38h/l55YKN4ZA=="
+    },
+    "react-native-root-toast": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/react-native-root-toast/-/react-native-root-toast-3.3.0.tgz",
+      "integrity": "sha512-C4Pqu+Ae7kXsYJwTvz8NshyJ9SL5YJd+/vCkvgDAxxR8AYlPFggEcTCMNARIWXuRwthLbuwcakh4z9k6qg95dg==",
+      "requires": {
+        "prop-types": "^15.5.10",
+        "react-native-root-siblings": "^4.0.0"
+      }
+    },
     "react-native-safe-area-context": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
@@ -51400,6 +51452,12 @@
         "path-dirname": "^1.0.2"
       }
     },
+    "react-native-toast-message": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/react-native-toast-message/-/react-native-toast-message-2.1.3.tgz",
+      "integrity": "sha512-K3hHSWezWixxOZUDbxPSarEG+tPv2WcaJG4L7dvRUC1TOKNVCEKzXjsx+6ZMxllDrJ0sFczuYGqZibGpFe/ubA==",
+      "requires": {}
+    },
     "react-native-vector-icons": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-8.1.0.tgz",
@@ -51450,6 +51508,12 @@
         }
       }
     },
+    "react-native-view-shot": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz",
+      "integrity": "sha512-9u9fPtp6a52UMoZ/UCPrCjKZk8tnkI9To0Eh6yYnLKFEGkRZ7Chm6DqwDJbYJHeZrheCCopaD5oEOnRqhF4L2Q==",
+      "requires": {}
+    },
     "react-native-web": {
       "version": "0.13.18",
       "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.13.18.tgz",

+ 3 - 0
package.json

@@ -91,6 +91,7 @@
     "react-native-picker-select": "^7.0.0",
     "react-native-popup-menu": "^0.15.12",
     "react-native-reanimated": "~2.2.0",
+    "react-native-root-toast": "^3.3.0",
     "react-native-safe-area-context": "3.3.2",
     "react-native-screens": "~3.8.0",
     "react-native-sectioned-multi-select": "^0.8.1",
@@ -100,6 +101,8 @@
     "react-native-spokestack": "^6.1.4",
     "react-native-svg": "^12.1.1",
     "react-native-svg-transformer": "^1.0.0",
+    "react-native-toast-message": "^2.1.3",
+    "react-native-view-shot": "3.1.2",
     "react-native-web": "^0.13",
     "react-native-windows": "0.64.2",
     "react-query": "^3.19.0",

+ 22 - 18
src/components/Map/IndoorFloor.tsx

@@ -1,20 +1,24 @@
-import React from "react"
-import BasementC from './images/BasementC.svg';
-import FirstFloorC from './images/FirstFloorC.svg'
-import SecondFloorC from './images/SecondFloorC.svg'
-import ThirdFloorC from './images/ThirdFloorC.svg'
-import FourthFloorC from './images/FourthFloorC.svg'
-import FifthFloorC from './images/FifthFloorC.svg'
+import { View, Text } from 'react-native'
+import React from 'react'
+import BasementC from './MainMapComponent/images/BasementC.svg'
+import FirstFloorC from './MainMapComponent/images/FirstFloorC.svg'
+import SecondFloorC from './MainMapComponent/images/SecondFloorC.svg'
+import ThirdFloorC from './MainMapComponent/images/ThirdFloorC.svg'
+import FourthFloorC from './MainMapComponent/images/FourthFloorC.svg'
+import FifthFloorC from './MainMapComponent/images/FifthFloorC.svg'
 
-export const IndoorFloor: React.FC<{floorNum: number}> = ({floorNum}) => {
-    const compArray = [
-        <BasementC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-        <FirstFloorC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-        <SecondFloorC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-        <ThirdFloorC viewBox='250 136 310 310' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-        <FourthFloorC viewBox='260 151 270 260' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-        <FifthFloorC viewBox='257 153 270 290' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-    ]
+// export const IndoorFloor: React.FC<{floorNum: number}> = (props) => {
+function IndoorFloor(props) {
+  const compArray = [
+      <BasementC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
+      <FirstFloorC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
+      <SecondFloorC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
+      <ThirdFloorC viewBox='250 136 310 310' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
+      <FourthFloorC viewBox='260 151 270 260' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
+      <FifthFloorC viewBox='257 153 270 290' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
+  ]
 
-    return compArray[floorNum]
-}
+  return compArray[props.floorNum]
+}
+
+export default IndoorFloor

+ 42 - 0
src/components/Map/MainMapComponent/ArrowButton.tsx

@@ -0,0 +1,42 @@
+import { View, Text, TouchableOpacity, StyleSheet, ToastAndroid} from 'react-native'
+import React from 'react'
+import { FontAwesome } from "@expo/vector-icons";
+import { colors, lmTypes } from "../../../utils/GlobalUtils";
+
+
+function ArrowButton(props) {
+    if (props.num != 0) {
+        return (
+          <View style={{ flex: 1.2, marginHorizontal: 7, backgroundColor: "#d4d4d4", height: 53.5, maxWidth: 60, borderRadius: 8 }}>
+            {/* <TouchableOpacity style={styles.arrowButton} onPress={() => { setFloor(prevState => prevState + props.num) }}></TouchableOpacity> */}
+            <TouchableOpacity style={styles.arrowButton} onPress={props.propEvent} >
+              <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
+                {/* <FontAwesomeIcon icon="fa-solid fa-left" /> */}
+                <FontAwesome style={{ marginLeft: (props.num == 1 ? 5 : -5) }} color={"white"} size={35} name={props.fontAweIcon} />
+              </View>
+            </TouchableOpacity>
+          </View>
+        )
+      }
+      else {
+        return (
+          <View style={{ flex: 1.2, marginHorizontal: 7, height: 53.5, maxWidth: 60, borderRadius: 10 }}>
+          </View>
+        )
+      }
+  }
+
+
+const styles = StyleSheet.create({
+    arrowButton: {
+        flex: 1,
+        backgroundColor: colors.red,
+        // backgroundColor: "blue",
+        height: 53.5,
+        borderRadius: 8,
+      },
+})
+
+
+
+export default ArrowButton

+ 18 - 0
src/components/Map/MainMapComponent/BottomButtons.tsx

@@ -0,0 +1,18 @@
+import { View, Text, TouchableOpacity, StyleSheet, Linking} from 'react-native'
+import React from 'react'
+import { colors, lmTypes } from "../../../utils/GlobalUtils";
+
+function BottomButtons(props) {
+  return (
+<View style={{ flexDirection: "row", justifyContent: "space-around", alignItems: "flex-end", borderColor: "green", borderWidth: 0 }}>
+        <TouchableOpacity style={{ backgroundColor: colors.red, height: 30, paddingHorizontal: 7, paddingTop: 3, borderRadius: 5 }} onPress={() => props.navigation.goBack()}>
+          <Text style={{ fontSize: 16, color: "white", textAlign: "right", textAlignVertical: "bottom" }}>{"Go back to map"}</Text>
+        </TouchableOpacity>
+        <TouchableOpacity style={{ backgroundColor: colors.red, height: 30, paddingHorizontal: 7, paddingTop: 3, borderRadius: 5 }} onPress={() => Linking.openURL('https://www.library.ualberta.ca/')}>
+          <Text style={{ fontSize: 16, color: "white", textAlign: "center", }}>{"Resources"}</Text>
+        </TouchableOpacity>
+      </View>  )
+}
+
+
+export default BottomButtons

+ 90 - 96
src/components/Map/MainMapComponent/IndoorMap.tsx

@@ -1,8 +1,8 @@
 import React, { useState, useEffect, Children } from 'react';
-import { View, Text, StatusBar, StyleSheet, Dimensions, Button, ActivityIndicator, Alert, Modal, PanResponderCallbacks, PanResponderGestureState, GestureResponderEvent, ImageSourcePropType, TouchableOpacity, Platform } from 'react-native';
+import { View, Text, StatusBar, StyleSheet, Dimensions, Button, ActivityIndicator, Alert, Modal, PanResponderCallbacks, PanResponderGestureState, GestureResponderEvent, ImageSourcePropType, TouchableOpacity, Platform, Linking, } from 'react-native';
 import { Svg, Defs, Rect, Mask, Circle, Marker, Path, Polyline, Image } from 'react-native-svg';
 import { RadioButton } from 'react-native-paper';
-import { Picker } from '@react-native-picker/picker';
+import { Picker as EricPicker } from '@react-native-picker/picker';
 import ReactNativeZoomableView from '@openspacelabs/react-native-zoomable-view/src/ReactNativeZoomableView';
 import Spinner from 'react-native-spinkit'
 import { colors, lmTypes } from "../../../utils/GlobalUtils";
@@ -10,15 +10,13 @@ import { MapStackNavigationProp } from "../../../navigation/MapNavigator"
 import CustomModal from './modal';
 import { FontAwesome } from "@expo/vector-icons";
 import ReactDOMServer from 'react-dom/server'; //npm i --save-dev @types/react-dom
-
-
-import BasementC from './images/BasementC.svg';
-import FirstFloorC from './images/FirstFloorC.svg'
-import SecondFloorC from './images/SecondFloorC.svg'
-import ThirdFloorC from './images/ThirdFloorC.svg'
-import FourthFloorC from './images/FourthFloorC.svg'
-import FifthFloorC from './images/FifthFloorC.svg'
 import { ZoomableViewEvent } from '@openspacelabs/react-native-zoomable-view/src/typings';
+import IndoorFloor from '../IndoorFloor'
+// import Toast from 'react-native-toast-message';
+import Toast from 'react-native-root-toast';
+import ArrowButton from './ArrowButton'
+import BottomButtons from './BottomButtons'
+import Picker from 'react-native-picker-select';
 import { Landmark } from '../../../data/landmarks';
 
 
@@ -34,7 +32,7 @@ interface IndoorMapProps {
 
 
 const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddLandmark, focusLandmark, applyFilter }) => {
-  const [floor, setFloor] = useState(0);
+  const [floor, setFloor] = useState(1);
   const [showME, setShowME] = useState(false);
   const [showDots, setShowDots] = useState(false);
   const [showAddedDot, setShowAddedDot] = useState(false)
@@ -42,29 +40,11 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
 
   const [localLandmarks, setLocalLandmarks] = useState<Landmark[]>([])
 
-  const imageDim = 0.05 * Dimensions.get("window").width;
 
-  function arrowBut(num, fontAweIcon) {
-    if (num != 0) {
-      return (
-        <View style={{ flex: 1.2, marginHorizontal: 7, backgroundColor: "#d4d4d4", height: 53.5, maxWidth: 60 }}>
-          <TouchableOpacity style={styles.arrowButton} onPress={() => { setFloor(prevState => prevState + num) }}>
-            <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
-              {/* <FontAwesomeIcon icon="fa-solid fa-left" /> */}
-              <FontAwesome style={{ marginLeft: (num == 1 ? 5 : -5) }} color={"white"} size={35} name={fontAweIcon} />
-            </View>
-          </TouchableOpacity>
-        </View>
-      )
-    }
-    else {
-      return (
-        <View style={{ flex: 1.2, marginHorizontal: 7, height: 53.5, maxWidth: 60, borderRadius: 10 }}>
-        </View>
-      )
-    }
-  }
+  const [sport, setSport] = useState("football");
+
 
+  const imageDim = 0.05 * Dimensions.get("window").width;
 
   const loadCircles = applyFilter(landmarks)?.map((item) => {
     if (item.floor == floor && SVGdim[0] != 1 && SVGdim[1] != 1) {
@@ -84,20 +64,28 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
 
 
   function addLandmark(evt: GestureResponderEvent) {
-    if (evt) {
+    if (evt != null) {
       Alert.alert("Are you sure you want to add a landmark here?", undefined,
         [{ text: "Cancel" }
           ,
         {
           text: "Confirm", onPress: async () => {
-            await promptAddLandmark((evt.nativeEvent.locationX - imageDim / 2) / SVGdim[0], (evt.nativeEvent.locationY - imageDim / 2) / SVGdim[1], floor)
+            try {
+              await promptAddLandmark((evt.nativeEvent.locationX - imageDim / 2) / SVGdim[0], (evt.nativeEvent.locationY - imageDim / 2) / SVGdim[1], floor)
+            }
+            catch (err) {
+              console.log("error has been caught!")
+              Toast.show("An error has occured. Please ensure thumb 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
+            }
           }
-        }])
+        }]
+      )
     }
   }
 
-
-
   useEffect(() => {
     // Alert.alert("useEffect has been triggered")
     setShowAddedDot(false)
@@ -105,18 +93,6 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
     setTimeout(() => setShowME(true), 100);
   })
 
-
-
-  const compArray = [
-    <BasementC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-    <FirstFloorC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-    <SecondFloorC style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-    <ThirdFloorC viewBox='250 136 310 310' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-    <FourthFloorC viewBox='260 151 270 260' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-    <FifthFloorC viewBox='257 153 270 290' style={{ opacity: 0.7 }} height={"100%"} width={"100%"} />,
-  ]
-
-
   const childToWeb = (child: any) => {
     const { type, props } = child;
     const name = type && type.displayName;
@@ -130,16 +106,16 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
   function renderSvg() {
     return (
       <Svg height="100%" width="100%" style={{ backgroundColor: '#33AAFF' }}>
-      <Rect
-        x="50"
-        y="50"
-        width="50"
-        height="50"
-        fill="#3399ff"
-        strokeWidth="3"
-        stroke="rgb(0,0,0)"
-      />
-    </Svg>
+        <Rect
+          x="50"
+          y="50"
+          width="50"
+          height="50"
+          fill="#3399ff"
+          strokeWidth="3"
+          stroke="rgb(0,0,0)"
+        />
+      </Svg>
     )
   }
 
@@ -147,13 +123,14 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
     const element = renderSvg();
     const webJsx = Platform.OS === 'web' ? element : toWeb(element);
     const svgString = ReactDOMServer.renderToStaticMarkup(webJsx);
-    //console.log(svgString)
+    console.log(svgString)
     // setText(svgString)
     return svgString
   }
 
-
-
+  function changer(num) {
+    setFloor(prevState => prevState + num)
+  }
 
   // TODO: wire up promptaddlandmark, applyfilters, and focuslandmark methods passed from MapNavigator
   return (
@@ -166,33 +143,54 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
 
       <View style={{ borderColor: "blue", borderWidth: 0, maxHeight: 50, flex: 1, flexDirection: "row", justifyContent: "center", }}>
 
-        {/* arrow-circle-o-right */}
-        {floor == 0 ? arrowBut(0, "") : arrowBut(-1, "chevron-left")}
+        {floor == 0 ? <ArrowButton num={0} fontAweIcon={""} /> : <ArrowButton num={-1} fontAweIcon={"chevron-left"} propEvent={() => changer(-1)} />}
+
+        <View style={{ backgroundColor: colors.red, flex: 5, height: 53.5, width: 200 }}>
+          <Picker
+            placeholder={{}}
+            value={floor}
+            style={{ inputAndroid: {color:"white"} , inputIOS: {color:"white"} }}
+            onValueChange={(value) => {
+              setFloor(value)
+              setShowME(false)
+            }
+            }
+            items={[
+              { label: 'Basement', value: 0 },
+              { label: 'First Floor', value: 1 },
+              { label: 'Second Floor', value: 2 },
+              { label: 'Third Floor', value: 3 },
+              { label: 'Fourth Floor', value: 4 },
+              { label: 'Fifth Floor', value: 5 },
+            ]}
+          />
+        </View>
 
 
-        <Picker
+        {/* <EricPicker
           style={{ backgroundColor: colors.red, width: 200, height: 50, flex: 5, color: 'white' }}
           selectedValue={floor} // the text of what gets displayed on the dropdown header
           onValueChange={(itemValue, itemIndex: number) => {
             setFloor(itemIndex)
             setShowME(false)
           }}>
-          {/* The value in Picker.Item refers to selectedValue in Picker, which refers to the state "floor" */}
-          <Picker.Item label="Basement" value={0} />
-          <Picker.Item label="First Floor" value={1} />
-          <Picker.Item label="Second Floor" value={2} />
-          <Picker.Item label="Third Floor" value={3} />
-          <Picker.Item label="Fourth Floor" value={4} />
-          <Picker.Item label="Fifth Floor" value={5} />
-        </Picker>
+          The value in EricPicker.Item refers to selectedValue in EricPicker, which refers to the state "floor"
+          <EricPicker.Item label="Basement" value={0} />
+          <EricPicker.Item label="First Floor" value={1} />
+          <EricPicker.Item label="Second Floor" value={2} />
+          <EricPicker.Item label="Third Floor" value={3} />
+          <EricPicker.Item label="Fourth Floor" value={4} />
+          <EricPicker.Item label="Fifth Floor" value={5} />
+        </EricPicker> */}
 
-        {floor == 5 ? arrowBut(0, "") : arrowBut(1, "chevron-right")}
 
-      </View>
+        {/* {floor == 5 ? arrowBut(0, "") : arrowBut(1, "chevron-right")} */}
+        {floor == 5 ? <ArrowButton num={0} fontAweIcon={""} /> : <ArrowButton num={1} fontAweIcon={"chevron-right"} propEvent={() => changer(1)} />}
 
 
+      </View>
 
-      <View style={{ flex: 1, alignItems: "center", height: '100%', width: '100%' }}>
+      <View style={{ flex: 1, alignItems: "center", height: '100%', width: '100%', borderColor: 'purple', borderWidth: 0 }}>
         <View style={styles.container}>
           {showME === false ?
             <View style={{ display: 'flex', flexDirection: 'row', justifyContent: "center", }}>
@@ -204,24 +202,20 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
               // bindToBorders={false}
               bindToBorders={true}
 
-
               zoomStep={2.8}
               // initialZoom={2.2}
               maxZoom={2.8}
               minZoom={1}
               initialOffsetY={5}
               onLongPress={(event) => {
-                //console.log(event.nativeEvent)
-                serialize()
+                console.log("native event is " + event.nativeEvent)
+                // serialize()
                 addLandmark(event)
               }}>
 
-
-
               <Svg onLayout={event => {
-                // console.log("OFFICIAL: " + event.nativeEvent.layout.width + " , " + event.nativeEvent.layout.height)
+                console.log("OFFICIAL: " + event.nativeEvent.layout.width + " , " + event.nativeEvent.layout.height)
                 setSVGdim([event.nativeEvent.layout.width, event.nativeEvent.layout.height])
-
                 const transformedLandmarks = localLandmarks.map(item => {
                   return { ...item, coordx: item.longitude * event.nativeEvent.layout.width, coordy: item.latitude * event.nativeEvent.layout.height }
                 })
@@ -231,15 +225,9 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
               >
                 {/* {firstTime == true ? undefined : loadCircles} */}
                 {loadCircles}
-                {compArray[floor]}
+                <IndoorFloor floorNum={floor} />
               </Svg>
-
-
-
-
-
             </ReactNativeZoomableView>
-
           }
 
         </View>
@@ -249,11 +237,18 @@ const IndoorMap: React.FC<IndoorMapProps> = ({ navigation, landmarks, promptAddL
         setLocalLandmarks(landmarks)
       }} /> */}
 
-      <Button title='Press me to svgString' color={colors.red} onPress={serialize}></Button>
-      {/* <Button title="Go back to map" color={colors.red} onPress={() => navigation.goBack()} /> */}
+      {/* <Button title='Press me to svgString' color={colors.red} onPress={serialize}></Button> */}
+      {/* <View style={{ flex: 0.1, flexDirection: 'row', alignItems:'flex-end', justifyContent: 'space-around' , borderColor:'green' , borderWidth:0 ,}}>
+      <Button title="Go back to map" color={colors.red} onPress={() => navigation.goBack()} />
+      <Button title="Resources" color={colors.red} onPress={() => Linking.openURL('https://www.library.ualberta.ca/')} />
+      </View> */}
       {/* <TouchableOpacity style={styles.arrowButton} onPress={() => setFloor(prevState => prevState+1)} ><Text>Increase floor by 1</Text></TouchableOpacity> */}
 
-    </View>
+
+      <BottomButtons navigation={navigation}/>
+      {/* <Text>{floor}</Text> */}
+
+    </View >
   );
 }
 
@@ -265,7 +260,7 @@ const styles = StyleSheet.create({
     borderColor: "black",
     borderWidth: 2,
     marginVertical: 7,
-    aspectRatio: 8 / 10,  // (caters to portrait mode) (width is 66% the value of height dimension)
+    aspectRatio: 8 / 10,  // (caters to portrait mode) (width is 80% the value of height dimension)
     // flex: 1,
     // // backgroundColor: "#fff",
     // justifyContent: "center",
@@ -291,8 +286,7 @@ const styles = StyleSheet.create({
     // backgroundColor: "blue",
     height: 53.5,
     // borderRadius: 10,
-
-  }
+  },
 });
 
 

BIN
src/components/Map/MainMapComponent/landmark_images/information.png


BIN
src/components/Map/MainMapComponent/landmark_images/power.png


BIN
src/components/Map/MainMapComponent/landmark_images/stairs.png


+ 112 - 7
src/components/Map/Panels/AddLandmarkPanel.tsx

@@ -7,8 +7,8 @@
 
 import { FontAwesome } from "@expo/vector-icons";
 import { ImageInfo } from "expo-image-picker/build/ImagePicker.types";
-import React, { memo, useEffect, useState } from "react";
-import { ActivityIndicator, Dimensions, Image, Keyboard, KeyboardEventName, Platform, SafeAreaView, Text, TextInput, TouchableOpacity, View } from 'react-native';
+import React, { memo, useEffect, useState, useRef } from "react";
+import { ActivityIndicator, Dimensions, Image, Platform, SafeAreaView, Text, TextInput, TouchableOpacity, View, ImageSourcePropType, Share, KeyboardEventName, Keyboard, } from 'react-native';
 import { ScrollView } from "react-native-gesture-handler";
 import Modal from 'react-native-modal';
 import Picker from 'react-native-picker-select';
@@ -16,8 +16,12 @@ import { Landmark, LMPhoto, useAddLandmark } from "../../../data/landmarks";
 import { colors, lmTypes } from "../../../utils/GlobalUtils";
 import { IconButton, SecondaryButton } from "../../Buttons";
 import { PhotoPicker } from "../../PhotoPicker";
-import { IndoorFloor } from "../IndoorFloor";
 import TouchOpaq from "./LandmarkDetailsPanel/TouchOpaq";
+import { Svg, Rect, Image as ImageSVG, Circle } from 'react-native-svg'
+import ReactDOMServer from 'react-dom/server'; //npm i --save-dev @types/react-dom
+
+import IndoorFloor from "../IndoorFloor";
+import ViewShot from "react-native-view-shot";
 
 /**
  * Props for the {@link AddLandmarkPanel} component.
@@ -26,7 +30,7 @@ export interface AddLandmarkProps {
     /**
      * Whether the landmark is being added at the current users location
      */
-     landmarkAtCurrentLocation?: boolean;
+    landmarkAtCurrentLocation?: boolean;
     /**
      * The {@link landmark} object to be added.
      */
@@ -47,7 +51,7 @@ export interface AddLandmarkProps {
  * @component
  * @category Map
  */
-const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({newLandmark, setNewLandmark, setVisible, visible}) => {
+const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandmark, setVisible, visible }) => {
     const [photos, setPhotos] = useState<LMPhoto[]>([])
     const [photoSourceMenuOpened, togglePhotoSourceMenu] = useState<boolean>(false)
     const [keyboardOpened, setKeyboardOpened] = useState<boolean>(false);  
@@ -93,6 +97,11 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({newLandmark, setNewLandma
         else
             return Dimensions.get("window").height * .6
     }
+    const capture: any = useRef();
+
+    const imgWidth = 346
+    const imgHeight = 443
+    const imageDim = 27
 
     useEffect(() => {
         /**
@@ -112,11 +121,92 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({newLandmark, setNewLandma
         addLandmarkMutation.reset()
     }, [visible]);
 
+
+
+    const renderIndoorLandmarkPin = (landmark: Landmark) => {
+        return (
+            <ImageSVG
+                x={landmark.longitude * imgWidth}
+                y={landmark.latitude * imgHeight}
+                width={imageDim}
+                height={imageDim}
+                href={lmTypes[landmark.landmark_type]['image'] as ImageSourcePropType} />
+        )
+    }
+
+    const renderLandmarkSvg = (floor: number) => {
+        return (
+            <Svg height="100%" width="100%">
+                {renderIndoorLandmarkPin(newLandmark)}
+                <IndoorFloor floorNum={floor} />
+            </Svg>
+        )
+    }
+
+
+    // function renderLandmarkSvg(floor) {
+    //   return (
+    //   // <svg height="100%" width="100%" style="background-color:#33AAFF" preserveAspectRatio="xMidYMid meet"><rect x="50" y="50" width="50" height="50" fill="#3399ff" stroke-width="3" stroke="rgb(0,0,0)"></rect></svg>
+    //   //                                 style={{backgroundColor:'#33AAFF'}}
+    //     <Svg height="100%" width="100%" style={{ backgroundColor: '#33AAFF' }}>
+    //     <Rect
+    //       x="50"
+    //       y="50"
+    //       width="50"
+    //       height="50"
+    //       fill="#3399ff"
+    //       strokeWidth="3"
+    //       stroke="rgb(0,0,0)"
+    //     />
+    //     <Circle cx="200" cy="75" r="20" fill="pink" />
+    //   </Svg>
+    //   )
+    // }
+
+    // const childToWeb = (child: any) => {
+    //     const { type, props } = child;
+    //     const name = type && type.displayName;
+    //     const webName = name && name[0].toLowerCase() + name.slice(1);
+    //     const Tag = webName ? webName : type;
+    //     return <Tag {...props}>{toWeb(props.children)}</Tag>;
+    // };
+
+    // const toWeb = (children: any) => React.Children.map(children, childToWeb);
+
+
+    // function serialize() {
+    //     const element = renderLandmarkSvg(newLandmark.floor);
+    //     const webJsx = toWeb(element);
+    //     const svgString = ReactDOMServer.renderToStaticMarkup(webJsx);
+    //     console.log(svgString)
+    //     return svgString
+    // }
+
     /**
      * Calls {@link addLandmarkAsync} from {@link useLandmarks} to initate the process of adding a landmark, then closes the modal.
      */
     const submit = async () => {
-        await addLandmarkMutation.mutateAsync({landmarkValue: newLandmark, photos: photos})
+        if (typeof newLandmark.floor === 'number') {
+            // let imageURI = await capture.current.capture()
+            let imageURI = "string"
+            console.log("*AddLandmarkPanel* imageURI is " + imageURI)
+            await addLandmarkMutation.mutateAsync({ landmarkValue: newLandmark, photos: photos, indoorLmLocImg: imageURI })
+        }
+        else {
+            await addLandmarkMutation.mutateAsync({ landmarkValue: newLandmark, photos: photos })
+        }
+
+
+
+        // create svg content here, then pass it to addLandmarkAsync as the value of indoorLmLocImg
+        // if (typeof newLandmark.floor === 'number') {
+        //     let rectangle = serialize()
+        //     await addLandmarkAsync({ landmarkValue: newLandmark, photos: photos, indoorLmLocImg: rectangle }); // pass it in here
+        // }
+        // else {
+        //     await addLandmarkAsync({ landmarkValue: newLandmark, photos: photos });
+        // }
+
         close()
     }
 
@@ -132,7 +222,7 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({newLandmark, setNewLandma
 
     const addPhoto = (result: ImageInfo) => {
         togglePhotoSourceMenu(false)
-        const photo: LMPhoto = {id: '', image_b64: 'data:image/png;base64,' + result.base64, height: result.height, width: result.width, landmark: ''}
+        const photo: LMPhoto = { id: '', image_b64: 'data:image/png;base64,' + result.base64, height: result.height, width: result.width, landmark: '' }
         setPhotos([...photos, photo])
     }
 
@@ -246,6 +336,21 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({newLandmark, setNewLandma
                 </View> }
             </SafeAreaView>
             <PhotoPicker multiple={true} menuType='alert' photoSourceMenuOpened={photoSourceMenuOpened} onReceivedPhotoResult={result => addPhoto(result)} cancel={() => togglePhotoSourceMenu(false)} />
+
+            {/* <View style={{position:'absolute', zIndex:-1}}>
+            <ViewShot ref={capture} options={{format:'jpg'}}>
+                <Svg>
+                    {renderIndoorLandmarkPin(newLandmark)}
+                    <IndoorFloor floorNum={newLandmark.floor} />
+                    Issue rn is that upon initial rendering, RN doesnt know what "newLandmark" is
+
+                    <ImageSVG x={100} y={100} width={imageDim} height={imageDim} href={lmTypes[2]['image'] as ImageSourcePropType} />
+                    <IndoorFloor floorNum={2} />
+                </Svg> 
+            </ViewShot>
+            </View> */}
+
+
         </Modal>
     )
 }

+ 1 - 0
src/components/Profile/LoginView.tsx

@@ -95,6 +95,7 @@ const LoginView : React.FC<{navigation: BaseStackNavigationProp}> = ({navigation
                     <PrimaryButton text="Login" onPress={login}/>
                     <PrimaryButton text="Create account" onPress={goToRegistration} />
                     <View style={{width: '100%'}}>
+                        <Separator color="#E0E0E0" />
                         <GenericButton text="Legal" style={{justifyContent: 'center', alignItems: 'center', padding: 20, width: '100%', }} onPress={openLegal}/>
                         <Separator color="#E0E0E0" />
                         <Collapsible collapsed={legalCollapsed} style={{borderColor: '#E0E0E0', borderBottomWidth: 1, borderRightWidth: 1, borderLeftWidth: 1, justifyContent: 'center', alignItems: 'center', padding: 20}} >

+ 3 - 2
src/data/landmarks.ts

@@ -118,7 +118,7 @@ export const useAddLandmark = () => {
     const {sendApiRequestAsync, userId, anonUserId} = useAuth()
     const queryClient = useQueryClient();
 
-    const createLandmark = async (data: {landmarkValue: Landmark | undefined, photos?: LMPhoto[]}): Promise<Landmark | undefined> => {
+    const createLandmark = async (data: {landmarkValue: Landmark | undefined, photos?: LMPhoto[], indoorLmLocImg?: string}): Promise<Landmark | undefined> => {
         if (data.landmarkValue) {
             if (userId) {
                 data.landmarkValue.user = userId
@@ -136,7 +136,8 @@ export const useAddLandmark = () => {
                     url: `/api/landmark/`,
                     data: {
                         landmark: data.landmarkValue,
-                        photos: data.photos
+                        photos: data.photos,
+                        indoorLmLocImg: data.indoorLmLocImg
                     },
                 },
                 authorized: false,

+ 2 - 3
src/navigation/MapNavigator.tsx

@@ -42,6 +42,7 @@ const MapNavigator: React.FC = ({ }) => {
 
     const navigationState = useNavigationState(state => state)
     const [currentRoute, setCurrentRoute] = useState<string>()
+
     useEffect(() => {
         const currentRouteIndex = navigationState?.routes[0]?.state?.index
         const currentRouteName = navigationState?.routes[0]?.state?.routeNames[currentRouteIndex]
@@ -154,7 +155,7 @@ const MapNavigator: React.FC = ({ }) => {
 
             {/* Filter chips and button*/}
             {!mapState.filterVisible && currentRoute == 'Indoor' ?
-                <View style={{width:Dimensions.get("window").width*0.8 ,marginLeft:Dimensions.get("window").width*0.1, borderColor:"red" , borderWidth:0, bottom: 50, position: 'absolute', flexDirection: "row-reverse", justifyContent: 'flex-end' }}>
+                <View style={{width:Dimensions.get("window").width*0.8 ,marginLeft:Dimensions.get("window").width*0.1, borderColor:"red" , borderWidth:0, bottom: 55, 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}
@@ -179,8 +180,6 @@ const MapNavigator: React.FC = ({ }) => {
                         {mapState.minLmRating > 0 ? <Chip avatar={(<FontAwesome name="star" size={20} color='gray' style={{ textAlign: 'center', textAlignVertical: 'center' }} />)} style={{ borderWidth: 1, borderColor: 'lightgray', marginLeft: 5, marginRight: 10 }} onClose={() => mapState.setMinLmRating(0)}>Minimum rating: {mapState.minLmRating}</Chip> : null}
                     </ScrollView>
                 </View>
-                
-                
                 }
   
 

+ 1 - 0
src/utils/RequestUtils.ts

@@ -27,6 +27,7 @@ import Config from 'react-native-config'
 //export const API_URL = 'http://192.168.3.81:8000'
 // export const API_URL = 'https://staging.clicknpush.ca'
 
+// export const API_URL = 'http://192.168.3.162:8000'
 export const API_URL = 'http://192.168.3.102:8000'
 
 // export const API_URL = Config.API_URL

+ 46 - 3
yarn.lock

@@ -4882,6 +4882,13 @@
   "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
   "version" "2.2.0"
 
+"bindings@^1.5.0":
+  "integrity" "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="
+  "resolved" "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz"
+  "version" "1.5.0"
+  dependencies:
+    "file-uri-to-path" "1.0.0"
+
 "bl@^4.0.3", "bl@^4.1.0":
   "integrity" "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="
   "resolved" "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz"
@@ -8337,6 +8344,19 @@
   "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
   "version" "1.0.0"
 
+"fsevents@^1.2.7":
+  "integrity" "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw=="
+  "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz"
+  "version" "1.2.13"
+  dependencies:
+    "bindings" "^1.5.0"
+    "nan" "^2.12.1"
+
+"fsevents@^2.1.2", "fsevents@~2.3.2":
+  "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="
+  "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
+  "version" "2.3.2"
+
 "function-bind@^1.1.1":
   "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
   "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
@@ -11528,7 +11548,7 @@
     "object-assign" "^4.0.1"
     "thenify-all" "^1.0.0"
 
-"nan@^2.14.2":
+"nan@^2.12.1", "nan@^2.14.2":
   "integrity" "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
   "resolved" "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz"
   "version" "2.15.0"
@@ -13513,7 +13533,7 @@
     "kleur" "^3.0.3"
     "sisteransi" "^1.0.5"
 
-"prop-types@^15.5.8", "prop-types@^15.6.0", "prop-types@^15.6.2", "prop-types@^15.7.2", "prop-types@15.7.2":
+"prop-types@^15.5.10", "prop-types@^15.5.8", "prop-types@^15.6.0", "prop-types@^15.6.2", "prop-types@^15.7.2", "prop-types@15.7.2":
   "integrity" "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ=="
   "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
   "version" "15.7.2"
@@ -14012,6 +14032,19 @@
     "mockdate" "^3.0.2"
     "string-hash-64" "^1.0.3"
 
+"react-native-root-siblings@^4.0.0":
+  "integrity" "sha512-sdmLElNs5PDWqmZmj4/aNH4anyxreaPm61c4ZkRiR8SO/GzLg6KjAbb0e17RmMdnBdD0AIQbS38h/l55YKN4ZA=="
+  "resolved" "https://registry.npmjs.org/react-native-root-siblings/-/react-native-root-siblings-4.1.1.tgz"
+  "version" "4.1.1"
+
+"react-native-root-toast@^3.3.0":
+  "integrity" "sha512-C4Pqu+Ae7kXsYJwTvz8NshyJ9SL5YJd+/vCkvgDAxxR8AYlPFggEcTCMNARIWXuRwthLbuwcakh4z9k6qg95dg=="
+  "resolved" "https://registry.npmjs.org/react-native-root-toast/-/react-native-root-toast-3.3.0.tgz"
+  "version" "3.3.0"
+  dependencies:
+    "prop-types" "^15.5.10"
+    "react-native-root-siblings" "^4.0.0"
+
 "react-native-safe-area-context@>= 3.0.0", "react-native-safe-area-context@3.3.2":
   "integrity" "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q=="
   "resolved" "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz"
@@ -14070,6 +14103,11 @@
     "css-select" "^2.1.0"
     "css-tree" "^1.0.0-alpha.39"
 
+"react-native-toast-message@^2.1.3":
+  "integrity" "sha512-K3hHSWezWixxOZUDbxPSarEG+tPv2WcaJG4L7dvRUC1TOKNVCEKzXjsx+6ZMxllDrJ0sFczuYGqZibGpFe/ubA=="
+  "resolved" "https://registry.npmjs.org/react-native-toast-message/-/react-native-toast-message-2.1.3.tgz"
+  "version" "2.1.3"
+
 "react-native-vector-icons@*", "react-native-vector-icons@>6.0.0":
   "integrity" "sha512-sHIdBB6Y0dHaot2fMXgy5J/hhCn5YuyN7SKDNFgPzL8KA1oF2/v7mgYMavnK7LIIs2dJoGnDANKf61dsU+TZlg=="
   "resolved" "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-8.1.0.tgz"
@@ -14084,6 +14122,11 @@
     "prop-types" "^15.7.2"
     "yargs" "^16.1.1"
 
+"react-native-view-shot@3.1.2":
+  "integrity" "sha512-9u9fPtp6a52UMoZ/UCPrCjKZk8tnkI9To0Eh6yYnLKFEGkRZ7Chm6DqwDJbYJHeZrheCCopaD5oEOnRqhF4L2Q=="
+  "resolved" "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz"
+  "version" "3.1.2"
+
 "react-native-web@^0.13":
   "integrity" "sha512-WR/0ECAmwLQ2+2cL2Ur+0/swXFAtcSM0URoADJmG6D4MnY+wGc91JO8LoOTlgY0USBOY+qG/beRrjFa+RAuOiA=="
   "resolved" "https://registry.npmjs.org/react-native-web/-/react-native-web-0.13.18.tgz"
@@ -14139,7 +14182,7 @@
     "whatwg-fetch" "^3.0.0"
     "ws" "^6.1.4"
 
-"react-native@*", "react-native@^0.64.0", "react-native@>= 0.51", "react-native@>= 0.62", "react-native@>=0.42.0", "react-native@>=0.50.0", "react-native@>=0.54.0", "react-native@>=0.56", "react-native@>=0.57", "react-native@>=0.59.0", "react-native@>=0.60.0", "react-native@>=0.63.0", "react-native@>=0.63.3", "react-native@>=0.64.0-rc.0 || 0.0.0-*", "react-native@>0.57.0", "react-native@0.64.3":
+"react-native@*", "react-native@^0.64.0", "react-native@>= 0.51", "react-native@>= 0.62", "react-native@>=0.42.0", "react-native@>=0.47.0", "react-native@>=0.50.0", "react-native@>=0.54.0", "react-native@>=0.56", "react-native@>=0.57", "react-native@>=0.59.0", "react-native@>=0.60.0", "react-native@>=0.63.0", "react-native@>=0.63.3", "react-native@>=0.64.0-rc.0 || 0.0.0-*", "react-native@>0.57.0", "react-native@0.64.3":
   "integrity" "sha512-2OEU74U0Ek1/WeBzPbg6XDsCfjF/9fhrNX/5TFgEiBKd5mNc9LOZ/OlMmkb7iues/ZZ/oc51SbEfLRQdcW0fVw=="
   "resolved" "https://registry.npmjs.org/react-native/-/react-native-0.64.3.tgz"
   "version" "0.64.3"

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä