Advertisement
Guest User

Untitled

a guest
Jul 29th, 2017
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.10 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 {HumaniqContactsApiLib} from 'react-native-android-library-humaniq-api';
  15. import {HumaniqBlockchainApiLib} from 'react-native-android-library-humaniq-api';
  16. import Modal from 'react-native-modal';
  17. import { connect } from 'react-redux';
  18. import { HumaniqProfileApiLib } from 'react-native-android-library-humaniq-api';
  19. import moment from 'moment';
  20. import Item from './Item';
  21. import * as constants from './utils/constants';
  22. import TransactionsModal from './modals/TransactionsModal';
  23. import Fab from './shared/Fab';
  24. import * as actions from '../../actions/index';
  25.  
  26. const HEADER_MAX_HEIGHT = 160;
  27. const DELTA = 20;
  28. const TOOLBAR_HEIGHT = 56;
  29. const HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - TOOLBAR_HEIGHT;
  30.  
  31. const ic_close_black = require('../../assets/icons/ic_close_black.png');
  32. const ic_incoming_transaction = require('../../assets/icons/ic_incoming.png');
  33. const ic_outgoing_transaction = require('../../assets/icons/ic_payment.png');
  34. const ic_back_white = require('../../assets/icons/ic_back_white.png');
  35. const ic_settings_white = require('../../assets/icons/ic_settings_white.png');
  36. const ic_fab = require('../../assets/icons/ic_fab_money.png');
  37. const ic_empty = require('../../assets/icons/ic_profile_feed.png');
  38.  
  39. export class Profile extends Component {
  40. static propTypes = {
  41. navigation: PropTypes.shape({
  42. navigate: PropTypes.func.isRequired,
  43. dispatch: PropTypes.func.isRequired,
  44. }),
  45. };
  46. offset = 0
  47. limit = 10
  48. activity = true
  49. constructor(props) {
  50. super(props);
  51. this.state = {
  52. scrollY: new Animated.Value(0),
  53. modalVisibility: false,
  54. newTransactionModalVisibility: false,
  55. item: {},
  56. // TODO get user from props (redux)
  57. user: {
  58. id: '1568161709003113564',
  59. pic: 'http://www.gstatic.com/webp/gallery/2.jpg',
  60. balance: {
  61. token: {
  62. currency: '',
  63. amount: '',
  64. },
  65. price: {
  66. currency: '',
  67. amount: '',
  68. },
  69. },
  70. name: 'User',
  71. surname: 'User1',
  72. phone: '+1 (234) 567-8901',
  73. status: 1, // 1 - online, 0 - offline
  74. password: 'djamik123',
  75. wallet: '0x123123123123',
  76. },
  77. transactions: [],
  78. showFooter: false,
  79. arrayChanged: true,
  80. loading: false,
  81. refreshing: false,
  82. };
  83. }
  84.  
  85. componentWillMount() {
  86. // add listener to listen transactions status changes
  87. // DeviceEventEmitter.addListener('EVENT_TRANSACTION_CHANGED', (event) => {
  88. // // handle event
  89. // });
  90. }
  91.  
  92. async requestCameraPermission() {
  93. try {
  94. const granted = await PermissionsAndroid.request(
  95. PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
  96. {
  97. 'title': 'Cool Photo App Camera Permission',
  98. 'message': 'Cool Photo App needs access to your camera ' +
  99. 'so you can take awesome pictures.'
  100. }
  101. )
  102. if (granted === PermissionsAndroid.RESULTS.GRANTED) {
  103. console.log("You can use the camera")
  104. } else {
  105. console.log("Camera permission denied")
  106. }
  107. } catch (err) {
  108. console.warn(err)
  109. }
  110. }
  111.  
  112. componentDidMount() {
  113. this.getUserInfo();
  114. // first call transactions and balance
  115. this.getTransactions();
  116. this.getBalance();
  117. }
  118.  
  119. getUserInfo() {
  120. HumaniqProfileApiLib.getAccountProfile(this.state.user.id)
  121. .then((response) => {
  122. this.props.setProfile(response);
  123. })
  124. .catch((err) => {
  125. });
  126. }
  127.  
  128. getBalance() {
  129. // get Balance
  130. HumaniqProfileApiLib.getBalance(this.state.user.id)
  131. .then((addressState) => {
  132. const { user } = this.state;
  133. if (addressState) {
  134. user.balance.token.currency = addressState.token.currency;
  135. user.balance.token.amount = addressState.token.amount.toString();
  136. // if local currency is null, use default currency
  137. if (addressState.local) {
  138. user.balance.price.currency = addressState.local.currency;
  139. user.balance.price.amount = addressState.local.amount.toString();
  140. } else {
  141. user.balance.price.currency = addressState.default.currency;
  142. user.balance.price.amount = addressState.default.amount.toString();
  143. }
  144. }
  145. this.setState({ user });
  146. })
  147. .catch((err) => {
  148. // handle error
  149. console.log(err);
  150. });
  151. }
  152.  
  153. getTransactions() {
  154. // get Transactions
  155. HumaniqProfileApiLib.getTransactions('223344556677', this.offset, this.limit)
  156. .then((array) => {
  157. const oldArray = this.state.transactions;
  158. const newArray = [...oldArray, ...array];
  159. const arrayChanged = (oldArray.length !== array.length)
  160. && oldArray.every((element, index) => element === array[index]);
  161. // check if it returns empty or the same array
  162. if (arrayChanged) {
  163. this.offset += array.length;
  164. this.setState({
  165. transactions: newArray,
  166. showFooter: false,
  167. arrayChanged,
  168. });
  169. } else {
  170. this.setState({
  171. showFooter: false,
  172. arrayChanged,
  173. });
  174. // to prevent loop for footerView (with indicator)
  175. setTimeout(() => {
  176. if (this.activity) {
  177. this.setState({ arrayChanged: true });
  178. }
  179. }, 100);
  180. }
  181. })
  182. .catch((err) => {
  183. // handle error
  184. console.log(err);
  185. });
  186. }
  187.  
  188. // animation in order to make collapse effects
  189. getAnimationType = (type) => {
  190. switch (type) {
  191. case constants.HEADER_TRANSLATE:
  192. return this.state.scrollY.interpolate({
  193. inputRange: [0, HEADER_SCROLL_DISTANCE],
  194. outputRange: [0, -HEADER_SCROLL_DISTANCE],
  195. extrapolate: 'clamp',
  196. });
  197. case constants.HEADER_TRANSLATE2:
  198. return this.state.scrollY.interpolate({
  199. inputRange: [0, HEADER_SCROLL_DISTANCE],
  200. outputRange: [0, HEADER_SCROLL_DISTANCE],
  201. extrapolate: 'clamp',
  202. });
  203. case constants.VIEW_TRANSLATE:
  204. return this.state.scrollY.interpolate({
  205. inputRange: [0, HEADER_SCROLL_DISTANCE],
  206. outputRange: [1, 0.7],
  207. extrapolate: 'clamp',
  208. });
  209. case constants.VIEWY_TRANSLATE:
  210. return this.state.scrollY.interpolate({
  211. inputRange: [0, HEADER_SCROLL_DISTANCE],
  212. outputRange: [0, DELTA + (0.7 * DELTA)],
  213. extrapolate: 'clamp',
  214. });
  215. case constants.IMAGE_OPACITY:
  216. return this.state.scrollY.interpolate({
  217. inputRange: [0, HEADER_SCROLL_DISTANCE / 2],
  218. outputRange: [1, 0],
  219. extrapolate: 'clamp',
  220. });
  221. default:
  222. return '';
  223. }
  224. };
  225.  
  226. render() {
  227. const { user } = this.state;
  228. const { profile } = this.props;
  229. // break amount into two values to use them separately
  230. const hmqInt = user && user.balance && user.balance.token && user.balance.token.amount
  231. ? user.balance.token.amount.toString().split('.')[0] : '0';
  232. const hmqDec = user && user.balance && user.balance.token && user.balance.token.amount
  233. ? user.balance.token.amount.toString().split('.')[1] : '00';
  234. // break amount into two values to use them separately
  235. const currencyInt = user && user.balance && user.balance.price && user.balance.price.amount
  236. ? user.balance.price.amount.toString().split('.')[0] : '0';
  237. const currencyDec = user && user.balance && user.balance.price && user.balance.price.amount
  238. ? user.balance.price.amount.toString().split('.')[1] : '00';
  239.  
  240. return (
  241. <View style={styles.mainContainer}>
  242. {/* render status bar */}
  243. <StatusBar
  244. backgroundColor="#598FBA"
  245. />
  246. {/* render list */}
  247. {this.state.transactions.length > 0 ? this.renderContent() : this.renderEmptyView()}
  248. {/* render collapse layout */}
  249. <Animated.View
  250. style={[styles.collapseContainer, {
  251. transform: [{ translateY: this.getAnimationType(constants.HEADER_TRANSLATE) }],
  252. }]}
  253. >
  254. <Animated.View
  255. style={[styles.bar, {
  256. transform: [
  257. { scale: this.getAnimationType(constants.VIEW_TRANSLATE) },
  258. { translateY: this.getAnimationType(constants.VIEWY_TRANSLATE) },
  259. ],
  260. }]}
  261. >
  262. <Animated.View
  263. style={styles.avatarInfoContainer}
  264. >
  265. <Animated.Image
  266. style={styles.avatar}
  267. source={{ uri: profile.avatar ? profile.avatar.url : this.state.user.pic }}
  268. />
  269. <Animated.View style={styles.infoContainer}>
  270. <Text style={styles.title}>{`${hmqInt}.`}
  271. <Text style={{ fontSize: 17.5, color: '#DAE5EE' }}>
  272. {hmqDec || '00'} {user.balance.token.currency}
  273. </Text>
  274. </Text>
  275. <Text style={{ fontSize: 16, color: '#DAE5EE', marginTop: 3 }}>
  276. {`${currencyInt}.`}{currencyDec || '00'} {user.balance.price.currency}
  277. </Text>
  278. </Animated.View>
  279. </Animated.View>
  280. </Animated.View>
  281. {/* render toolbar */}
  282. <Animated.View style={[{
  283. transform: [{ translateY: this.getAnimationType(constants.HEADER_TRANSLATE2) }],
  284. }]}
  285. >
  286. <ToolbarAndroid
  287. onActionSelected={position => this.onActionClick(position)}
  288. style={styles.toolbar}
  289. onIconClicked={() => this.backButtonHandle()}
  290. navIcon={ic_back_white}
  291. actions={[{
  292. title: '',
  293. icon: ic_settings_white,
  294. show: 'always',
  295. }]}
  296. />
  297. </Animated.View>
  298. </Animated.View>
  299. {/* render fab button */}
  300. {this.renderFabButton()}
  301. {/* render transaction modal */}
  302. {this.showTransactionsModal()}
  303. {this.showNewTransactionsModal()}
  304. </View>
  305. );
  306. }
  307.  
  308. backButtonHandle = () => {
  309. };
  310.  
  311. renderHeaderSection = header => (
  312. <Text style={styles.headerSection}>{header}</Text>
  313. );
  314.  
  315. // render list
  316. renderScrollViewContent = () => {
  317. // convert array to map
  318. let index = 0;
  319. const categoryMap = {};
  320. const headerIds = [];
  321. this.state.transactions.forEach((item) => {
  322. // sort by category (timestamp)
  323. const category = moment.unix(item.timestamp).format('DD.MM.YYYY').toString();
  324. if (!categoryMap[category]) {
  325. categoryMap[category] = [];
  326. headerIds[index] = category;
  327. index += 1;
  328. }
  329. categoryMap[category].push(item);
  330. });
  331.  
  332. return (
  333. <View style={styles.scrollViewContent}>
  334. {headerIds.map(item => categoryMap[item].map((child, childIndex) => (
  335. <View>
  336. {/* render header text */}
  337. {childIndex === 0 ? this.renderHeaderSection(item) : null}
  338. {/* render transaction item */}
  339. <Item
  340. item={child}
  341. currentIndex={childIndex}
  342. size={categoryMap[item].length}
  343. onClick={() => this.onItemClick(child)}
  344. />
  345. </View>
  346. )))}
  347. {this.state.showFooter ? this.progressIndicator() : null}
  348. </View>
  349. );
  350. };
  351.  
  352. _onRefresh() {
  353. this.setState({ refreshing: true });
  354. }
  355.  
  356. progressIndicator = () => (
  357. <Animated.View style={{ padding: 5 }}>
  358. <ActivityIndicator
  359. size={30}
  360. />
  361. </Animated.View>
  362. );
  363.  
  364. settingsButtonHandle = () => {
  365. const navState = this.props.navigation.state;
  366. this.props.navigation.navigate('ProfileSettings', { ...navState.params, user: this.state.user });
  367. };
  368.  
  369. // on fab button click handler
  370. onFabButtonPress = () => {
  371. // show newTransactionModal
  372. this.setState({ newTransactionModalVisibility: true });
  373. };
  374.  
  375. renderContent = () => (
  376. <Animated.ScrollView
  377. ref="scrollRef"
  378. style={{ backgroundColor: '#fff' }}
  379. scrollEventThrottle={15}
  380. onScroll={this.onScroll.bind(this)}
  381. >
  382. {this.renderScrollViewContent()}
  383. </Animated.ScrollView>
  384. );
  385.  
  386. isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => {
  387. let paddingToBottom = 0;
  388. paddingToBottom += layoutMeasurement.height;
  389. return contentOffset.y >= contentSize.height - paddingToBottom;
  390. };
  391.  
  392. // combining two onScroll events (Animated.Event and onScroll event)
  393. onScroll(event) {
  394. if (this.props.onScroll) this.props.onScroll(event);
  395. Animated.event(
  396. [{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }],
  397. { onScroll: this.props.onScroll }, { useNativeDriver: true },
  398. )(event);
  399. // detect if scroll reached the end of the list to load more data
  400. if (this.isCloseToBottom(event.nativeEvent)
  401. && !this.state.showFooter && this.state.arrayChanged) {
  402. this.scrollToPosition = event.nativeEvent.contentSize.height;
  403. this.setState({ showFooter: true });
  404. setTimeout(() => {
  405. if (this.activity && this.refs.scrollRef) {
  406. this.refs.scrollRef._component.scrollTo({ x: 0, y: this.scrollToPosition });
  407. this.loadMoreData();
  408. }
  409. }, 50);
  410. }
  411. }
  412. // render Empty View, if array is empty
  413. renderEmptyView = () => (
  414. <View style={styles.emptyViewContainer}>
  415. <Image
  416. resizeMode="contain"
  417. style={styles.emptyImage}
  418. source={ic_empty}
  419. />
  420. </View>
  421. );
  422.  
  423. // on transaction item click handler
  424. onItemClick = (item) => {
  425. this.setState({
  426. item,
  427. modalVisibility: true,
  428. });
  429. };
  430.  
  431. // on chat icon click handler
  432. onChatClick = () => {
  433. this.setState({
  434. modalVisibility: false,
  435. });
  436. };
  437.  
  438. // rendering fab button
  439. renderFabButton = () => (
  440. <Fab
  441. onClick={() => this.onFabButtonPress()}
  442. source={ic_fab}
  443. scroll={this.state.scrollY}
  444. opacity={this.getAnimationType(constants.IMAGE_OPACITY)}
  445. />);
  446.  
  447. // to load more data
  448. loadMoreData() {
  449. this.getTransactions();
  450. }
  451.  
  452. componentWillUnmount() {
  453. // to prevent null pointers
  454. this.activity = false;
  455. // DeviceEventEmitter.removeAllListeners()
  456. }
  457.  
  458. // toolbar action click handler
  459. onActionClick = (position) => {
  460. switch (position) {
  461. case 0:
  462. return this.settingsButtonHandle();
  463. default:
  464. return '';
  465. }
  466. };
  467.  
  468. onRefresh() {
  469. // when pull to refresh triggers
  470. this.setState({ loading: true });
  471. setTimeout(() => this.setState({ loading: false }));
  472. }
  473.  
  474. showTransactionsModal() {
  475. return (
  476. <TransactionsModal
  477. onCancelClick={() => this.setState({ modalVisibility: false })}
  478. onChatClick={() => this.onChatClick()}
  479. item={this.state.item}
  480. visibility={this.state.modalVisibility}
  481. />
  482. );
  483. }
  484.  
  485. showNewTransactionsModal() {
  486. return (
  487. <Modal
  488. style={{ margin: 0 }}
  489. isVisible={this.state.newTransactionModalVisibility}
  490. >
  491. <TouchableWithoutFeedback onPress={() => this.dismissModal()}>
  492. <View style={styles.rootContainer}>
  493. <TouchableWithoutFeedback onPress={() => {}}>
  494. <View style={styles.content}>
  495.  
  496. <View style={styles.imageContainer}>
  497. <TouchableNativeFeedback onPress={() => this.dismissModal()}>
  498. <View style={styles.transactionImageContainer}>
  499. <Image source={ic_incoming_transaction} />
  500. </View>
  501. </TouchableNativeFeedback>
  502.  
  503. <TouchableNativeFeedback onPress={() => this.dismissModal()}>
  504. <View style={styles.transactionImageContainer}>
  505. <Image source={ic_outgoing_transaction} />
  506. </View>
  507. </TouchableNativeFeedback>
  508. </View>
  509.  
  510. <View style={{ backgroundColor: '#e0e0e0', height: 1 }} />
  511. <TouchableNativeFeedback onPress={() => this.dismissModal()}>
  512. <View style={styles.closeButtonContainer}>
  513. <Image source={ic_close_black} />
  514. </View>
  515. </TouchableNativeFeedback>
  516. </View>
  517. </TouchableWithoutFeedback>
  518.  
  519. </View>
  520. </TouchableWithoutFeedback>
  521. </Modal>
  522. );
  523. }
  524.  
  525. dismissModal() {
  526. this.setState({ newTransactionModalVisibility: false });
  527. }
  528. }
  529.  
  530. const styles = StyleSheet.create({
  531. mainContainer: {
  532. flex: 1,
  533. },
  534. collapseContainer: {
  535. position: 'absolute',
  536. top: 0,
  537. left: 0,
  538. right: 0,
  539. backgroundColor: '#598fba',
  540. overflow: 'hidden',
  541. height: HEADER_MAX_HEIGHT,
  542. },
  543. avatarInfoContainer: {
  544. flexDirection: 'row',
  545. alignItems: 'center',
  546. alignSelf: 'flex-start',
  547. marginLeft: 16,
  548. },
  549. avatar: {
  550. height: 60,
  551. width: 60,
  552. borderRadius: 65,
  553. },
  554. infoContainer: { marginLeft: 13 },
  555. bar: {
  556. backgroundColor: 'transparent',
  557. justifyContent: 'center',
  558. alignSelf: 'flex-end',
  559. position: 'absolute',
  560. left: 0,
  561. right: 0,
  562. bottom: DELTA,
  563. },
  564. title: {
  565. color: 'white',
  566. fontSize: 28,
  567. },
  568. scrollViewContent: {
  569. marginTop: HEADER_MAX_HEIGHT,
  570. backgroundColor: '#fff',
  571. },
  572. backButton: {
  573. marginLeft: 16,
  574. },
  575. settingsButton: {
  576. marginRight: 16,
  577. },
  578. headerSection: {
  579. marginLeft: 16.5,
  580. marginTop: 33,
  581. marginBottom: 13,
  582. color: '#2586C6',
  583. fontSize: 16.5,
  584. fontWeight: '500',
  585. },
  586. back: {
  587. height: 24,
  588. width: 24,
  589. },
  590. settings: {
  591. height: 24,
  592. width: 24,
  593. },
  594. toolbar: {
  595. height: TOOLBAR_HEIGHT,
  596. backgroundColor: 'transparent',
  597. },
  598. emptyViewContainer: {
  599. marginTop: HEADER_MAX_HEIGHT,
  600. flex: 1,
  601. backgroundColor: '#fff',
  602. alignItems: 'center',
  603. justifyContent: 'center',
  604. },
  605. emptyImage: {},
  606. priceInt: {
  607. fontSize: 16,
  608. color: '#000000',
  609. },
  610. priceDecimal: {
  611. color: '#8B8B8B',
  612. fontSize: 14,
  613. },
  614.  
  615. rootContainer: {
  616. justifyContent: 'flex-end',
  617. flex: 1,
  618. },
  619. content: {
  620. backgroundColor: '#ffffff',
  621. height: 250,
  622. justifyContent: 'flex-end',
  623. },
  624. closeButtonContainer: {
  625. padding: 15,
  626. alignItems: 'center',
  627. },
  628. imageContainer: {
  629. flexDirection: 'row',
  630. flex: 1,
  631. },
  632. transactionImageContainer: {
  633. padding: 10,
  634. flex: 1,
  635. alignItems: 'center',
  636. justifyContent: 'center',
  637. },
  638. });
  639.  
  640. export default connect(
  641. state => ({
  642. user: state.user,
  643. profile: state.user.profile || {},
  644. }),
  645. dispatch => ({
  646. setProfile: profile => dispatch(actions.setProfile(profile)),
  647. }),
  648. )(Profile);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement