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