ViewArticle.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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, {top: statusHeight + 4}]}>
  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. marginLeft: isIOS ? 0 : 2,
  243. alignItems: 'center',
  244. justifyContent: 'center'
  245. },
  246. loadingView: {
  247. flex: 1,
  248. padding: 16,
  249. justifyContent: 'flex-start',
  250. backgroundColor: pageBackground
  251. },
  252. textTitle: {
  253. color: textPrimary,
  254. fontSize: 18,
  255. fontWeight: 'bold',
  256. paddingBottom: 2
  257. },
  258. textDate: {
  259. flex: 1,
  260. color: textSecondary,
  261. fontSize: 10,
  262. paddingLeft: 2
  263. },
  264. textView: {
  265. color: textSecondary,
  266. fontSize: 10,
  267. padding: 4
  268. },
  269. header: {
  270. padding: 16,
  271. backgroundColor: pageBackground
  272. },
  273. textMessage: {
  274. color: textPrimary,
  275. fontSize: 12,
  276. paddingLeft: 16,
  277. paddingRight: 16
  278. },
  279. labelTypeText: {
  280. fontSize: 12,
  281. fontWeight: 'bold',
  282. borderWidth: 1,
  283. borderRadius: 4,
  284. borderColor: colorPrimary,
  285. marginTop: 8,
  286. ...$padding(2, 6)
  287. },
  288. textLinkTitle: {
  289. color: textPrimary,
  290. fontSize: 14,
  291. fontWeight: 'bold',
  292. padding: 16
  293. },
  294. itemLink: {
  295. ...$padding(0, 16, 8),
  296. flexDirection: 'row'
  297. },
  298. linkIndex: {
  299. fontSize: 12,
  300. paddingRight: 2
  301. },
  302. linkHyper: {
  303. ...ui.link,
  304. fontSize: 12,
  305. textDecorationLine: 'underline'
  306. },
  307. linkActive: {
  308. color: "#FF3B30"
  309. }
  310. })