LandmarkDetails.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import React, { useState, useEffect, useRef } from 'react';
  2. import { TextInput, Image, ScrollView, View, Text, StyleSheet, TouchableOpacity, Keyboard, Alert, Dimensions} from 'react-native';
  3. import { format } from 'date-fns';
  4. import { Controller, useForm } from 'react-hook-form';
  5. import { useMockState } from '../contexts/MockContext';
  6. import Icon from 'react-native-vector-icons/FontAwesome'
  7. import { Icons } from '../globals';
  8. import { v4 as uuidv4 } from 'uuid';
  9. const LandmarkDetails = ({closeModal, editLandmark}) => {
  10. const {dispatch, state} = useMockState();
  11. const [ownedByUser, setOwned] = useState(state.username === state.selectedLandmark.postedBy);
  12. const [keyboardShown, setKeyboard] = useState(false);
  13. const [commentText, setComment] = useState('');
  14. const [commentPosted, setPosted] = useState(false);
  15. const [commentInputOffset, setOffset] = useState({});
  16. const commentInput = useRef()
  17. const _keyboardDidShow = (e) => {
  18. setKeyboard(true);
  19. setOffset({
  20. position: 'absolute',
  21. bottom: e.endCoordinates.height / 2 - 22,
  22. marginHorizontal: -20,
  23. paddingLeft: 10,
  24. width: Dimensions.get('window').width
  25. });
  26. }
  27. const _keyboardDidHide = (e) => {
  28. setKeyboard(false);
  29. setOffset({});
  30. commentInput.current.blur();
  31. console.log(commentPosted)
  32. console.log(commentText)
  33. // only show alert if comment wasn't posted
  34. if (!commentPosted && commentText != '') {
  35. Alert.alert(
  36. 'Discard comment?',
  37. '',
  38. [
  39. {
  40. text: 'Keep writing', onPress: () => commentInput.current.focus(),
  41. },
  42. {
  43. text: 'Discard', onPress: () => {
  44. setComment('');
  45. setOffset({});
  46. setPosted(false);
  47. },
  48. style: 'cancel'
  49. }
  50. ]
  51. )
  52. }
  53. }
  54. useEffect(() => {
  55. Keyboard.addListener('keyboardDidShow', _keyboardDidShow);
  56. Keyboard.addListener('keyboardDidHide', _keyboardDidHide);
  57. return () => {
  58. Keyboard.removeListener('keyboardDidShow', _keyboardDidShow);
  59. Keyboard.removeListener('keyboardDidHide', _keyboardDidHide);
  60. }
  61. })
  62. const postComment = () => {
  63. let currentLandmark;
  64. dispatch({type: "UPDATE_LANDMARKS", payload: state.landmarks.map(l => {
  65. if (l.id === state.selectedLandmark.id) {
  66. const newComment = {
  67. id: uuidv4(),
  68. dateAdded: new Date(),
  69. name: "cdmoss",
  70. text: commentText
  71. }
  72. currentLandmark = {...l, comments: [...l.comments, newComment]};
  73. dispatch({type: "UPDATE_SELECTED_LANDMARK", payload: currentLandmark});
  74. return currentLandmark;
  75. }
  76. return l;
  77. })});
  78. setComment('');
  79. setPosted(true);
  80. Keyboard.dismiss();
  81. setPosted(false);
  82. }
  83. const notifyError = (errors) => {
  84. Alert.alert('Comment must include text.');
  85. }
  86. const endorseLandmark = () => {
  87. dispatch({type: "UPDATE_LANDMARKS", payload: state.landmarks.map(l => {
  88. if (l.id == state.selectedLandmark.id) {
  89. const newRating = l.rating + 1;
  90. const updatedLandmark = {...l, rating: newRating};
  91. dispatch({type: "UPDATE_SELECTED_LANDMARK", payload: updatedLandmark});
  92. return updatedLandmark;
  93. }
  94. return l;
  95. })})
  96. }
  97. const removeEndorsement = () => {
  98. dispatch({type: "UPDATE_LANDMARKS", payload: state.landmarks.map(l => {
  99. if (l.id == state.selectedLandmark.id) {
  100. const newRating = l.rating - 1;
  101. const updatedLandmark = {...l, rating: newRating};
  102. dispatch({type: "UPDATE_SELECTED_LANDMARK", payload: updatedLandmark});
  103. return updatedLandmark;
  104. }
  105. return l;
  106. })})
  107. }
  108. const deleteLandmark = () => {
  109. dispatch({type: "UPDATE_LANDMARKS", payload: state.landmarks.filter(l => l.id !== state.selectedLandmark.id)});
  110. closeModal('Successfully deleted landmark.');
  111. }
  112. return (
  113. <View style={styles.container}>
  114. <View style={styles.detailsHeader}>
  115. <Icon.Button style={styles.headerBtn} onPress={() => closeModal()} name="times"/>
  116. <Text style={styles.rating}><Icon style={styles.headerBtn} name="thumbs-up" size={18}/> {state.selectedLandmark.rating}</Text>
  117. {ownedByUser ?
  118. <View style={{flexDirection: 'row'}}>
  119. <Icon.Button style={styles.headerBtn} onPress={editLandmark} name="edit"/>
  120. <Icon.Button style={styles.headerBtn} onPress={deleteLandmark} name="trash"/>
  121. </View>
  122. : null}
  123. {!ownedByUser && state.selectedLandmark.rating == 0 ? <Icon.Button style={styles.headerBtn} onPress={endorseLandmark} name="thumbs-up"/> : null}
  124. {!ownedByUser && state.selectedLandmark.rating > 0 ?
  125. <View style={{flexDirection: 'row'}}>
  126. <Text style={{marginRight: 5, color: 'lightgrey'}}>
  127. You like this landmark
  128. </Text>
  129. <TouchableOpacity onPress={removeEndorsement} style={{alignItems: 'center', padding: 0}}>
  130. <Text style={{marginRight: 10, color: '#005A9C'}}>(Undo)</Text>
  131. </TouchableOpacity>
  132. </View> : null}
  133. </View>
  134. <View style={styles.titleContainer}>
  135. <Text style={styles.title}>{state.selectedLandmark.title}</Text>
  136. <View style={{ backgroundColor: 'white', borderRadius: 25, height: 40, width: 40 ,justifyContent: 'center', alignItems: 'center'}}>
  137. <Image style={styles.titleIcon} source={Icons[state.selectedLandmark.icon]} />
  138. </View>
  139. </View>
  140. <View style={styles.detailsContainer}>
  141. <ScrollView><Text style={styles.desc}>{state.selectedLandmark.desc}</Text></ScrollView>
  142. <Text style={styles.date}>{format(state.selectedLandmark.dateAdded, "MMMM do, yyyy h:mma")}</Text>
  143. <Text style={{fontSize: 15, color: 'white'}}>Comments:</Text>
  144. <View style={styles.commentsBox}>
  145. <ScrollView>
  146. <TouchableOpacity>
  147. {state.selectedLandmark.comments.sort((a, b) => {return b.dateAdded - a.dateAdded}).map(comment => {
  148. return(
  149. <View style={styles.commentContainer} key={comment.id}>
  150. <View style={styles.commentHeader}>
  151. <Text style={{fontWeight: 'bold'}} >{comment.name}</Text>
  152. <Text style={{fontSize: 12, color: 'lightgrey'}}>{format(comment.dateAdded, "MMMM do, yyyy h:mma")}</Text>
  153. </View>
  154. <Text style={{padding: 10}}>{comment.text}</Text>
  155. </View>
  156. );
  157. })}
  158. </TouchableOpacity>
  159. </ScrollView>
  160. <View style={[styles.commentInputContainer, commentInputOffset]}>
  161. <TextInput
  162. ref={commentInput}
  163. multiline={true}
  164. style={[styles.addComment]}
  165. placeholder="Add comment..."
  166. onChangeText={text => setComment(text)}
  167. value={commentText}
  168. ></TextInput>
  169. {keyboardShown && commentText != '' ?
  170. <View style={styles.postBtn} onPress={postComment}>
  171. <Icon.Button
  172. name='paper-plane'
  173. color='black'
  174. style={{backgroundColor: 'white'}}
  175. onPress={postComment}/>
  176. </View> : null }
  177. </View>
  178. </View>
  179. </View>
  180. </View>
  181. )
  182. }
  183. const styles = StyleSheet.create({
  184. detailsHeader: {
  185. borderBottomWidth: 1,
  186. borderColor: 'lightgrey',
  187. flexDirection: 'row',
  188. justifyContent: 'space-between',
  189. alignItems: 'center',
  190. backgroundColor: '#df3f3f',
  191. marginBottom: 10,
  192. },
  193. headerBtn: {
  194. backgroundColor: '#df3f3f',
  195. padding: 10,
  196. justifyContent: 'center',
  197. },
  198. container: {
  199. marginBottom: 30,
  200. backgroundColor: '#df3f3f',
  201. },
  202. detailsContainer: {
  203. marginHorizontal: 20
  204. },
  205. titleContainer: {
  206. marginHorizontal: 20,
  207. marginBottom: 20,
  208. flexDirection: 'row',
  209. justifyContent: 'space-between'
  210. },
  211. titleIcon: {
  212. height: 30,
  213. width: 22,
  214. },
  215. title: {
  216. color: 'white',
  217. fontSize: 20,
  218. marginBottom: 20,
  219. },
  220. rating: {
  221. color: 'white',
  222. },
  223. desc: {
  224. color: 'white',
  225. height: 50,
  226. },
  227. date: {
  228. color: 'lightgray',
  229. marginBottom: 10,
  230. alignSelf: "flex-end",
  231. },
  232. commentsBox: {
  233. height: 275,
  234. backgroundColor: 'white',
  235. marginTop: 10,
  236. padding: 10,
  237. marginBottom: 20,
  238. borderColor: 'black',
  239. },
  240. commentContainer: {
  241. padding: 10
  242. },
  243. commentHeader: {
  244. flexDirection: 'row',
  245. justifyContent: 'space-between'
  246. },
  247. commentBody: {
  248. padding: 10
  249. },
  250. commentInputContainer: {
  251. backgroundColor: 'white',
  252. flexDirection: 'row',
  253. borderTopWidth: 1,
  254. borderColor: 'grey',
  255. justifyContent: 'space-between',
  256. alignItems: 'center',
  257. width: 300
  258. },
  259. addComment: {
  260. backgroundColor: 'white',
  261. width: 270,
  262. },
  263. postBtn: {
  264. justifyContent: 'center',
  265. alignItems: 'center',
  266. borderRadius: 10,
  267. width: 75,
  268. height: 60
  269. },
  270. btnContainer: {
  271. backgroundColor: '#df3f3f',
  272. marginTop: 10,
  273. height: 70,
  274. flexDirection: 'row',
  275. justifyContent: 'space-evenly',
  276. alignItems: 'center',
  277. padding: 20,
  278. },
  279. btn: {
  280. borderColor: 'white',
  281. borderRadius: 25,
  282. justifyContent: 'center',
  283. alignItems: 'center',
  284. marginHorizontal: 20,
  285. marginVertical: 20,
  286. borderWidth: 1,
  287. width: 170,
  288. height: 50
  289. },
  290. btnText: {
  291. color: 'white',
  292. }
  293. })
  294. export default LandmarkDetails;