/ app / js / components / UserTweets.js
UserTweets.js
  1  import { Grid, Row, Col, Thumbnail, ListGroup, ListGroupItem, PageHeader } from 'react-bootstrap';
  2  import React, { Component } from 'react';
  3  import imgAvatar from '../../img/avatar-default.png';
  4  import { formatDistance } from 'date-fns/esm'
  5  
  6  // The Player looks up the player using the number parsed from
  7  // the URL's pathname. If no player is found with the given
  8  // number, then a "player not found" message is displayed.
  9  class UserTweets extends Component {
 10    
 11    //#region Constructor
 12    constructor(props, context){
 13      super(props, context);
 14      this.state = {
 15        user: {},
 16        tweets: []
 17      };
 18      this.event = null;
 19    }
 20    //#endregion
 21  
 22    //#region Helper methods
 23    /**
 24     * Get the user details and subscribe to their tweet event
 25     */
 26    _init(){
 27      const { username } = this.props.match.params;
 28      this._getUserDetails(username);
 29  
 30      // subscribe to tweet events
 31      this._subscribeToNewTweetEvent(username);
 32    }
 33  
 34    /**
 35     * Fetches the user's details from the contract for display
 36     */
 37    _getUserDetails = async(username) => {
 38        // get user details and update state
 39        let user = await DTwitter.methods.users(web3.utils.keccak256(username)).call();
 40  
 41        // update picture url for ipfs
 42        user.picture = user.picture.length > 0 ? EmbarkJS.Storage.getUrl(user.picture) : imgAvatar;
 43        
 44        // prettify creation date
 45        user.creationDate = this._formatDate(user.creationDate);
 46        
 47        this.setState({user: user});
 48    }
 49  
 50    /**
 51     * Subscribes to a tweet event from the contract.
 52     * When a tweet is received, it is appended to the list of
 53     * tweets.
 54     * 
 55     * @param {String} username 
 56     * @returns {null}
 57     */
 58    _subscribeToNewTweetEvent(username){
 59      this.event = DTwitter.events.NewTweet({
 60          filter: {_from: web3.utils.keccak256(username)}, 
 61          fromBlock: 1
 62        }, (err, event) => {
 63          if (err){
 64            this.props.onError(err, 'UserTweets._subscribeToNewTweetEvent');
 65          }
 66        })
 67        .on('data', (event) => {
 68          let tweets = this.state.tweets;
 69          tweets.push({
 70            content: event.returnValues.tweet,
 71            
 72            // prettify our tweet date
 73            time: this._formatDate(event.returnValues.time)
 74          });
 75          this.setState({tweets: tweets});
 76        })
 77        .on('error', function(error){
 78          this.props.onError(err, 'UserTweets._subscribeToNewTweetEvent');
 79        });
 80    }
 81  
 82    /**
 83     * Formats an int date into a displayable date
 84     * @param {Number} intDate - date in seconds
 85     * @returns {String} prettyfied date
 86     */
 87    _formatDate(intDate){
 88      const padZeros = 13 - intDate.length;
 89      if(padZeros > 0){
 90        intDate *= Math.pow(10, padZeros);
 91      }
 92      return formatDistance(new Date(intDate), new Date()) + ' ago';
 93    }
 94    //#endregion
 95  
 96    //#region React lifecycle events
 97    /**
 98     * Get the user details and subscribe to their tweet event
 99     */
100    componentDidMount(){
101      EmbarkJS.onReady((err) => {
102        this._init();
103      });
104    }
105  
106    /**
107     * If the username was changed (ie redirected from a new route),
108     * we need to get the new user's details and subscribe to their tweet
109     * event.
110     */
111    componentDidUpdate(prevProps){
112      if(this.props.match.params.username !== prevProps.match.params.username){
113        this._init();
114      }
115    }
116  
117    /**
118     * Unsubscribe from our tweet event so we stop
119     * receiving tweets.
120     */
121    componentWillUnmount(){
122      if(!this.event) return;
123      
124      // unsubscribe from our contract event listening for tweets
125      this.event.unsubscribe();
126    }
127  
128    render(){
129      const {user} = this.state;
130  
131      if (user === {}) {
132        // Render loading state ...
133        return (<Grid><Row><Col xs={12}>Loading...</Col></Row></Grid>);
134      } else if (user.username === ''){
135        return (
136        <Grid>
137          <Row>
138            <Col xs={12}>
139              <PageHeader>{ this.props.match.params.username } <small>doesn't exist!</small></PageHeader>
140            </Col>
141          </Row>
142        </Grid>);  
143      }else {
144        // Render real UI ...
145        const {username, description, picture, creationDate} = user;
146        const tweetList = this.state.tweets.map(function(tweet, index){
147                            return <ListGroupItem className='tweet' key={ index } header={ tweet.time }>{ tweet.content }</ListGroupItem>
148                          });
149        return (
150          <Grid>
151            <Row>
152              <Col xs={12}>
153                <PageHeader>{ username }'s <small>tweets</small></PageHeader>
154              </Col>
155            </Row>
156            <Row>
157              <Col xs={4}>
158                <Thumbnail src={picture} alt={username} className='profilePic'>
159                  <h3>{ username }</h3>
160                  <p>{ description }</p>
161                  <p className='created'>Created { creationDate }</p>
162                </Thumbnail>
163                
164              </Col>
165              <Col xs={8}>
166                <ListGroup className='tweets'>
167                  { tweetList }
168                </ListGroup>
169              </Col>
170            </Row>
171          </Grid>
172        )
173      }
174    }
175    //#endregion
176  }
177  export default UserTweets