LandmarkForm.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import React, { useEffect, useRef, useState } from 'react';
  2. import { ScrollView, View, Text, StyleSheet, TouchableOpacity, Dimensions, Image, Alert} from 'react-native';
  3. import { TextInput } from 'react-native-paper';
  4. import { Controller, useForm } from 'react-hook-form';
  5. import { useMockState } from '../contexts/MockContext';
  6. import Modal from 'react-native-modalbox';
  7. import { Icons } from '../globals';
  8. import Icon from 'react-native-vector-icons/FontAwesome';
  9. import { v4 as uuidv4 } from 'uuid';
  10. import { useMemo } from 'react/cjs/react.production.min';
  11. const editTheme = {
  12. colors: {
  13. primary: 'black',
  14. text: 'black'
  15. }
  16. }
  17. const LandmarkForm = ({navigation}) => {
  18. const {dispatch, state} = useMockState();
  19. const { control, isDirty, handleSubmit, setValue, formState } = useForm({
  20. mode: 'all'
  21. });
  22. const [selectedIcon, setIcon] = useState(state.selectedLandmark.icon);
  23. const iconSelector = useRef();
  24. useEffect(() => {
  25. setValue('title', state.selectedLandmark.title);
  26. setValue('desc', state.selectedLandmark.desc);
  27. setIcon(state.selectedLandmark.icon)
  28. }, [state]);
  29. const saveLandmark = (formData) => {
  30. let currentLandmark = state.landmarks.filter(l => l.id === state.selectedLandmark.id)[0];
  31. console.log(currentLandmark)
  32. if (currentLandmark != null) {
  33. // we're updating an existing landmark
  34. dispatch({type: "UPDATE_LANDMARKS", payload: state.landmarks.map(l => {
  35. if (l.id === state.selectedLandmark.id) {
  36. currentLandmark = {...l, title: formData.title, desc: formData.desc, icon: selectedIcon};
  37. dispatch({type: "UPDATE_SELECTED_LANDMARK", payload: currentLandmark});
  38. return currentLandmark;
  39. }
  40. return l;
  41. })});
  42. console.log('here')
  43. navigation.navigate('Map');
  44. }
  45. else {
  46. // we're adding a new landmark
  47. const newLandmark = {
  48. id: uuidv4(),
  49. postedBy: 'cdmoss',
  50. dateAdded: Date.now(),
  51. title: formData.title,
  52. desc: formData.desc,
  53. icon: selectedIcon,
  54. longitude: state.selectedLandmark.longitude,
  55. latitude: state.selectedLandmark.latitude,
  56. comments: []
  57. };
  58. dispatch({type: "UPDATE_LANDMARKS", payload: [...state.landmarks, newLandmark]})
  59. navigation.navigate('Map');
  60. }
  61. }
  62. const openSelector = () => {
  63. iconSelector.current.open();
  64. }
  65. const updateIcon = (icon) => {
  66. setIcon(icon);
  67. iconSelector.current.close();
  68. }
  69. const closeModal = () => {
  70. iconSelector.current.close();
  71. }
  72. return (
  73. <View style={styles.container}>
  74. <View style={styles.inputContainer}>
  75. <ScrollView>
  76. <Text style={styles.label}>Title</Text>
  77. <Controller
  78. control={control}
  79. render={({ field: { onBlur, onChange, value }}) => (
  80. <TextInput
  81. multiline={true}
  82. theme={editTheme}
  83. underlineColor="white"
  84. style={[styles.title, styles.input]}
  85. placeholder="Title"
  86. value={value}
  87. onBlur={value => onBlur(value)}
  88. onChangeText={value => onChange(value)}
  89. ></TextInput>
  90. )}
  91. name="title"
  92. rules={{ required: true, maxLength: 100 }} />
  93. {formState.errors.title?.type === "required" && <Text style={styles.errorText}>Title is required.</Text>}
  94. <Controller
  95. control={control}
  96. render={({ field: { onChange, onBlur, value }}) => (
  97. <TextInput
  98. multiline={true}
  99. theme={editTheme}
  100. style={[styles.desc, styles.input]}
  101. placeholder="Description"
  102. value={value}
  103. on={value => onBlur(value)}
  104. onChangeText={value => onChange(value)}
  105. ></TextInput>
  106. )}
  107. name="desc"
  108. rules={{ required: true, maxLength: 500 }} />
  109. {formState.errors.desc?.type === "required" && <Text style={styles.errorText}>Description is required.</Text>}
  110. {formState.errors.desc?.type === "maxLength" && <Text style={styles.errorText}>Description must be less than 500 characters.</Text>}
  111. <Text style={styles.label}>Type</Text>
  112. <TouchableOpacity onPress={openSelector}>
  113. <View style={styles.selectedIconContainer}>
  114. <View style={styles.selectedIcon}>
  115. <Image style={{height: 30, width: 22}} source={Icons[selectedIcon]}/>
  116. <Text style={{marginLeft: 15, color: 'black'}}>{selectedIcon}</Text>
  117. </View>
  118. <Icon style={iconSelector.isOpen ? { transform: [{rotate: '180deg'}] } : null} name="chevron-down" />
  119. </View>
  120. </TouchableOpacity>
  121. </ScrollView>
  122. </View>
  123. <TouchableOpacity style={{margin: 20, alignSelf: 'flex-end', zIndex: 5}} onPress={handleSubmit(saveLandmark)}>
  124. <Text style={{fontSize: 15, color: 'white'}}>Save Changes</Text>
  125. </TouchableOpacity>
  126. <Modal position={"bottom"} ref={iconSelector} style={{height: 400}} >
  127. <TouchableOpacity
  128. style={{flexDirection: 'row', justifyContent: 'center', paddingVertical: 10}}
  129. onPress={closeModal}>
  130. <Icon name="chevron-down"/>
  131. </TouchableOpacity>
  132. <ScrollView style={{marginBottom: 100}}>
  133. {Object.entries(Icons).map(icon => {
  134. return(
  135. <TouchableOpacity style={styles.iconChoice} key={icon[0]} onPress={() => updateIcon(icon[0])} >
  136. <Image style={{height: 30, width: 22}} source={icon[1]}/>
  137. <Text style={{marginLeft: 15}}>{icon[0]}</Text>
  138. </TouchableOpacity>)
  139. })}
  140. </ScrollView>
  141. </Modal>
  142. </View>
  143. )
  144. }
  145. const styles = StyleSheet.create({
  146. container: {
  147. position: 'absolute',
  148. width: Dimensions.get('window').width,
  149. height: Dimensions.get('window').height,
  150. flex: 1,
  151. backgroundColor: '#df3f3f',
  152. },
  153. inputContainer: {
  154. height: 500,
  155. paddingHorizontal: 20,
  156. paddingBottom: 20,
  157. paddingTop: 70,
  158. },
  159. label: {
  160. color: 'white',
  161. marginTop: 20,
  162. marginBottom: 8
  163. },
  164. title: {
  165. fontSize: 20,
  166. marginBottom: 20,
  167. },
  168. input: {
  169. backgroundColor: 'white',
  170. },
  171. desc: {
  172. color: 'white',
  173. },
  174. iconPicker: {
  175. color: 'white',
  176. },
  177. selectedIconContainer: {
  178. backgroundColor: 'white',
  179. paddingHorizontal: 30,
  180. padding: 15,
  181. flexDirection: 'row',
  182. alignItems: 'center',
  183. justifyContent: 'space-between'
  184. },
  185. errorText: {
  186. alignSelf: 'flex-end',
  187. color: 'black',
  188. fontSize: 15
  189. },
  190. selectedIcon: {
  191. backgroundColor: 'white',
  192. flexDirection: 'row',
  193. alignItems: 'center',
  194. },
  195. iconChoice: {
  196. marginHorizontal: 30,
  197. paddingVertical: 10,
  198. flexDirection: 'row',
  199. alignItems: 'center',
  200. borderBottomWidth: 1,
  201. },
  202. chevronRotate: {
  203. transform: [{ rotate: '180deg'}]
  204. }
  205. })
  206. export default LandmarkForm;