MomentScreen.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. import React, {Component} from 'react';
  2. import Toast from '@remobile/react-native-toast';
  3. import CommonTitleBar from '../views/CommonTitleBar';
  4. import CountEmitter from '../event/CountEmitter';
  5. import StorageUtil from '../utils/StorageUtil';
  6. import LoadingView from '../views/LoadingView';
  7. import MomentMenuView from '../views/MomentMenuView';
  8. import {UIManager} from 'NativeModules';
  9. import ReplyPopWin from '../views/ReplyPopWin';
  10. import Global from '../utils/Global';
  11. import Utils from '../utils/Utils';
  12. import Base64Utils from '../utils/Base64';
  13. import TimeUtils from '../utils/TimeUtil';
  14. import {
  15. ART,
  16. Dimensions,
  17. FlatList,
  18. Image,
  19. PixelRatio,
  20. StyleSheet,
  21. Text,
  22. TextInput,
  23. TouchableOpacity,
  24. View
  25. } from 'react-native';
  26. const {width} = Dimensions.get('window');
  27. const AVATAR_WIDTH = 80;
  28. const HEIGHT = width * 7 / 10;
  29. export default class MomentScreen extends Component {
  30. constructor(props) {
  31. super(props);
  32. this.state = {
  33. moments: [],
  34. avatar: '',
  35. showProgress: false,
  36. menuPos: {},
  37. menuShow: false,
  38. doFavorMomentId: -1,
  39. isUpdate: false,
  40. isLoadMore: false,
  41. hasMoreData: true,
  42. showReplyInput: false
  43. };
  44. // 分页需要使用的两个参数offset:偏移量, pagesize:一页的大小,pagesize=-1代表获取所有数据
  45. this.offset = 0;
  46. this.pagesize = 5;
  47. StorageUtil.get('username', (error, object) => {
  48. if (!error && object != null) {
  49. this.setState({username: object.username});
  50. StorageUtil.get('userInfo-' + object.username, (error, object) => {
  51. if (!error && object != null) {
  52. this.setState({avatar: object.info.avatar});
  53. }
  54. });
  55. }
  56. });
  57. }
  58. componentWillMount() {
  59. CountEmitter.addListener('updateMomentList', () => {
  60. // 刷新朋友圈列表
  61. this.setState({isUpdate: true, isLoadMore: false});
  62. this.offset = 0;
  63. this.pagesize = 5;
  64. this.getMoments(true);
  65. });
  66. }
  67. componentDidMount() {
  68. if (!this.state.isUpdate) {
  69. this.setState({isLoadMore: false});
  70. this.getMoments(true);
  71. }
  72. let replyInput = this.refs.replyInput;
  73. if (!Utils.isEmpty(replyInput)) {
  74. replyInput.focus();
  75. }
  76. }
  77. getMoments(useLoading) {
  78. if (useLoading) {
  79. this.showLoading();
  80. }
  81. let url = 'http://app.yubo725.top/moments?offset=' + this.offset + '&pagesize=' + this.pagesize;
  82. fetch(url).then((res) => res.json())
  83. .then((json) => {
  84. if (useLoading) {
  85. this.hideLoading();
  86. }
  87. if (json != null) {
  88. if (json.code == 1) {
  89. let data = json.msg; // 数组
  90. if (data.length == 0) {
  91. Toast.showShortCenter('没有更多数据了');
  92. this.setState({hasMoreData: false});
  93. return;
  94. }
  95. let moments = this.state.moments;
  96. if (data != null && data.length > 0) {
  97. for (let i = 0; i < data.length; i++) {
  98. data[i].key = i + '-' + this.offset;
  99. if (this.state.isLoadMore) {
  100. moments.push(data[i]);
  101. }
  102. }
  103. }
  104. if (this.state.isLoadMore) {
  105. this.setState({moments: moments});
  106. } else {
  107. this.setState({moments: data});
  108. }
  109. }
  110. }
  111. }).catch((e) => {
  112. if (useLoading) {
  113. this.hideLoading();
  114. }
  115. Toast.showShortCenter(e.toString());
  116. })
  117. }
  118. showLoading() {
  119. this.setState({showProgress: true});
  120. }
  121. hideLoading() {
  122. this.setState({showProgress: false});
  123. }
  124. renderHeaderView(username, avatar) {
  125. return (
  126. <View>
  127. <Image style={styles.momentImg} source={require('../../images/moment.jpg')}/>
  128. <Text style={styles.userNameText}>{this.state.username}</Text>
  129. <Image style={styles.avatarImg} source={avatar}/>
  130. </View>
  131. );
  132. }
  133. render() {
  134. let avatar = require('../../images/avatar.png');
  135. if (!Utils.isEmpty(this.state.avatar)) {
  136. avatar = {uri: this.state.avatar};
  137. }
  138. return (
  139. <View style={styles.container}>
  140. <CommonTitleBar title={"朋友圈"} nav={this.props.navigation} rightIcon={require('../../images/ic_camera.png')}
  141. handleRightClick={() => this.props.navigation.navigate('PublishMoment')}/>
  142. {
  143. this.state.showProgress ? (
  144. <LoadingView cancel={() => this.setState({showProgress: false})}/>
  145. ) : (null)
  146. }
  147. <View style={{backgroundColor: 'transparent', position: 'absolute', left: 0, top: 0, width: width}}>
  148. <MomentMenuView ref="momentMenuView"/>
  149. </View>
  150. <View style={{backgroundColor: 'transparent', position: 'absolute', left: 0, top: 0, width: width}}>
  151. <ReplyPopWin ref="replyPopWin"/>
  152. </View>
  153. <FlatList
  154. ListHeaderComponent={() => this.renderHeaderView(this.state.username, avatar)}
  155. data={this.state.moments}
  156. renderItem={this.renderItem}
  157. onEndReached={() => {
  158. this.loadNextPage()
  159. }}
  160. onEndReachedThreshold={0.2}
  161. />
  162. {
  163. this.state.showReplyInput ? (
  164. <TextInput autoFocus={true} ref="replyInput"
  165. style={{position: 'absolute', left: 0, bottom: 0, width: width}}/>
  166. ) : (null)
  167. }
  168. </View>
  169. );
  170. }
  171. renderImageRow(arr, start, end) {
  172. let images = [];
  173. for (let i = start; i < end; i++) {
  174. let img = {uri: arr[i]};
  175. images.push(
  176. <TouchableOpacity key={"row-image-" + i} activeOpacity={0.6}
  177. onPress={() => this.props.navigation.navigate('ImageShow', {'images': arr, 'index': i})}>
  178. <Image source={img} style={listItemStyle.imageCell}/>
  179. </TouchableOpacity>
  180. );
  181. }
  182. return (
  183. <View key={"row-" + start} style={{flexDirection: 'row', marginTop: 3}}>
  184. {images}
  185. </View>
  186. );
  187. }
  188. renderImages(pictures) {
  189. if (pictures == null || pictures == '') {
  190. return null;
  191. }
  192. let arr = pictures.split('#');
  193. let len = arr.length;
  194. let images = [];
  195. if (len > 0) {
  196. let rowNum = Math.ceil(len / 3);
  197. for (let i = 0; i < rowNum; i++) {
  198. let start = i * 3;
  199. let end = i * 3 + 3;
  200. if (end > len) {
  201. end = len;
  202. }
  203. images.push(this.renderImageRow(arr, start, end));
  204. }
  205. }
  206. return (
  207. <View style={listItemStyle.imageContainer}>
  208. {images}
  209. </View>
  210. );
  211. }
  212. loadNextPage = (info) => {
  213. if (!this.state.hasMoreData) {
  214. return;
  215. }
  216. this.setState({isLoadMore: true});
  217. Toast.showShortCenter('加载下一页');
  218. this.offset = this.offset + this.pagesize;
  219. this.getMoments(false);
  220. }
  221. renderReplys(item) {
  222. let replys = [];
  223. let arr = item.item.replys;
  224. if (!Utils.isEmpty(arr) && arr.length > 0) {
  225. for (let i = 0; i < arr.length; i++) {
  226. replys.push(<Text key={item.item.moment_id + "-" + i}
  227. style={listItemStyle.commentText}>{arr[i].username + ":" + Base64Utils.decoder(arr[i].content)}</Text>);
  228. }
  229. }
  230. return replys;
  231. }
  232. isCommentEmpty(item) {
  233. if (Utils.isEmpty(item.item.favor_names) && (item.item.replys == null || item.item.replys.length == 0)) {
  234. return true;
  235. }
  236. return false;
  237. }
  238. renderItem = (item) => {
  239. const path = ART.Path();
  240. path.moveTo(10, 10);
  241. path.lineTo(20, 0);
  242. path.lineTo(30, 10);
  243. path.close();
  244. let avatar = require('../../images/avatar.png');
  245. if (!Utils.isEmpty(item.item.avatar)) {
  246. avatar = {uri: item.item.avatar};
  247. }
  248. return (
  249. <View key={item.item.key}>
  250. <View style={listItemStyle.container}>
  251. <Image style={listItemStyle.avatar} source={avatar}/>
  252. <View style={listItemStyle.content}>
  253. <Text style={listItemStyle.nameText}>{item.item.username}</Text>
  254. <Text style={listItemStyle.msgText}>{Base64Utils.decoder(item.item.content)}</Text>
  255. {this.renderImages(item.item.pictures)}
  256. <View style={listItemStyle.timeContainer}>
  257. <Text style={listItemStyle.timeText}>{TimeUtils.getFormattedTime(item.item.time)}</Text>
  258. <TouchableOpacity activeOpacity={0.6}
  259. onPress={(e) => this.showMenuView(e, item.item.moment_id, item.item.username, this.doFavorSuccessCallback, this.doCommentCallback)}
  260. style={{marginLeft: 10}}>
  261. <Image style={listItemStyle.commentImg} source={require('../../images/ic_comment.png')}/>
  262. </TouchableOpacity>
  263. </View>
  264. {
  265. this.isCommentEmpty(item) ? (null) : (
  266. <View style={listItemStyle.commentContainer}>
  267. <ART.Surface width={30} height={10}>
  268. <ART.Shape d={path} fill={'#EEEEEE'}/>
  269. </ART.Surface>
  270. <View style={listItemStyle.commentContent}>
  271. {
  272. Utils.isEmpty(item.item.favor_names) ? (null) : (
  273. <View style={{flexDirection: 'column'}}>
  274. <View style={listItemStyle.favorContainer}>
  275. <Image style={listItemStyle.favorImg} source={require('../../images/ic_favor.png')}/>
  276. <Text style={listItemStyle.commentText}>{item.item.favor_names}</Text>
  277. </View>
  278. {
  279. (item.item.replys == null || item.item.replys.length == 0) ? (null) : (
  280. <View style={[listItemStyle.divider, {marginTop: 3, marginBottom: 3}]}/>
  281. )
  282. }
  283. </View>
  284. )
  285. }
  286. <View style={{flexDirection: 'column'}}>
  287. {this.renderReplys(item)}
  288. </View>
  289. </View>
  290. </View>
  291. )
  292. }
  293. </View>
  294. </View>
  295. <View style={listItemStyle.divider}/>
  296. </View>
  297. );
  298. }
  299. showMenuView(event, momentId, momentUsername, callback1, callback2) {
  300. this.setState({doFavorMomentId: momentId});
  301. this.refs.momentMenuView.showModal(event.nativeEvent.pageX, event.nativeEvent.pageY, momentId, momentUsername, callback1, callback2);
  302. }
  303. doFavorSuccessCallback = (favorNames) => {
  304. let arr = this.state.moments.concat();
  305. if (!Utils.isEmpty(arr)) {
  306. for (let i = 0; i < arr.length; i++) {
  307. let momentId = arr[i].moment_id;
  308. if (momentId == this.state.doFavorMomentId) {
  309. arr[i].favor_names = favorNames;
  310. }
  311. }
  312. }
  313. this.setState({moments: arr});
  314. }
  315. doCommentSuccessCallback = (momentId, data) => {
  316. // 评论成功后,刷新列表
  317. let arr = this.state.moments.concat();
  318. if (!Utils.isEmpty(arr)) {
  319. for (let i = 0; i < arr.length; i++) {
  320. let id = arr[i].moment_id;
  321. if (momentId == id) {
  322. arr[i].replys = data;
  323. break;
  324. }
  325. }
  326. }
  327. this.setState({moments: arr});
  328. }
  329. doCommentCallback = (momentId, momentUsername) => {
  330. this.refs.replyPopWin.showModal(momentId, momentUsername, this.doCommentSuccessCallback);
  331. }
  332. componentWillUnmount() {
  333. CountEmitter.removeListener('updateMomentList', ()=>{});
  334. }
  335. }
  336. const listItemStyle = StyleSheet.create({
  337. container: {
  338. flexDirection: 'row',
  339. alignItems: 'flex-start',
  340. padding: 10,
  341. },
  342. imageContainer: {
  343. flexDirection: 'column',
  344. marginTop: 6,
  345. },
  346. imageCell: {
  347. width: 80,
  348. height: 80,
  349. marginRight: 3,
  350. },
  351. avatar: {
  352. width: 40,
  353. height: 40,
  354. },
  355. content: {
  356. flex: 1,
  357. flexDirection: 'column',
  358. marginLeft: 10,
  359. },
  360. nameText: {
  361. fontSize: 15,
  362. color: '#54688D'
  363. },
  364. msgText: {
  365. fontSize: 15,
  366. color: '#000000',
  367. marginTop: 2,
  368. },
  369. timeContainer: {
  370. flex: 1,
  371. flexDirection: 'row',
  372. alignItems: 'flex-start',
  373. marginTop: 10,
  374. },
  375. timeText: {
  376. flex: 1,
  377. fontSize: 12,
  378. color: '#999999',
  379. },
  380. commentImg: {
  381. width: 25,
  382. height: 17,
  383. },
  384. divider: {
  385. flex: 1,
  386. height: 1 / PixelRatio.get(),
  387. backgroundColor: Global.dividerColor,
  388. },
  389. commentContainer: {
  390. flex: 1,
  391. },
  392. commentContent: {
  393. backgroundColor: '#EEEEEE',
  394. padding: 6,
  395. },
  396. favorContainer: {
  397. flexDirection: 'row',
  398. alignItems: 'center',
  399. },
  400. favorImg: {
  401. width: 13,
  402. height: 13,
  403. marginRight: 5,
  404. marginTop: 5,
  405. },
  406. commentText: {
  407. flex: 1,
  408. fontSize: 13,
  409. color: '#54688D',
  410. marginTop: 2,
  411. }
  412. });
  413. const styles = StyleSheet.create({
  414. container: {
  415. flex: 1,
  416. flexDirection: 'column',
  417. backgroundColor: '#F8F8F8',
  418. },
  419. momentImg: {
  420. width: width,
  421. height: HEIGHT,
  422. marginBottom: 40,
  423. },
  424. userNameText: {
  425. color: '#FFFFFF',
  426. fontSize: 16,
  427. fontWeight: 'bold',
  428. position: 'absolute',
  429. right: 95,
  430. top: HEIGHT - 25,
  431. },
  432. avatarImg: {
  433. width: AVATAR_WIDTH,
  434. height: AVATAR_WIDTH,
  435. position: 'absolute',
  436. right: 10,
  437. top: HEIGHT - 45,
  438. borderWidth: 2,
  439. borderColor: '#FFFFFF'
  440. }
  441. });