Advertisement
Guest User

Untitled

a guest
Jul 29th, 2017
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.38 KB | None | 0 0
  1. import React, { Component, PropTypes } from 'react';
  2. import {
  3. View,
  4. StyleSheet,
  5. Image,
  6. Text,
  7. StatusBar,
  8. Animated,
  9. ActivityIndicator,
  10. ToolbarAndroid,
  11. TouchableNativeFeedback,
  12. TouchableWithoutFeedback,
  13. } from 'react-native';
  14. import { connect } from 'react-redux';
  15. import moment from 'moment';
  16. import * as constants from './utils/constants';
  17. import * as actions from '../../actions/index';
  18.  
  19. const HEADER_MAX_HEIGHT = 160;
  20. const DELTA = 20;
  21. const TOOLBAR_HEIGHT = 56;
  22. const HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - TOOLBAR_HEIGHT;
  23.  
  24. const ic_close_black = require('../../assets/icons/ic_close_black.png');
  25. const ic_incoming_transaction = require('../../assets/icons/ic_incoming.png');
  26. const ic_outgoing_transaction = require('../../assets/icons/ic_payment.png');
  27. const ic_back_white = require('../../assets/icons/ic_back_white.png');
  28. const ic_settings_white = require('../../assets/icons/ic_settings_white.png');
  29. const ic_fab = require('../../assets/icons/ic_fab_money.png');
  30. const ic_empty = require('../../assets/icons/ic_profile_feed.png');
  31.  
  32. export class Profile extends Component {
  33. static propTypes = {
  34. navigation: PropTypes.shape({
  35. navigate: PropTypes.func.isRequired,
  36. dispatch: PropTypes.func.isRequired,
  37. }),
  38. };
  39. offset = 0
  40. limit = 10
  41. activity = true
  42. constructor(props) {
  43. super(props);
  44. this.state = {
  45. scrollY: new Animated.Value(0),
  46. modalVisibility: false,
  47. newTransactionModalVisibility: false,
  48. item: {},
  49. // TODO get user from props (redux)
  50. user: {
  51. id: '1568161709003113564',
  52. pic: 'http://www.gstatic.com/webp/gallery/2.jpg',
  53. balance: {
  54. token: {
  55. currency: '',
  56. amount: '',
  57. },
  58. price: {
  59. currency: '',
  60. amount: '',
  61. },
  62. },
  63. name: 'User',
  64. surname: 'User1',
  65. phone: '+1 (234) 567-8901',
  66. status: 1, // 1 - online, 0 - offline
  67. password: 'djamik123',
  68. wallet: '0x123123123123',
  69. },
  70. transactions: [],
  71. showFooter: false,
  72. arrayChanged: true,
  73. loading: false,
  74. refreshing: false,
  75. };
  76. }
  77.  
  78. componentWillMount() {
  79.  
  80. }
  81.  
  82.  
  83. componentDidMount() {
  84.  
  85. }
  86.  
  87.  
  88. // animation in order to make collapse effects
  89. getAnimationType = (type) => {
  90. switch (type) {
  91. case constants.HEADER_TRANSLATE:
  92. return this.state.scrollY.interpolate({
  93. inputRange: [0, HEADER_SCROLL_DISTANCE],
  94. outputRange: [0, -HEADER_SCROLL_DISTANCE],
  95. extrapolate: 'clamp',
  96. });
  97. case constants.HEADER_TRANSLATE2:
  98. return this.state.scrollY.interpolate({
  99. inputRange: [0, HEADER_SCROLL_DISTANCE],
  100. outputRange: [0, HEADER_SCROLL_DISTANCE],
  101. extrapolate: 'clamp',
  102. });
  103. case constants.VIEW_TRANSLATE:
  104. return this.state.scrollY.interpolate({
  105. inputRange: [0, HEADER_SCROLL_DISTANCE],
  106. outputRange: [1, 0.7],
  107. extrapolate: 'clamp',
  108. });
  109. case constants.VIEWY_TRANSLATE:
  110. return this.state.scrollY.interpolate({
  111. inputRange: [0, HEADER_SCROLL_DISTANCE],
  112. outputRange: [0, DELTA + (0.7 * DELTA)],
  113. extrapolate: 'clamp',
  114. });
  115. case constants.IMAGE_OPACITY:
  116. return this.state.scrollY.interpolate({
  117. inputRange: [0, HEADER_SCROLL_DISTANCE / 2],
  118. outputRange: [1, 0],
  119. extrapolate: 'clamp',
  120. });
  121. default:
  122. return '';
  123. }
  124. };
  125.  
  126. render() {
  127. const { user } = this.state;
  128. const { profile } = this.props;
  129. // break amount into two values to use them separately
  130. const hmqInt = user && user.balance && user.balance.token && user.balance.token.amount
  131. ? user.balance.token.amount.toString().split('.')[0] : '0';
  132. const hmqDec = user && user.balance && user.balance.token && user.balance.token.amount
  133. ? user.balance.token.amount.toString().split('.')[1] : '00';
  134. // break amount into two values to use them separately
  135. const currencyInt = user && user.balance && user.balance.price && user.balance.price.amount
  136. ? user.balance.price.amount.toString().split('.')[0] : '0';
  137. const currencyDec = user && user.balance && user.balance.price && user.balance.price.amount
  138. ? user.balance.price.amount.toString().split('.')[1] : '00';
  139.  
  140. return (
  141. <View style={styles.mainContainer}>
  142. {/* render status bar */}
  143. <StatusBar
  144. backgroundColor="#598FBA"
  145. />
  146. {/* render list */}
  147. {this.state.transactions.length > 0 ? this.renderContent() : this.renderEmptyView()}
  148. {/* render collapse layout */}
  149. <Animated.View
  150. style={[styles.collapseContainer, {
  151. transform: [{ translateY: this.getAnimationType(constants.HEADER_TRANSLATE) }],
  152. }]}
  153. >
  154. <Animated.View
  155. style={[styles.bar, {
  156. transform: [
  157. { scale: this.getAnimationType(constants.VIEW_TRANSLATE) },
  158. { translateY: this.getAnimationType(constants.VIEWY_TRANSLATE) },
  159. ],
  160. }]}
  161. >
  162. <Animated.View
  163. style={styles.avatarInfoContainer}
  164. >
  165. <Animated.Image
  166. style={styles.avatar}
  167. source={{ uri: profile.avatar ? profile.avatar.url : this.state.user.pic }}
  168. />
  169. <Animated.View style={styles.infoContainer}>
  170. <Text style={styles.title}>{`${hmqInt}.`}
  171. <Text style={{ fontSize: 17.5, color: '#DAE5EE' }}>
  172. {hmqDec || '00'} {user.balance.token.currency}
  173. </Text>
  174. </Text>
  175. <Text style={{ fontSize: 16, color: '#DAE5EE', marginTop: 3 }}>
  176. {`${currencyInt}.`}{currencyDec || '00'} {user.balance.price.currency}
  177. </Text>
  178. </Animated.View>
  179. </Animated.View>
  180. </Animated.View>
  181. {/* render toolbar */}
  182. <Animated.View style={[{
  183. transform: [{ translateY: this.getAnimationType(constants.HEADER_TRANSLATE2) }],
  184. }]}
  185. >
  186. <ToolbarAndroid
  187. onActionSelected={position => this.onActionClick(position)}
  188. style={styles.toolbar}
  189. onIconClicked={() => this.backButtonHandle()}
  190. navIcon={ic_back_white}
  191. actions={[{
  192. title: '',
  193. icon: ic_settings_white,
  194. show: 'always',
  195. }]}
  196. />
  197. </Animated.View>
  198. </Animated.View>
  199.  
  200.  
  201. </View>
  202. );
  203. }
  204.  
  205. backButtonHandle = () => {
  206. };
  207.  
  208. renderHeaderSection = header => (
  209. <Text style={styles.headerSection}>{header}</Text>
  210. );
  211.  
  212. // render list
  213. renderScrollViewContent = () => {
  214. // convert array to map
  215. let index = 0;
  216. const categoryMap = {};
  217. const headerIds = [];
  218. this.state.transactions.forEach((item) => {
  219. // sort by category (timestamp)
  220. const category = moment.unix(item.timestamp).format('DD.MM.YYYY').toString();
  221. if (!categoryMap[category]) {
  222. categoryMap[category] = [];
  223. headerIds[index] = category;
  224. index += 1;
  225. }
  226. categoryMap[category].push(item);
  227. });
  228.  
  229. return (
  230. <View style={styles.scrollViewContent}>
  231. {headerIds.map(item => categoryMap[item].map((child, childIndex) => (
  232. <View>
  233. {/* render header text */}
  234. {childIndex === 0 ? this.renderHeaderSection(item) : null}
  235. {/* render transaction item */}
  236. <Item
  237. item={child}
  238. currentIndex={childIndex}
  239. size={categoryMap[item].length}
  240. onClick={() => this.onItemClick(child)}
  241. />
  242. </View>
  243. )))}
  244. {this.state.showFooter ? this.progressIndicator() : null}
  245. </View>
  246. );
  247. };
  248.  
  249. _onRefresh() {
  250. this.setState({ refreshing: true });
  251. }
  252.  
  253. progressIndicator = () => (
  254. <Animated.View style={{ padding: 5 }}>
  255. <ActivityIndicator
  256. size={30}
  257. />
  258. </Animated.View>
  259. );
  260.  
  261. settingsButtonHandle = () => {
  262. const navState = this.props.navigation.state;
  263. this.props.navigation.navigate('ProfileSettings', { ...navState.params, user: this.state.user });
  264. };
  265.  
  266. // on fab button click handler
  267. onFabButtonPress = () => {
  268. // show newTransactionModal
  269. this.setState({ newTransactionModalVisibility: true });
  270. };
  271.  
  272. renderContent = () => (
  273. <Animated.ScrollView
  274. ref="scrollRef"
  275. style={{ backgroundColor: '#fff' }}
  276. scrollEventThrottle={15}
  277. onScroll={this.onScroll.bind(this)}
  278. >
  279. {this.renderScrollViewContent()}
  280. </Animated.ScrollView>
  281. );
  282.  
  283. isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => {
  284. let paddingToBottom = 0;
  285. paddingToBottom += layoutMeasurement.height;
  286. return contentOffset.y >= contentSize.height - paddingToBottom;
  287. };
  288.  
  289. // combining two onScroll events (Animated.Event and onScroll event)
  290. onScroll(event) {
  291. if (this.props.onScroll) this.props.onScroll(event);
  292. Animated.event(
  293. [{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }],
  294. { onScroll: this.props.onScroll }, { useNativeDriver: true },
  295. )(event);
  296. // detect if scroll reached the end of the list to load more data
  297. if (this.isCloseToBottom(event.nativeEvent)
  298. && !this.state.showFooter && this.state.arrayChanged) {
  299. this.scrollToPosition = event.nativeEvent.contentSize.height;
  300. this.setState({ showFooter: true });
  301. setTimeout(() => {
  302. if (this.activity && this.refs.scrollRef) {
  303. this.refs.scrollRef._component.scrollTo({ x: 0, y: this.scrollToPosition });
  304. this.loadMoreData();
  305. }
  306. }, 50);
  307. }
  308. }
  309. // render Empty View, if array is empty
  310. renderEmptyView = () => (
  311. <View style={styles.emptyViewContainer}>
  312. <Image
  313. resizeMode="contain"
  314. style={styles.emptyImage}
  315. source={ic_empty}
  316. />
  317. </View>
  318. );
  319.  
  320. // on transaction item click handler
  321. onItemClick = (item) => {
  322. this.setState({
  323. item,
  324. modalVisibility: true,
  325. });
  326. };
  327.  
  328. // on chat icon click handler
  329. onChatClick = () => {
  330. this.setState({
  331. modalVisibility: false,
  332. });
  333. };
  334.  
  335.  
  336. // to load more data
  337. loadMoreData() {
  338. this.getTransactions();
  339. }
  340.  
  341. componentWillUnmount() {
  342. // to prevent null pointers
  343. this.activity = false;
  344. // DeviceEventEmitter.removeAllListeners()
  345. }
  346.  
  347. // toolbar action click handler
  348. onActionClick = (position) => {
  349. switch (position) {
  350. case 0:
  351. return this.settingsButtonHandle();
  352. default:
  353. return '';
  354. }
  355. };
  356.  
  357. onRefresh() {
  358. // when pull to refresh triggers
  359. this.setState({ loading: true });
  360. setTimeout(() => this.setState({ loading: false }));
  361. }
  362.  
  363.  
  364. dismissModal() {
  365. this.setState({ newTransactionModalVisibility: false });
  366. }
  367. }
  368.  
  369. const styles = StyleSheet.create({
  370. mainContainer: {
  371. flex: 1,
  372. },
  373. collapseContainer: {
  374. position: 'absolute',
  375. top: 0,
  376. left: 0,
  377. right: 0,
  378. backgroundColor: '#598fba',
  379. overflow: 'hidden',
  380. height: HEADER_MAX_HEIGHT,
  381. },
  382. avatarInfoContainer: {
  383. flexDirection: 'row',
  384. alignItems: 'center',
  385. alignSelf: 'flex-start',
  386. marginLeft: 16,
  387. },
  388. avatar: {
  389. height: 60,
  390. width: 60,
  391. borderRadius: 65,
  392. },
  393. infoContainer: { marginLeft: 13 },
  394. bar: {
  395. backgroundColor: 'transparent',
  396. justifyContent: 'center',
  397. alignSelf: 'flex-end',
  398. position: 'absolute',
  399. left: 0,
  400. right: 0,
  401. bottom: DELTA,
  402. },
  403. title: {
  404. color: 'white',
  405. fontSize: 28,
  406. },
  407. scrollViewContent: {
  408. marginTop: HEADER_MAX_HEIGHT,
  409. backgroundColor: '#fff',
  410. },
  411. backButton: {
  412. marginLeft: 16,
  413. },
  414. settingsButton: {
  415. marginRight: 16,
  416. },
  417. headerSection: {
  418. marginLeft: 16.5,
  419. marginTop: 33,
  420. marginBottom: 13,
  421. color: '#2586C6',
  422. fontSize: 16.5,
  423. fontWeight: '500',
  424. },
  425. back: {
  426. height: 24,
  427. width: 24,
  428. },
  429. settings: {
  430. height: 24,
  431. width: 24,
  432. },
  433. toolbar: {
  434. height: TOOLBAR_HEIGHT,
  435. backgroundColor: 'transparent',
  436. },
  437. emptyViewContainer: {
  438. marginTop: HEADER_MAX_HEIGHT,
  439. flex: 1,
  440. backgroundColor: '#fff',
  441. alignItems: 'center',
  442. justifyContent: 'center',
  443. },
  444. emptyImage: {},
  445. priceInt: {
  446. fontSize: 16,
  447. color: '#000000',
  448. },
  449. priceDecimal: {
  450. color: '#8B8B8B',
  451. fontSize: 14,
  452. },
  453.  
  454. rootContainer: {
  455. justifyContent: 'flex-end',
  456. flex: 1,
  457. },
  458. content: {
  459. backgroundColor: '#ffffff',
  460. height: 250,
  461. justifyContent: 'flex-end',
  462. },
  463. closeButtonContainer: {
  464. padding: 15,
  465. alignItems: 'center',
  466. },
  467. imageContainer: {
  468. flexDirection: 'row',
  469. flex: 1,
  470. },
  471. transactionImageContainer: {
  472. padding: 10,
  473. flex: 1,
  474. alignItems: 'center',
  475. justifyContent: 'center',
  476. },
  477. });
  478.  
  479. export default connect(
  480. state => ({
  481. user: state.user,
  482. profile: state.user.profile || {},
  483. }),
  484. dispatch => ({
  485. setProfile: profile => dispatch(actions.setProfile(profile)),
  486. }),
  487. )(Profile);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement