embark-dtwitter-workshop/app/js/components/UserTweets.js

170 lines
4.6 KiB
JavaScript

import { Grid, Row, Col, Thumbnail, ListGroup, ListGroupItem, PageHeader } from 'react-bootstrap';
import React, { Component } from 'react';
import imgAvatar from '../../img/avatar-default.png';
import { formatDistance } from 'date-fns/esm'
import { EventEmitter } from 'events';
// The Player looks up the player using the number parsed from
// the URL's pathname. If no player is found with the given
// number, then a "player not found" message is displayed.
class UserTweets extends Component {
//#region Constructor
constructor(props, context){
super(props, context);
this.state = {
user: {},
tweets: []
};
this.event = null;
}
//#endregion
//#region Helper methods
/**
* Get the user details and subscribe to their tweet event
*/
_init(){
const { username } = this.props.match.params;
this._getUserDetails(username);
// subscribe to tweet events
this._subscribeToNewTweetEvent(username);
}
/**
* Fetches the user's details from the contract for display
*/
_getUserDetails = async(username) => {
// get user details and update state
let user = { creationDate: '' } // remove me
// update picture url for ipfs
// format the user.creationDate for display
user.creationDate = this._formatDate(user.creationDate);
this.setState({user: user});
}
/**
* Subscribes to a tweet event from the contract.
* When a tweet is received, it is appended to the list of
* tweets.
*
* @param {String} username
* @returns {null}
*/
_subscribeToNewTweetEvent(username){
this.event = new EventEmitter() // replace me with the NewTweet subscription
.on('data', (event) => {
let tweets = this.state.tweets;
tweets.push({
content: event.returnValues.tweet,
time: this._formatDate(event.returnValues.time)
});
this.setState({tweets: tweets});
})
.on('error', function(error){
this.props.onError(err, 'UserTweets._subscribeToNewTweetEvent');
});
}
/**
* Formats an int date into a displayable date
* @param {Number} intDate - date in seconds
* @returns {String} prettyfied date
*/
_formatDate(intDate){
const padZeros = 13 - intDate.length;
if(padZeros > 0){
intDate *= Math.pow(10, padZeros);
}
return formatDistance(new Date(intDate), new Date()) + ' ago';
}
//#endregion
//#region React lifecycle events
/**
* Get the user details and subscribe to their tweet event
*/
componentDidMount(){
EmbarkJS.onReady((err) => {
this._init();
});
}
/**
* If the username was changed (ie redirected from a new route),
* we need to get the new user's details and subscribe to their tweet
* event.
*/
componentDidUpdate(prevProps){
if(this.props.match.params.username !== prevProps.match.params.username){
this._init();
}
}
/**
* Unsubscribe from our tweet event so we stop
* receiving tweets.
*/
componentWillUnmount(){
if(!this.event) return;
// unsubscribe from our contract event listening for tweets
this.event.unsubscribe();
}
render(){
const {user} = this.state;
if (user === {}) {
// Render loading state ...
return (<Grid><Row><Col xs={12}>Loading...</Col></Row></Grid>);
} else if (user.username === ''){
return (
<Grid>
<Row>
<Col xs={12}>
<PageHeader>{ this.props.match.params.username } <small>doesn't exist!</small></PageHeader>
</Col>
</Row>
</Grid>);
}else {
// Render real UI ...
const {username, description, picture, creationDate} = user;
const tweetList = this.state.tweets.map(function(tweet, index){
return <ListGroupItem className='tweet' key={ index } header={ tweet.time }>{ tweet.content }</ListGroupItem>
});
return (
<Grid>
<Row>
<Col xs={12}>
<PageHeader>{ username }'s <small>tweets</small></PageHeader>
</Col>
</Row>
<Row>
<Col xs={4}>
<Thumbnail src={picture} alt={username} className='profilePic'>
<h3>{ username }</h3>
<p>{ description }</p>
<p className='created'>Created { creationDate }</p>
</Thumbnail>
</Col>
<Col xs={8}>
<ListGroup className='tweets'>
{ tweetList }
</ListGroup>
</Col>
</Row>
</Grid>
)
}
}
//#endregion
}
export default UserTweets