|
@@ -8,7 +8,7 @@
|
|
|
import { FontAwesome } from "@expo/vector-icons";
|
|
|
import { ImageInfo } from "expo-image-picker/build/ImagePicker.types";
|
|
|
import React, { memo, useEffect, useState, useRef } from "react";
|
|
|
-import { ActivityIndicator, Dimensions, Image, Platform, SafeAreaView, Text, TextInput, TouchableOpacity, View, ImageSourcePropType, Share, KeyboardEventName, Keyboard, StyleSheet } from 'react-native';
|
|
|
+import { ActivityIndicator, Dimensions, Image, Platform, SafeAreaView, Text, TextInput, TouchableOpacity, View, ImageSourcePropType, Share, KeyboardEventName, Keyboard, StyleSheet, KeyboardAvoidingView, Alert } from 'react-native';
|
|
|
import { ScrollView } from "react-native-gesture-handler";
|
|
|
import Modal from 'react-native-modal';
|
|
|
import Picker from 'react-native-picker-select';
|
|
@@ -54,43 +54,43 @@ export interface AddLandmarkProps {
|
|
|
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);
|
|
|
+ const [keyboardOpened, setKeyboardOpened] = useState<boolean>(false);
|
|
|
|
|
|
const addLandmarkMutation = useAddLandmark()
|
|
|
|
|
|
useEffect(() => {
|
|
|
- let eventString= Platform.OS == "android" ? 'keyboardDidShow' : Platform.OS == "ios" ? 'keyboardWillShow' : null;
|
|
|
+ let eventString = Platform.OS == "android" ? 'keyboardDidShow' : Platform.OS == "ios" ? 'keyboardWillShow' : null;
|
|
|
if (eventString) {
|
|
|
const validEventString: KeyboardEventName = eventString as KeyboardEventName;
|
|
|
|
|
|
const keyboardDidShowListener = Keyboard.addListener(
|
|
|
validEventString,
|
|
|
() => {
|
|
|
- setKeyboardOpened(true); // or some other action
|
|
|
+ setKeyboardOpened(true); // or some other action
|
|
|
}
|
|
|
- );
|
|
|
- const keyboardDidHideListener = Keyboard.addListener(
|
|
|
+ );
|
|
|
+ const keyboardDidHideListener = Keyboard.addListener(
|
|
|
validEventString,
|
|
|
() => {
|
|
|
- setKeyboardOpened(false); // or some other action
|
|
|
+ setKeyboardOpened(false); // or some other action
|
|
|
}
|
|
|
- );
|
|
|
-
|
|
|
- return () => {
|
|
|
+ );
|
|
|
+
|
|
|
+ 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 = () => {
|
|
|
+ const determineModalHeight = () => {
|
|
|
if (keyboardOpened) {
|
|
|
return Dimensions.get("window").height * .45
|
|
|
}
|
|
|
- else if (photos?.length > 0)
|
|
|
+ else if (photos?.length > 0)
|
|
|
return Dimensions.get("window").height * .9
|
|
|
else
|
|
|
return Dimensions.get("window").height * .6
|
|
@@ -119,67 +119,6 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
|
|
|
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.
|
|
|
*/
|
|
@@ -190,14 +129,14 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
|
|
|
format: "jpg",
|
|
|
quality: 1,
|
|
|
result: 'base64'
|
|
|
- })
|
|
|
+ })
|
|
|
|
|
|
- //console.log("Image is", uri.substring(0, 100))
|
|
|
+ console.log("Image is", uri.substring(0, 100))
|
|
|
await addLandmarkMutation.mutateAsync({ landmarkValue: newLandmark, photos: photos, indoorLmLocImg: uri }); // pass it in here
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
- console.error("[AddLandmarkPanel]: Creating floorplan image containing new landmark failed.", error)
|
|
|
+ console.error("Oops, snapshot failed", error)
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
@@ -245,120 +184,133 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
|
|
|
testID="addLMModal"
|
|
|
avoidKeyboard={false}
|
|
|
onBackdropPress={close}
|
|
|
- style={{flex: 0,justifyContent: "flex-end", height: '100%', margin: 0}}
|
|
|
+ style={{ flex: 0, justifyContent: "flex-end", height: '100%', margin: 0 }}
|
|
|
isVisible={visible} >
|
|
|
- <SafeAreaView style={{backgroundColor: colors.red, height: determineModalHeight()}}>
|
|
|
- {addLandmarkMutation.isIdle ?
|
|
|
- <>
|
|
|
- <View style={{
|
|
|
- justifyContent: 'space-between',
|
|
|
- alignItems: 'center',
|
|
|
- flexDirection: "row",
|
|
|
- marginBottom: 15,
|
|
|
- borderBottomWidth: 1,
|
|
|
- borderBottomColor: 'white',
|
|
|
- paddingHorizontal: 20,
|
|
|
- paddingVertical: 10}}>
|
|
|
- <Text style={{color: 'white', fontSize: 15}}>Add landmark here?</Text>
|
|
|
- <TouchOpaq
|
|
|
- func={close}
|
|
|
- name={"close"}
|
|
|
- size={25}
|
|
|
- col={"white"}
|
|
|
- />
|
|
|
-
|
|
|
- </View>
|
|
|
- <ScrollView>
|
|
|
- <View style={{paddingHorizontal: 20, paddingBottom: 20 }}>
|
|
|
- <TextInput
|
|
|
- returnKeyType="done"
|
|
|
- blurOnSubmit={true}
|
|
|
- multiline={true}
|
|
|
- style={{backgroundColor: 'white', textAlignVertical: 'top', paddingHorizontal: 10, paddingTop: 10, paddingBottom: 10, marginBottom: 20, height: 150}}
|
|
|
- placeholder="Description"
|
|
|
- onChangeText={value => setNewLandmark({...newLandmark, description: value})}>
|
|
|
- {newLandmark?.description}
|
|
|
- </TextInput>
|
|
|
- <View style={{flexDirection: 'row'}}>
|
|
|
- <Picker
|
|
|
- style={{
|
|
|
- inputIOS: {color: 'white'},
|
|
|
- inputAndroid: {color: 'white'},
|
|
|
- viewContainer: {marginVertical: 5, flex: 1}, placeholder: {color: 'white'}}}
|
|
|
- textInputProps={{placeholderTextColor: 'white', selectionColor: 'white'}}
|
|
|
- Icon={() => <FontAwesome name="chevron-down" color='white' size={20} />}
|
|
|
- placeholder={{label: "Select a landmark type...", value: 0}}
|
|
|
- value={newLandmark?.landmark_type}
|
|
|
- onValueChange={(value) => {
|
|
|
- if (value) {
|
|
|
- setNewLandmark({...newLandmark, landmark_type: value, title: lmTypes[value].label})
|
|
|
- }
|
|
|
- else {
|
|
|
- setNewLandmark({...newLandmark, landmark_type: undefined, title: 'no title'})
|
|
|
- }
|
|
|
- }}
|
|
|
- useNativeAndroidPickerStyle={true}
|
|
|
- items={Object.keys(lmTypes)?.map(icon => {
|
|
|
- return (
|
|
|
- {label: lmTypes[parseInt(icon)]?.label.toUpperCase(), value: icon, key: icon}
|
|
|
- )})}
|
|
|
+
|
|
|
+ <KeyboardAvoidingView
|
|
|
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
|
+ enabled={photos?.length > 0}
|
|
|
+ >
|
|
|
+ {console.log("state of keyboard is " + keyboardOpened)}
|
|
|
+ <SafeAreaView style={{ backgroundColor: colors.red, height: determineModalHeight(), }}>
|
|
|
+ {addLandmarkMutation.isIdle ?
|
|
|
+ <>
|
|
|
+ <View style={{
|
|
|
+ justifyContent: 'space-between',
|
|
|
+ alignItems: 'center',
|
|
|
+ flexDirection: "row",
|
|
|
+ marginBottom: 15,
|
|
|
+ borderBottomWidth: 1,
|
|
|
+ borderBottomColor: 'white',
|
|
|
+ paddingHorizontal: 20,
|
|
|
+ paddingVertical: 10,
|
|
|
+ }}
|
|
|
+ onTouchStart={() => Keyboard.dismiss()}
|
|
|
+ >
|
|
|
+ <Text style={{ color: 'white', fontSize: 15 }}>Add landmark here?</Text>
|
|
|
+ <TouchOpaq
|
|
|
+ func={close}
|
|
|
+ name={"close"}
|
|
|
+ size={25}
|
|
|
+ col={"white"}
|
|
|
/>
|
|
|
- {newLandmark?.landmark_type ? <Image style={{marginLeft: 20}} source={lmTypes[newLandmark.landmark_type].image}/>
|
|
|
- : null}
|
|
|
+
|
|
|
</View>
|
|
|
- </View>
|
|
|
- {newLandmark?.landmark_type ?
|
|
|
- <View style={{justifyContent: 'flex-end', flexDirection: 'row', paddingHorizontal: 20, marginTop: 5}}>
|
|
|
- {newLandmark.description && newLandmark.title ?
|
|
|
- <View style={{flexDirection: 'row' }}>
|
|
|
- <TouchableOpacity onPress={async () => await submit()}><Text style={{color: 'white', marginRight: 25}}>Add</Text></TouchableOpacity>
|
|
|
- <TouchableOpacity onPress={close}><Text style={{color: 'white', marginRight: 25}}>Cancel</Text></TouchableOpacity>
|
|
|
- {photos.length == 0 ? <TouchableOpacity onPress={() => togglePhotoSourceMenu(true)}><Text style={{color: 'white'}}>Include photos</Text></TouchableOpacity> : null }
|
|
|
- </View> : null}
|
|
|
- </View> : null}
|
|
|
- {photos?.length > 0 && !keyboardOpened ?
|
|
|
- <View>
|
|
|
- <ScrollView style={{borderTopWidth: 1, borderColor: 'lightgray', paddingTop: 20, marginHorizontal: 20, flexDirection: 'row', marginBottom: 5, marginTop: 30}} horizontal={true}>
|
|
|
- {photos.map((photo, i) => {
|
|
|
- return (
|
|
|
- <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={() => deletePhoto(i)} />
|
|
|
- <Image style={{borderWidth: 1, alignSelf: 'center', height: 200, width: 200 * photo.width / photo.height}} source={{uri: photo.image_b64}} />
|
|
|
- </View>
|
|
|
- )
|
|
|
- })}
|
|
|
- {photos.length < 5 ? <IconButton style={{alignSelf: 'center', padding: 10, opacity: .5, marginLeft: 10}} color='white' size={30} icon="plus" onPress={() => togglePhotoSourceMenu(true)} /> : null}
|
|
|
+ <ScrollView>
|
|
|
+ <View style={{ paddingHorizontal: 20, paddingBottom: 20 }}>
|
|
|
+ <TextInput
|
|
|
+ returnKeyType="done"
|
|
|
+ blurOnSubmit={true}
|
|
|
+ multiline={true}
|
|
|
+ style={{ backgroundColor: 'white', textAlignVertical: 'top', paddingHorizontal: 10, paddingTop: 10, paddingBottom: 10, marginBottom: 20, height: 150 }}
|
|
|
+ placeholder="Description"
|
|
|
+ onChangeText={value => setNewLandmark({ ...newLandmark, description: value })}>
|
|
|
+ {newLandmark?.description}
|
|
|
+ </TextInput>
|
|
|
+ <View style={{ flexDirection: 'row' }}>
|
|
|
+ <Picker
|
|
|
+ style={{
|
|
|
+ inputIOS: { color: 'white' },
|
|
|
+ inputAndroid: { color: 'white' },
|
|
|
+ viewContainer: { marginVertical: 5, flex: 1 }, placeholder: { color: 'white' }
|
|
|
+ }}
|
|
|
+ textInputProps={{ placeholderTextColor: 'white', selectionColor: 'white' }}
|
|
|
+ Icon={() => <FontAwesome name="chevron-down" color='white' size={20} />}
|
|
|
+ placeholder={{ label: "Select a landmark type...", value: 0 }}
|
|
|
+ value={newLandmark?.landmark_type}
|
|
|
+ onValueChange={(value) => {
|
|
|
+ if (value) {
|
|
|
+ setNewLandmark({ ...newLandmark, landmark_type: value, title: lmTypes[value].label })
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ setNewLandmark({ ...newLandmark, landmark_type: undefined, title: 'no title' })
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ useNativeAndroidPickerStyle={true}
|
|
|
+ items={Object.keys(lmTypes)?.map(icon => {
|
|
|
+ return (
|
|
|
+ { label: lmTypes[parseInt(icon)]?.label.toUpperCase(), value: icon, key: icon }
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ />
|
|
|
+ {newLandmark?.landmark_type ? <Image style={{ marginLeft: 20 }} source={lmTypes[newLandmark.landmark_type].image} />
|
|
|
+ : null}
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ {newLandmark?.landmark_type ?
|
|
|
+ <View style={{ justifyContent: 'flex-end', flexDirection: 'row', paddingHorizontal: 20, marginTop: 5 }}>
|
|
|
+ {newLandmark.description && newLandmark.title ?
|
|
|
+ <View style={{ flexDirection: 'row' }}>
|
|
|
+ <TouchableOpacity onPress={async () => await submit()}><Text style={{ color: 'white', marginRight: 25 }}>Add</Text></TouchableOpacity>
|
|
|
+ <TouchableOpacity onPress={close}><Text style={{ color: 'white', marginRight: 25 }}>Cancel</Text></TouchableOpacity>
|
|
|
+ {photos.length == 0 ? <TouchableOpacity onPress={() => togglePhotoSourceMenu(true)}><Text style={{ color: 'white' }}>Include photos</Text></TouchableOpacity> : null}
|
|
|
+ </View> : null}
|
|
|
+ </View> : null}
|
|
|
+ {photos?.length > 0 && !keyboardOpened ?
|
|
|
+ <View>
|
|
|
+ <ScrollView style={{ borderTopWidth: 1, borderColor: 'lightgray', paddingTop: 20, marginHorizontal: 20, flexDirection: 'row', marginBottom: 5, marginTop: 30 }} horizontal={true}>
|
|
|
+ {photos.map((photo, i) => {
|
|
|
+ return (
|
|
|
+ <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={() => deletePhoto(i)} />
|
|
|
+ <Image style={{ borderWidth: 1, alignSelf: 'center', height: 200, width: 200 * photo.width / photo.height }} source={{ uri: photo.image_b64 }} />
|
|
|
+ </View>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ {photos.length < 5 ? <IconButton style={{ alignSelf: 'center', padding: 10, opacity: .5, marginLeft: 10 }} color='white' size={30} icon="plus" onPress={() => togglePhotoSourceMenu(true)} /> : null}
|
|
|
+ </ScrollView>
|
|
|
+ </View> : null}
|
|
|
</ScrollView>
|
|
|
- </View> : null}
|
|
|
- </ScrollView>
|
|
|
- </> :
|
|
|
- <View style={{height: '100%', justifyContent: "space-evenly", alignItems: "center"}}>
|
|
|
- <Text style={{color: 'white', fontSize: 20}}>{
|
|
|
- addLandmarkMutation.isLoading ? 'Uploading landmark...' :
|
|
|
- addLandmarkMutation.isError ? 'Something went wrong when trying to upload the landmark.' : null }
|
|
|
- </Text>
|
|
|
- {
|
|
|
- addLandmarkMutation.isLoading ? <ActivityIndicator color='white' size="large"/> :
|
|
|
- addLandmarkMutation.isError ? <SecondaryButton text="Okay" onPress={close}/> : null
|
|
|
+ </> :
|
|
|
+ <View style={{ height: '100%', justifyContent: "space-evenly", alignItems: "center" }}>
|
|
|
+ <Text style={{ color: 'white', fontSize: 20 }}>{
|
|
|
+ addLandmarkMutation.isLoading ? 'Uploading landmark...' :
|
|
|
+ addLandmarkMutation.isError ? 'Something went wrong when trying to upload the landmark.' : null}
|
|
|
+ </Text>
|
|
|
+ {
|
|
|
+ addLandmarkMutation.isLoading ? <ActivityIndicator color='white' size="large" /> :
|
|
|
+ addLandmarkMutation.isError ? <SecondaryButton text="Okay" onPress={close} /> : null
|
|
|
+ }
|
|
|
+ </View>}
|
|
|
+ </SafeAreaView>
|
|
|
+ <PhotoPicker multiple={true} menuType='alert' photoSourceMenuOpened={photoSourceMenuOpened} onReceivedPhotoResult={result => addPhoto(result)} cancel={() => togglePhotoSourceMenu(false)} />
|
|
|
+
|
|
|
+
|
|
|
+ <ViewShot style={{ width: imgWidth + 20, height: imgHeight + 20, position: 'absolute', right: -2000 }} ref={capture} >
|
|
|
+ {/* {console.log("newLandmark is " + newLandmark)} */}
|
|
|
+ {newLandmark == null || newLandmark.floor == null || newLandmark.landmark_type == null ? <></> :
|
|
|
+ <View style={styles.container}>
|
|
|
+ <Svg>
|
|
|
+ {console.log("x coord is " + newLandmark.longitude + " and y coord is " + newLandmark.latitude)}
|
|
|
+ <ImageSVG x={newLandmark.longitude * imgWidth - 3} y={newLandmark.latitude * imgHeight - 3} width={imageDim} height={imageDim} href={lmTypes[newLandmark.landmark_type]['image'] as ImageSourcePropType} />
|
|
|
+ <IndoorFloor floorNum={newLandmark.floor} />
|
|
|
+ </Svg>
|
|
|
+ </View>
|
|
|
}
|
|
|
- </View> }
|
|
|
- </SafeAreaView>
|
|
|
- <PhotoPicker multiple={true} menuType='alert' photoSourceMenuOpened={photoSourceMenuOpened} onReceivedPhotoResult={result => addPhoto(result)} cancel={() => togglePhotoSourceMenu(false)} />
|
|
|
-
|
|
|
-
|
|
|
- <ViewShot style={{ width: imgWidth+20, height: imgHeight+20, position: 'absolute', right: -2000 }} ref={capture} >
|
|
|
- {/* {console.log("newLandmark is " + newLandmark)} */}
|
|
|
- {newLandmark == null || newLandmark.floor == null || newLandmark.landmark_type == null ? <></> :
|
|
|
- <View style={styles.container}>
|
|
|
- <Svg>
|
|
|
- {/* {console.log("x coord is " + newLandmark.longitude + " and y coord is " + newLandmark.latitude)} */}
|
|
|
- <ImageSVG x={newLandmark.longitude*imgWidth-3} y={newLandmark.latitude*imgHeight-3} width={imageDim} height={imageDim} href={lmTypes[newLandmark.landmark_type]['image'] as ImageSourcePropType} />
|
|
|
- <IndoorFloor floorNum={newLandmark.floor} />
|
|
|
- </Svg>
|
|
|
- </View>
|
|
|
- }
|
|
|
- </ViewShot>
|
|
|
+ </ViewShot>
|
|
|
+ </KeyboardAvoidingView>
|
|
|
</Modal>
|
|
|
+
|
|
|
)
|
|
|
}
|
|
|
|
|
@@ -370,7 +322,7 @@ const styles = StyleSheet.create({
|
|
|
maxWidth: "100%",
|
|
|
maxHeight: "100%",
|
|
|
backgroundColor: "white",
|
|
|
- padding:10
|
|
|
+ padding: 10
|
|
|
}
|
|
|
})
|
|
|
|