NewsDetailPage.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import {
  2. StyleSheet,
  3. Text,
  4. View,
  5. TouchableWithoutFeedback,
  6. ScrollView,
  7. Image,
  8. } from 'react-native';
  9. import React, {useCallback} from 'react';
  10. import NewscoutTitleHeader from '../../components/molecules/Header/NewscoutTitleHeader';
  11. import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
  12. import colors from '../../constants/colors';
  13. import {
  14. horizontalScale,
  15. moderateScale,
  16. verticalScale,
  17. } from '../../constants/metrics';
  18. import {
  19. getArticleBySlug,
  20. getCommentByArticleID,
  21. getRecommendationByArticleID,
  22. } from '../../api/data';
  23. import LoadingScreen from '../../components/organisms/Sections/LoadingScreen';
  24. import {
  25. getTimestamp,
  26. navigateToArticle,
  27. useConstructor,
  28. } from '../../constants/functions';
  29. import fonts from '../../constants/fonts';
  30. import BookmarkButton from '../../components/atoms/Buttons/BookmarkButton';
  31. import ShareButton from '../../components/atoms/Buttons/ShareButton';
  32. import IonIcon from 'react-native-vector-icons/Ionicons';
  33. import {
  34. BottomSheetModal,
  35. BottomSheetModalProvider,
  36. BottomSheetView,
  37. BottomSheetScrollView,
  38. } from '@gorhom/bottom-sheet';
  39. import ButtonWrapper from '../../components/atoms/Buttons/ButtonWrapper';
  40. import images from '../../assets/images/images';
  41. import SectionHeader from '../../components/molecules/Header/SectionHeader';
  42. import VerticalNewsCard from '../../components/molecules/Cards/VerticalNewsCard';
  43. const NewsDetailPage = props => {
  44. const {navigation, route} = props;
  45. const {id, slug} = route.params;
  46. const [isLoading, setLoading] = React.useState(true);
  47. const [article, setArticle] = React.useState({});
  48. const [comments, setComments] = React.useState({
  49. total_article_likes: 0,
  50. results: [],
  51. });
  52. const [recommendations, setRecommendations] = React.useState([]);
  53. const mainPageRef = React.useRef(null);
  54. const bottomSheetModalRef = React.useRef('');
  55. // Comments Modal
  56. const snapPoints = React.useMemo(() => ['70%', '100%'], []);
  57. const handlePresentModalPress = useCallback(() => {
  58. bottomSheetModalRef.current?.present();
  59. }, []);
  60. const handleCloseModalPress = () => bottomSheetModalRef.current.close();
  61. const handleSheetChanges = useCallback(index => {
  62. console.log('handleSheetChanges', index);
  63. }, []);
  64. const fetchArticle = slug => {
  65. getArticleBySlug(slug)
  66. .then(res => {
  67. console.log(res.data);
  68. setArticle(res.data.body.article);
  69. setLoading(false);
  70. })
  71. .catch(error => console.log(error));
  72. };
  73. const fetchComments = id => {
  74. getCommentByArticleID(id)
  75. .then(res => {
  76. console.log(res.data.body);
  77. setComments(res.data.body);
  78. })
  79. .catch(err => console.log(err));
  80. };
  81. const fetchRecommendations = id => {
  82. getRecommendationByArticleID(id)
  83. .then(res => setRecommendations(res.data.body.results))
  84. .catch(err => console.log(err));
  85. };
  86. useConstructor(function () {
  87. fetchArticle(slug);
  88. fetchComments(id);
  89. fetchRecommendations(id);
  90. });
  91. const styles = StyleSheet.create({
  92. container: {backgroundColor: colors().dominant, minHeight: '100%'},
  93. newsContainer: {paddingHorizontal: horizontalScale(16)},
  94. newsTitle: {
  95. fontFamily: fonts.type.semibold,
  96. color: colors().recessive,
  97. fontSize: moderateScale(16),
  98. },
  99. newsTagline: {
  100. color: colors().recessive_variant,
  101. fontFamily: fonts.type.regular,
  102. paddingVertical: verticalScale(12),
  103. },
  104. newsDescriptorContainer: {
  105. flexDirection: 'row',
  106. gap: horizontalScale(8),
  107. },
  108. newsDescriptor: {
  109. color: colors().recessive,
  110. fontFamily: fonts.type.semibold,
  111. fontSize: moderateScale(12),
  112. },
  113. imagesContainer: {
  114. width: 'auto',
  115. height: verticalScale(200),
  116. marginTop: verticalScale(16),
  117. },
  118. image: {
  119. borderRadius: moderateScale(4),
  120. width: '100%',
  121. height: '100%',
  122. },
  123. buttonStyle: {
  124. padding: moderateScale(8),
  125. },
  126. commentSection: {
  127. alignItems: 'center',
  128. justifyContent: 'center',
  129. flexDirection: 'row',
  130. gap: moderateScale(8),
  131. paddingLeft: horizontalScale(4),
  132. },
  133. commentText: {
  134. fontFamily: fonts.type.regular,
  135. color: colors().grayShade_200,
  136. },
  137. commentInputContainer: {
  138. flexDirection: 'row',
  139. justifyContent: 'space-between',
  140. alignItems: 'center',
  141. paddingVertical: verticalScale(4),
  142. },
  143. profileImage: {
  144. height: 42,
  145. width: 42,
  146. borderRadius: 32,
  147. marginRight: horizontalScale(16),
  148. },
  149. commentInput: {
  150. paddingVertical: verticalScale(16),
  151. alignItems: 'center',
  152. justifyContent: 'flex-start',
  153. flexDirection: 'row',
  154. },
  155. utilButtons: {
  156. flexDirection: 'row',
  157. justifyContent: 'space-between',
  158. paddingVertical: verticalScale(4),
  159. },
  160. newsText: {
  161. color: colors().grayShade_200,
  162. fontFamily: fonts.type.regular,
  163. lineHeight: verticalScale(24),
  164. paddingVertical: verticalScale(4),
  165. },
  166. backToTop: {
  167. alignItems: 'center',
  168. justifyContent: 'center',
  169. paddingTop: verticalScale(16),
  170. paddingBottom: verticalScale(8),
  171. },
  172. backToTopText: {
  173. color: colors().primaryColor,
  174. fontFamily: fonts.type.medium,
  175. textDecorationLine: 'underline',
  176. fontSize: moderateScale(16),
  177. },
  178. recommendationContainer: {
  179. paddingHorizontal: horizontalScale(16),
  180. gap: moderateScale(4),
  181. paddingBottom: verticalScale(16),
  182. backgroundColor: colors().dominant,
  183. flexDirection: 'row',
  184. },
  185. });
  186. return (
  187. <BottomSheetModalProvider>
  188. <ScrollView ref={mainPageRef} contentContainerStyle={styles.container}>
  189. <NewscoutTitleHeader
  190. backButtonShown
  191. onBackClick={() => navigation.goBack()}>
  192. <TouchableWithoutFeedback onPress={() => navigation.toggleDrawer()}>
  193. <MaterialIcon name="list" color={colors().primaryColor} size={30} />
  194. </TouchableWithoutFeedback>
  195. </NewscoutTitleHeader>
  196. {isLoading === true ? (
  197. <LoadingScreen />
  198. ) : (
  199. <View style={styles.newsContainer}>
  200. <Text style={styles.newsTitle}>{article.title}</Text>
  201. <Text style={styles.newsTagline}>{'No Tagline'}</Text>
  202. <View style={styles.newsDescriptorContainer}>
  203. <Text style={styles.newsDescriptor}>
  204. {getTimestamp(article.published_on)}
  205. </Text>
  206. {(article.author !== undefined || article.author.length <= 0) ?? (
  207. <Text style={styles.newsDescriptor}>By {article.author}</Text>
  208. )}
  209. <Text style={styles.newsDescriptor}>{article.source}</Text>
  210. </View>
  211. <View style={styles.imagesContainer}>
  212. <Image source={{uri: article.cover_image}} style={styles.image} />
  213. </View>
  214. <View style={styles.utilButtons}>
  215. <TouchableWithoutFeedback onPress={handlePresentModalPress}>
  216. <View style={styles.commentSection}>
  217. <IonIcon
  218. name="chatbubble-outline"
  219. size={moderateScale(20)}
  220. color={colors().primaryColor}
  221. />
  222. <Text style={styles.commentText}>
  223. {comments.results.length ?? 123} COMMENTS
  224. </Text>
  225. </View>
  226. </TouchableWithoutFeedback>
  227. <View style={{flexDirection: 'row'}}>
  228. <BookmarkButton
  229. buttonStyle={styles.buttonStyle}
  230. iconSize={20}
  231. onPress={true}
  232. />
  233. <ShareButton
  234. buttonStyle={styles.buttonStyle}
  235. iconSize={20}
  236. onPress={true}
  237. />
  238. </View>
  239. </View>
  240. <Text style={styles.newsText}>{article.blurb}</Text>
  241. <View style={styles.backToTop}>
  242. <TouchableWithoutFeedback
  243. onPress={() =>
  244. mainPageRef.current.scrollTo({
  245. y: 0,
  246. })
  247. }>
  248. <Text style={styles.backToTopText}>Back To Top</Text>
  249. </TouchableWithoutFeedback>
  250. </View>
  251. <>
  252. <BottomSheetModal
  253. ref={bottomSheetModalRef}
  254. index={1}
  255. snapPoints={snapPoints}
  256. onChange={handleSheetChanges}
  257. backgroundStyle={{
  258. backgroundColor: colors().dominant,
  259. minHeight: '100%',
  260. }}
  261. handleStyle={{
  262. backgroundColor: colors().dominant_variant,
  263. }}>
  264. <BottomSheetScrollView>
  265. <BottomSheetView
  266. style={{
  267. paddingHorizontal: horizontalScale(24),
  268. }}>
  269. <BottomSheetView style={styles.commentInputContainer}>
  270. <Text
  271. style={{
  272. fontFamily: fonts.type.semibold,
  273. color: colors().recessive,
  274. fontSize: moderateScale(16),
  275. }}>
  276. Comments
  277. </Text>
  278. <ButtonWrapper onPress={handleCloseModalPress}>
  279. <IonIcon
  280. name="close-sharp"
  281. size={moderateScale(20)}
  282. color={colors().recessive}
  283. />
  284. </ButtonWrapper>
  285. </BottomSheetView>
  286. <View style={styles.commentInput}>
  287. <Image
  288. source={images.imageCard}
  289. style={[styles.profileImage]}
  290. />
  291. <Text>Comment Text Input</Text>
  292. </View>
  293. <>
  294. <Text
  295. style={{
  296. fontFamily: fonts.type.medium,
  297. color: colors().recessive,
  298. paddingBottom: verticalScale(8),
  299. }}>
  300. View all Comments({comments.results.length})
  301. </Text>
  302. </>
  303. <BottomSheetView style={{gap: moderateScale(16)}}>
  304. {comments.results.length <= 0 ? (
  305. <LoadingScreen />
  306. ) : (
  307. comments.results.map(() => <CommentCard />)
  308. )}
  309. </BottomSheetView>
  310. </BottomSheetView>
  311. </BottomSheetScrollView>
  312. </BottomSheetModal>
  313. </>
  314. </View>
  315. )}
  316. <SectionHeader label={'Recommendations'} />
  317. <ScrollView
  318. horizontal
  319. contentContainerStyle={styles.recommendationContainer}
  320. style={{flexDirection: 'row'}}
  321. showsHorizontalScrollIndicator={false}>
  322. {recommendations.map(item => (
  323. <VerticalNewsCard
  324. image={{uri: item.cover_image}}
  325. headline={item.title}
  326. onPress={() => navigateToArticle(navigation, item.id, item.slug)}
  327. />
  328. ))}
  329. </ScrollView>
  330. </ScrollView>
  331. </BottomSheetModalProvider>
  332. );
  333. };
  334. export default NewsDetailPage;