ViewArticle.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /**
  2. * 文章详情
  3. * @邠心vbe on 2023/10/24
  4. */
  5. import React, { Component } from 'react';
  6. import { View, StyleSheet, Image, ScrollView, Linking, Animated, Easing } from 'react-native';
  7. import Swiper from 'react-native-swiper';
  8. import apiArticle from '../../api/apiArticle';
  9. import TextView from '../../components/TextView';
  10. import VbeSkeleton from '../../components/VbeSkeleton';
  11. import utils from '../../utils/utils';
  12. import { PagerView } from './ViewUtil';
  13. import MyStatusBar from '../../components/MyStatusBar';
  14. import Toolbar, { BackButton } from '../../components/Toolbar';
  15. export default class ViewArticle extends Component {
  16. constructor(props) {
  17. super(props);
  18. this.state = {
  19. id: "",
  20. loading: true,
  21. showTitleBar: false,
  22. messageInfo: {
  23. articleTypeName: "",
  24. articleTitle: "",
  25. articleContent: ""
  26. },
  27. opacity: new Animated.Value(0)
  28. };
  29. }
  30. componentDidMount() {
  31. if (this.props.route?.params?.id) {
  32. this.setState({
  33. id: this.props.route?.params?.id
  34. }, () => {
  35. this.readMessage();
  36. })
  37. }
  38. MyStatusBar.setStatusBarTheme(MyStatusBar.LIGHT_STYLE);
  39. this.props.navigation.addListener('beforeRemove', (e) => {
  40. MyStatusBar.setStatusBarTheme(MyStatusBar.DEFAULT_STYLE);
  41. });
  42. }
  43. readMessage() {
  44. apiArticle.readMessage(this.state.id).then(res => {
  45. if (res.data) {
  46. this.setState({
  47. messageInfo: res.data
  48. });
  49. this.setPageTitle();
  50. }
  51. }).catch(err => {
  52. toastShort(err);
  53. });
  54. }
  55. setPageTitle() {
  56. if (this.state.messageInfo.articleTitle) {
  57. /*this.props.navigation.setOptions({
  58. headerTitle: () => (<HeaderTitle title={this.state.messageInfo.articleTitle}/>)
  59. })*/
  60. setTimeout(() => {
  61. this.setState({
  62. loading: false
  63. });
  64. }, 300);
  65. }
  66. }
  67. accessLink(url) {
  68. Linking.openURL(utils.getImageUrl(url))
  69. }
  70. onScrollView(e) {
  71. if (e.nativeEvent.contentOffset) {
  72. const isR = e.nativeEvent.contentOffset.y >= $vw(95);
  73. if (isR != this.state.showTitleBar) {
  74. this.setState({
  75. showTitleBar: isR
  76. });
  77. if (isR) {
  78. this.startTitleAnimate();
  79. } else {
  80. this.hideTitleAnimate();
  81. }
  82. MyStatusBar.setStatusBarTheme(isR ? MyStatusBar.DEFAULT_STYLE : MyStatusBar.LIGHT_STYLE);
  83. }
  84. }
  85. }
  86. startTitleAnimate() {
  87. Animated.timing(this.state.opacity, {
  88. toValue: 1,
  89. duration: 250,
  90. easing: Easing.linear,
  91. useNativeDriver: true
  92. }).start(() => {
  93. });
  94. }
  95. hideTitleAnimate() {
  96. Animated.timing(this.state.opacity, {
  97. toValue: 0,
  98. duration: 250,
  99. easing: Easing.linear,
  100. useNativeDriver: true
  101. }).start(() => {
  102. });
  103. }
  104. render() {
  105. if (this.state.loading) {
  106. return (
  107. <View style={styles.container}>
  108. <VbeSkeleton
  109. style={{width: $width, height: $width}}
  110. layout={[
  111. {width: $width, height: $width}
  112. ]}
  113. animationDirection={"horizontalRight"}/>
  114. <VbeSkeleton
  115. style={styles.loadingView}
  116. layout={[
  117. {width: '90%', height: 20, marginTop: 8},
  118. {width: '50%', height: 12, marginTop: 8},
  119. {width: '100%', height: 15, marginTop: 24},
  120. {width: '100%', height: 15, marginTop: 8},
  121. {width: '100%', height: 15, marginTop: 8},
  122. {width: '100%', height: 15, marginTop: 8},
  123. {width: '30%', height: 15, marginTop: 8},
  124. {width: '100%', height: 15, marginTop: 24},
  125. {width: '100%', height: 15, marginTop: 8},
  126. {width: '100%', height: 15, marginTop: 8},
  127. {width: '100%', height: 15, marginTop: 8},
  128. {width: '30%', height: 15, marginTop: 8}
  129. ]}
  130. animationDirection={"horizontalRight"}/>
  131. </View>
  132. )
  133. } else {
  134. return (
  135. <>
  136. <ScrollView
  137. style={styles.container}
  138. onScroll={e => this.onScrollView(e)}
  139. scrollEventThrottle={16}
  140. contentContainerStyle={$padding(0,0,32)}>
  141. { utils.isNotEmpty(this.state.messageInfo.articleImages) &&
  142. <Swiper
  143. style={{height: $width}}
  144. autoplay={true}
  145. autoplayTimeout={5}
  146. renderPagination={(index,total) => <PagerView index={index+1} total={total}/> }
  147. removeClippedSubviews={false}>
  148. { this.state.messageInfo.articleImages.map((item, index) => {
  149. return (
  150. <Image
  151. key={index}
  152. style={{width: $width, height: $width}}
  153. source={{uri: utils.getImageUrl(item.articleImagePath)}}/>
  154. );
  155. })}
  156. </Swiper>
  157. }
  158. <View style={styles.header}>
  159. <TextView
  160. style={styles.textTitle}>
  161. {this.state.messageInfo.articleTitle}
  162. </TextView>
  163. <View style={ui.flexc}>
  164. <MaterialCommunityIcons
  165. name="clock-time-four-outline"
  166. color={textSecondary}
  167. size={12}/>
  168. <TextView
  169. style={styles.textDate}
  170. numberOfLines={1}>
  171. {this.state.messageInfo.createTime}
  172. </TextView>
  173. <View style={ui.flexc}>
  174. <MaterialCommunityIcons
  175. name="eye-check-outline"
  176. size={12}
  177. color={textPrimary}/>
  178. <TextView
  179. style={styles.textView}
  180. numberOfLines={1}>
  181. {this.state.messageInfo.articleViews}
  182. </TextView>
  183. </View>
  184. </View>
  185. <View style={ui.flex}>
  186. <TextView
  187. style={styles.labelTypeText}
  188. numberOfLines={1}>
  189. {this.state.messageInfo.articleTypeName}
  190. </TextView>
  191. </View>
  192. </View>
  193. <TextView
  194. style={styles.textMessage}
  195. selectable={true}>
  196. {this.state.messageInfo.articleContent}
  197. </TextView>
  198. { utils.isNotEmpty(this.state.messageInfo.articleLinks) &&
  199. <>
  200. <TextView style={styles.textLinkTitle}>{$t("notification.labelLinks")}</TextView>
  201. { this.state.messageInfo.articleLinks.map((item, index) =>
  202. <View style={styles.itemLink} key={index}>
  203. <TextView style={styles.linkIndex}>{index + 1}.</TextView>
  204. <TextView
  205. style={styles.linkHyper}
  206. onPress={() => this.accessLink(item.articleLink)}>{item.articleLinkName}</TextView>
  207. </View>
  208. )}
  209. </>
  210. }
  211. <EndView/>
  212. </ScrollView>
  213. { !this.state.showTitleBar &&
  214. <View style={styles.toolbar}>
  215. <BackButton style={styles.backIcon} color={"#F0F0F0"}/>
  216. </View>
  217. }
  218. <Animated.View style={[styles.toolbar, {opacity: this.state.opacity}]}>
  219. <Toolbar title={this.state.messageInfo.articleTitle}/>
  220. </Animated.View>
  221. </>
  222. );
  223. }
  224. }
  225. }
  226. const styles = StyleSheet.create({
  227. container: {
  228. flex: 1,
  229. backgroundColor: pageBackground
  230. },
  231. toolbar: {
  232. top: 0,
  233. left: 0,
  234. right: 0,
  235. zIndex: 2,
  236. position: 'absolute'
  237. },
  238. backIcon: {
  239. width: 48,
  240. height: 48,
  241. zIndex: 2,
  242. marginTop: statusHeight + 4,
  243. marginLeft: 4,
  244. alignItems: 'center',
  245. justifyContent: 'center'
  246. },
  247. loadingView: {
  248. flex: 1,
  249. padding: 16,
  250. justifyContent: 'flex-start',
  251. backgroundColor: pageBackground
  252. },
  253. textTitle: {
  254. color: textPrimary,
  255. fontSize: 18,
  256. fontWeight: 'bold',
  257. paddingBottom: 2
  258. },
  259. textDate: {
  260. flex: 1,
  261. color: textSecondary,
  262. fontSize: 10,
  263. paddingLeft: 2
  264. },
  265. textView: {
  266. color: textSecondary,
  267. fontSize: 10,
  268. padding: 4
  269. },
  270. header: {
  271. padding: 16,
  272. backgroundColor: pageBackground
  273. },
  274. textMessage: {
  275. color: textPrimary,
  276. fontSize: 12,
  277. paddingLeft: 16,
  278. paddingRight: 16
  279. },
  280. labelTypeText: {
  281. fontSize: 12,
  282. fontWeight: 'bold',
  283. borderWidth: 1,
  284. borderRadius: 4,
  285. borderColor: colorPrimary,
  286. marginTop: 8,
  287. ...$padding(2, 6)
  288. },
  289. textLinkTitle: {
  290. color: textPrimary,
  291. fontSize: 14,
  292. fontWeight: 'bold',
  293. padding: 16
  294. },
  295. itemLink: {
  296. ...$padding(0, 16, 8),
  297. flexDirection: 'row'
  298. },
  299. linkIndex: {
  300. fontSize: 12,
  301. paddingRight: 2
  302. },
  303. linkHyper: {
  304. ...ui.link,
  305. fontSize: 12,
  306. textDecorationLine: 'underline'
  307. },
  308. linkActive: {
  309. color: "#FF3B30"
  310. }
  311. })