|
@@ -7,66 +7,94 @@
|
|
|
|
|
|
import FontAwesome from "@expo/vector-icons/build/FontAwesome";
|
|
import FontAwesome from "@expo/vector-icons/build/FontAwesome";
|
|
import { observer } from "mobx-react-lite";
|
|
import { observer } from "mobx-react-lite";
|
|
-import React, { memo } from "react";
|
|
|
|
-import { ActivityIndicator, Image, ImageSourcePropType, Keyboard, KeyboardAvoidingView, Platform, SafeAreaView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
|
|
|
|
+import React, { memo, useEffect, useRef, useState } from "react";
|
|
|
|
+import { ActivityIndicator, Dimensions, Image, ImageSourcePropType, Keyboard, KeyboardAvoidingView, KeyboardEventName, Platform, SafeAreaView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
import { ScrollView } from "react-native-gesture-handler";
|
|
import { ScrollView } from "react-native-gesture-handler";
|
|
import Modal from 'react-native-modal';
|
|
import Modal from 'react-native-modal';
|
|
import { Image as ImageSVG, Svg } from 'react-native-svg';
|
|
import { Image as ImageSVG, Svg } from 'react-native-svg';
|
|
import ViewShot from "react-native-view-shot";
|
|
import ViewShot from "react-native-view-shot";
|
|
|
|
+import { store } from "../../../../main-store";
|
|
import { colors, lmTypes } from "../../../../utils/GlobalUtils";
|
|
import { colors, lmTypes } from "../../../../utils/GlobalUtils";
|
|
import { IconButton, SecondaryButton } from "../../../Buttons";
|
|
import { IconButton, SecondaryButton } from "../../../Buttons";
|
|
import LandmarkTypePicker from "../../../LandmarkTypePicker";
|
|
import LandmarkTypePicker from "../../../LandmarkTypePicker";
|
|
import { PhotoPicker } from "../../../PhotoPicker";
|
|
import { PhotoPicker } from "../../../PhotoPicker";
|
|
import { Separator } from "../../../Separator";
|
|
import { Separator } from "../../../Separator";
|
|
import IndoorFloor from "../../indoor/indoor-floor";
|
|
import IndoorFloor from "../../indoor/indoor-floor";
|
|
-import TouchOpaq from "../LandmarkDetailsPanel/TouchOpaq";
|
|
|
|
-import { useAddLandmarkPanel } from "./add-landmark-panel.logic";
|
|
|
|
-import { addLandmarkStore } from "./add-landmark-panel.store";
|
|
|
|
|
|
+import TouchOpaq from "../selected-landmark-panel/TouchOpaq";
|
|
|
|
+import { useAddLandmarkApi } from "./add-landmark-panel.api";
|
|
|
|
|
|
const imgWidth = 346;
|
|
const imgWidth = 346;
|
|
const imgHeight = 448;
|
|
const imgHeight = 448;
|
|
const imageDim = 25;
|
|
const imageDim = 25;
|
|
|
|
|
|
-/**
|
|
|
|
- * Props for the {@link AddLandmarkPanel} component.
|
|
|
|
- */
|
|
|
|
-export interface AddLandmarkProps {
|
|
|
|
- /**
|
|
|
|
- * Whether the landmark is being added at the current users location
|
|
|
|
- */
|
|
|
|
- landmarkAtCurrentLocation?: boolean;
|
|
|
|
- /**
|
|
|
|
- * A call back that toggles the visibility state of the {@link AddLandmarkPanel} modal. Passed down from {@link AddLandmarkPanel}.
|
|
|
|
- */
|
|
|
|
- setVisible: (state: boolean) => void;
|
|
|
|
- visible: boolean;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Component that renders a form for adding a new {@link Landmark}. Contained within a [react-native-modal]{@link https://github.com/react-native-modal/react-native-modal}.
|
|
* Component that renders a form for adding a new {@link Landmark}. Contained within a [react-native-modal]{@link https://github.com/react-native-modal/react-native-modal}.
|
|
* @component
|
|
* @component
|
|
* @category Map
|
|
* @category Map
|
|
*/
|
|
*/
|
|
-const AddLandmarkPanel: React.FC<AddLandmarkProps> = observer(() => {
|
|
|
|
- const state = useAddLandmarkPanel()
|
|
|
|
|
|
+const AddLandmarkPanel: React.FC = observer(() => {
|
|
|
|
+ const {lmLocationScreenCap, addLandmarkMutation, submit} = useAddLandmarkApi();
|
|
|
|
+ const [keyboardOpened, setKeyboardOpened] = useState<boolean>(false);
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ let eventString = Platform.OS == "android" ? 'keyboardDidShow' : Platform.OS == "ios" ? 'keyboardWillShow' : null;
|
|
|
|
+ if (eventString) {
|
|
|
|
+ const validEventString: KeyboardEventName = eventString as KeyboardEventName;
|
|
|
|
+
|
|
|
|
+ const keyboardDidShowListener = Keyboard.addListener(
|
|
|
|
+ validEventString,
|
|
|
|
+ () => {
|
|
|
|
+ console.log('keyboard open')
|
|
|
|
+ setKeyboardOpened(true); // or some other action
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ const keyboardDidHideListener = Keyboard.addListener(
|
|
|
|
+ 'keyboardDidHide',
|
|
|
|
+ () => {
|
|
|
|
+ console.log('keyboard close')
|
|
|
|
+ setKeyboardOpened(false); // or some other action
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ return () => {
|
|
|
|
+ keyboardDidHideListener.remove();
|
|
|
|
+ keyboardDidShowListener.remove();
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ }, []);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Returns a height for the modal depending on if an image is maximzed, if the keyboard is opened, and if the current landmark has photos associated with it
|
|
|
|
+ */
|
|
|
|
+ const determineModalHeight = () => {
|
|
|
|
+ if (keyboardOpened) {
|
|
|
|
+ return Dimensions.get("window").height * .45
|
|
|
|
+ }
|
|
|
|
+ else if (store.addLm.photos?.length > 0) {
|
|
|
|
+ return Dimensions.get("window").height * .9
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ return Dimensions.get("window").height * .8
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
return (
|
|
return (
|
|
<Modal
|
|
<Modal
|
|
useNativeDriver={true}
|
|
useNativeDriver={true}
|
|
useNativeDriverForBackdrop={true}
|
|
useNativeDriverForBackdrop={true}
|
|
testID="addLMModal"
|
|
testID="addLMModal"
|
|
- avoidKeyboard={addLandmarkStore.photos?.length > 0}
|
|
|
|
- onBackdropPress={close}
|
|
|
|
|
|
+ avoidKeyboard={store.addLm.photos?.length > 0}
|
|
|
|
+ onBackdropPress={store.addLm.close}
|
|
style={{ flex: 1, justifyContent: "flex-end", height: '100%', margin: 0}}
|
|
style={{ flex: 1, justifyContent: "flex-end", height: '100%', margin: 0}}
|
|
- isVisible={addLandmarkStore.panelVisible} >
|
|
|
|
|
|
+ isVisible={store.addLm.panelVisible} >
|
|
|
|
|
|
<KeyboardAvoidingView
|
|
<KeyboardAvoidingView
|
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
- enabled={addLandmarkStore.photos?.length > 0}
|
|
|
|
|
|
+ enabled={store.addLm.photos?.length > 0}
|
|
style={{ flex: 1, justifyContent: "flex-end"}}
|
|
style={{ flex: 1, justifyContent: "flex-end"}}
|
|
>
|
|
>
|
|
- <SafeAreaView style={{ backgroundColor: colors.red, height: state.determineModalHeight(), justifyContent: 'flex-end'}}>
|
|
|
|
- {state.addLandmarkMutation.isIdle ?
|
|
|
|
|
|
+ <SafeAreaView style={{ backgroundColor: colors.red, height: determineModalHeight(), justifyContent: 'flex-end'}}>
|
|
|
|
+ {addLandmarkMutation.isIdle ?
|
|
<>
|
|
<>
|
|
<View style={{
|
|
<View style={{
|
|
justifyContent: 'space-between',
|
|
justifyContent: 'space-between',
|
|
@@ -82,7 +110,7 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = observer(() => {
|
|
>
|
|
>
|
|
<Text style={{ color: 'white', fontSize: 15 }}>Add landmark here?</Text>
|
|
<Text style={{ color: 'white', fontSize: 15 }}>Add landmark here?</Text>
|
|
<TouchOpaq
|
|
<TouchOpaq
|
|
- func={close}
|
|
|
|
|
|
+ func={store.addLm.close}
|
|
name={"close"}
|
|
name={"close"}
|
|
size={25}
|
|
size={25}
|
|
col={"white"}
|
|
col={"white"}
|
|
@@ -97,19 +125,19 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = observer(() => {
|
|
multiline={true}
|
|
multiline={true}
|
|
style={{ backgroundColor: 'white', textAlignVertical: 'top', paddingHorizontal: 10, paddingTop: 10, paddingBottom: 10, marginBottom: 20, height: 150 }}
|
|
style={{ backgroundColor: 'white', textAlignVertical: 'top', paddingHorizontal: 10, paddingTop: 10, paddingBottom: 10, marginBottom: 20, height: 150 }}
|
|
placeholder="Description"
|
|
placeholder="Description"
|
|
- onChangeText={value => addLandmarkStore.updatePendingLandmark({ ...addLandmarkStore.pendingLandmark, description: value })}>
|
|
|
|
- {addLandmarkStore.pendingLandmark?.description}
|
|
|
|
|
|
+ onChangeText={value => store.addLm.updatePendingLandmark({ ...store.addLm.pendingLandmark, description: value })}>
|
|
|
|
+ {store.addLm.pendingLandmark?.description}
|
|
</TextInput>
|
|
</TextInput>
|
|
<View style={{ flexDirection: 'row' }}>
|
|
<View style={{ flexDirection: 'row' }}>
|
|
<LandmarkTypePicker
|
|
<LandmarkTypePicker
|
|
placeholder={{ label: "Select a landmark type...", value: 0 }}
|
|
placeholder={{ label: "Select a landmark type...", value: 0 }}
|
|
- value={addLandmarkStore.pendingLandmark?.landmark_type}
|
|
|
|
|
|
+ value={store.addLm.pendingLandmark?.landmark_type}
|
|
onValueChange={(value) => {
|
|
onValueChange={(value) => {
|
|
if (value) {
|
|
if (value) {
|
|
- addLandmarkStore.updatePendingLandmark({ ...addLandmarkStore.pendingLandmark, landmark_type: value, title: lmTypes[value].label })
|
|
|
|
|
|
+ store.addLm.updatePendingLandmark({ ...store.addLm.pendingLandmark, landmark_type: value, title: lmTypes[value].label })
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
- addLandmarkStore.updatePendingLandmark({ ...addLandmarkStore.pendingLandmark, landmark_type: undefined, title: 'no title' })
|
|
|
|
|
|
+ store.addLm.updatePendingLandmark({ ...store.addLm.pendingLandmark, landmark_type: undefined, title: 'no title' })
|
|
}
|
|
}
|
|
}}
|
|
}}
|
|
items={Object.keys(lmTypes)?.map(icon => {
|
|
items={Object.keys(lmTypes)?.map(icon => {
|
|
@@ -117,60 +145,60 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = observer(() => {
|
|
{ label: lmTypes[parseInt(icon)]?.label.toUpperCase(), value: icon, key: icon }
|
|
{ label: lmTypes[parseInt(icon)]?.label.toUpperCase(), value: icon, key: icon }
|
|
)
|
|
)
|
|
})}/>
|
|
})}/>
|
|
- {addLandmarkStore.pendingLandmark?.landmark_type ? <Image source={lmTypes[addLandmarkStore.pendingLandmark.landmark_type].image} />
|
|
|
|
|
|
+ {store.addLm.pendingLandmark?.landmark_type ? <Image source={lmTypes[store.addLm.pendingLandmark.landmark_type].image} />
|
|
: null}
|
|
: null}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
<View style={{ justifyContent: 'flex-end', flexDirection: 'row', paddingHorizontal: 20, marginTop: 5 }}>
|
|
<View style={{ justifyContent: 'flex-end', flexDirection: 'row', paddingHorizontal: 20, marginTop: 5 }}>
|
|
<View style={{ flexDirection: 'row' }}>
|
|
<View style={{ flexDirection: 'row' }}>
|
|
- <TouchableOpacity onPress={async () => await state.submit()}><Text style={{ color: 'white', marginRight: 25, opacity: addLandmarkStore.landmarkReady ? 1 : .5 }}>Add</Text></TouchableOpacity>
|
|
|
|
- <TouchableOpacity onPress={close}><Text style={{ color: 'white', marginRight: 25 }}>Cancel</Text></TouchableOpacity>
|
|
|
|
|
|
+ <TouchableOpacity onPress={async () => await submit()}><Text style={{ color: 'white', marginRight: 25, opacity: store.addLm.landmarkReady ? 1 : .5 }}>Add</Text></TouchableOpacity>
|
|
|
|
+ <TouchableOpacity onPress={store.addLm.close}><Text style={{ color: 'white', marginRight: 25 }}>Cancel</Text></TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
- {addLandmarkStore.photos?.length > 0 && !state.keyboardOpened ?
|
|
|
|
|
|
+ {store.addLm.photos?.length > 0 && !keyboardOpened ?
|
|
<>
|
|
<>
|
|
<ScrollView style={{ borderTopWidth: 1, borderColor: 'lightgray', paddingTop: 20, marginHorizontal: 20, flexDirection: 'row', marginBottom: 5, marginTop: 30 }} horizontal={true}>
|
|
<ScrollView style={{ borderTopWidth: 1, borderColor: 'lightgray', paddingTop: 20, marginHorizontal: 20, flexDirection: 'row', marginBottom: 5, marginTop: 30 }} horizontal={true}>
|
|
- {addLandmarkStore.photos.map((photo, i) => {
|
|
|
|
|
|
+ {store.addLm.photos.map((photo, i) => {
|
|
return (
|
|
return (
|
|
<View key={i} style={{ marginHorizontal: 1, padding: 15 }}>
|
|
<View key={i} style={{ marginHorizontal: 1, padding: 15 }}>
|
|
- <IconButton style={{ position: 'absolute', top: 0, right: 0, zIndex: 10, }} icon="times-circle" color="lightgray" size={20} onPress={() => addLandmarkStore.deletePhoto(i)} />
|
|
|
|
|
|
+ <IconButton style={{ position: 'absolute', top: 0, right: 0, zIndex: 10, }} icon="times-circle" color="lightgray" size={20} onPress={() => store.addLm.deletePhoto(i)} />
|
|
<Image style={{ borderWidth: 1, alignSelf: 'center', height: 200, width: 200 * photo.width / photo.height }} source={{ uri: photo.image_b64 }} />
|
|
<Image style={{ borderWidth: 1, alignSelf: 'center', height: 200, width: 200 * photo.width / photo.height }} source={{ uri: photo.image_b64 }} />
|
|
</View>
|
|
</View>
|
|
)
|
|
)
|
|
})}
|
|
})}
|
|
- {addLandmarkStore.photos.length < 5 ? <IconButton style={{ alignSelf: 'center', padding: 10, opacity: .5, marginLeft: 10 }} color='white' size={30} icon="plus" onPress={() => state.togglePhotoSourceMenu(true)} /> : null}
|
|
|
|
|
|
+ {store.addLm.photos.length < 5 ? <IconButton style={{ alignSelf: 'center', padding: 10, opacity: .5, marginLeft: 10 }} color='white' size={30} icon="plus" onPress={() => store.addLm.togglePhotoSourceMenu(true)} /> : null}
|
|
</ScrollView>
|
|
</ScrollView>
|
|
</> : null}
|
|
</> : null}
|
|
|
|
+ {store.addLm.photos?.length == 0 && !keyboardOpened ?
|
|
|
|
+ <>
|
|
|
|
+ <Separator color="white" style={{marginHorizontal: 20, marginTop: 20}} />
|
|
|
|
+ <TouchableOpacity style={{height: '30%', alignItems: 'center', justifyContent: 'center', marginBottom: 20}} onPress={() => {store.addLm.togglePhotoSourceMenu(true)}}>
|
|
|
|
+ <Text style={{fontSize: 20, marginBottom: 10, color: 'white'}}>Add photo of landmark</Text>
|
|
|
|
+ <FontAwesome name="plus" size={30} color='white' />
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
+ </>: null}
|
|
</ScrollView>
|
|
</ScrollView>
|
|
- {addLandmarkStore.photos?.length == 0 && !state.keyboardOpened ?
|
|
|
|
- <>
|
|
|
|
- <Separator color="white" style={{marginHorizontal: 20}} />
|
|
|
|
- <TouchableOpacity style={{height: '30%', alignItems: 'center', justifyContent: 'center', marginBottom: 20}} onPress={() => {state.togglePhotoSourceMenu(true)}}>
|
|
|
|
- <Text style={{fontSize: 20, marginBottom: 10, color: 'white'}}>Add photo of landmark</Text>
|
|
|
|
- <FontAwesome name="plus" size={30} color='white' />
|
|
|
|
- </TouchableOpacity>
|
|
|
|
- </>: null}
|
|
|
|
</> :
|
|
</> :
|
|
<View style={{ height: '100%', justifyContent: "space-evenly", alignItems: "center" }}>
|
|
<View style={{ height: '100%', justifyContent: "space-evenly", alignItems: "center" }}>
|
|
<Text style={{ color: 'white', fontSize: 20 }}>{
|
|
<Text style={{ color: 'white', fontSize: 20 }}>{
|
|
- state.addLandmarkMutation.isLoading ? 'Uploading landmark...' :
|
|
|
|
- state.addLandmarkMutation.isError ? 'Something went wrong when trying to upload the landmark.' : null}
|
|
|
|
|
|
+ addLandmarkMutation.isLoading ? 'Uploading landmark...' :
|
|
|
|
+ addLandmarkMutation.isError ? 'Something went wrong when trying to upload the landmark.' : null}
|
|
</Text>
|
|
</Text>
|
|
{
|
|
{
|
|
- state.addLandmarkMutation.isLoading ? <ActivityIndicator color='white' size="large" /> :
|
|
|
|
- state.addLandmarkMutation.isError ? <SecondaryButton text="Okay" onPress={close} /> : null
|
|
|
|
|
|
+ addLandmarkMutation.isLoading ? <ActivityIndicator color='white' size="large" /> :
|
|
|
|
+ addLandmarkMutation.isError ? <SecondaryButton text="Okay" onPress={store.addLm.close} /> : null
|
|
}
|
|
}
|
|
</View>}
|
|
</View>}
|
|
</SafeAreaView>
|
|
</SafeAreaView>
|
|
- <PhotoPicker multiple={true} menuType='alert' photoSourceMenuOpened={state.photoSourceMenuOpened} onReceivedPhotoResult={result => addLandmarkStore.addPhoto(result)} cancel={() => state.togglePhotoSourceMenu(false)} />
|
|
|
|
|
|
+ <PhotoPicker multiple={true} menuType='alert' photoSourceMenuOpened={store.addLm.photoSourceMenuVisible} onReceivedPhotoResult={result => store.addLm.addPhoto(result)} cancel={() => store.addLm.togglePhotoSourceMenu(false)} />
|
|
|
|
|
|
- <ViewShot style={{ width: imgWidth + 20, height: imgHeight + 20, position: 'absolute', right: -2000 }} ref={state.capture} >
|
|
|
|
|
|
+ <ViewShot style={{ width: imgWidth + 20, height: imgHeight + 20, position: 'absolute', right: -2000 }} ref={lmLocationScreenCap} >
|
|
{/* {console.log("newLandmark is " + newLandmark)} */}
|
|
{/* {console.log("newLandmark is " + newLandmark)} */}
|
|
- {addLandmarkStore.pendingLandmark == null || addLandmarkStore.pendingLandmark.floor == null || addLandmarkStore.pendingLandmark.landmark_type == null ? <></> :
|
|
|
|
|
|
+ {store.addLm.pendingLandmark == null || store.addLm.pendingLandmark.floor == null || store.addLm.pendingLandmark.landmark_type == null ? <></> :
|
|
<View style={styles.container}>
|
|
<View style={styles.container}>
|
|
<Svg>
|
|
<Svg>
|
|
- <IndoorFloor floorNum={addLandmarkStore.pendingLandmark.floor} />
|
|
|
|
- <ImageSVG x={addLandmarkStore.pendingLandmark.longitude * imgWidth - 3} y={addLandmarkStore.pendingLandmark.latitude * imgHeight - 3} width={imageDim} height={imageDim} href={lmTypes[addLandmarkStore.pendingLandmark.landmark_type]['image'] as ImageSourcePropType} />
|
|
|
|
|
|
+ <IndoorFloor floorNum={store.addLm.pendingLandmark.floor} />
|
|
|
|
+ <ImageSVG x={store.addLm.pendingLandmark.longitude * imgWidth - 3} y={store.addLm.pendingLandmark.latitude * imgHeight - 3} width={imageDim} height={imageDim} href={lmTypes[store.addLm.pendingLandmark.landmark_type]['image'] as ImageSourcePropType} />
|
|
</Svg>
|
|
</Svg>
|
|
</View>
|
|
</View>
|
|
}
|
|
}
|