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

140 lines
3.9 KiB
JavaScript
Raw Permalink Normal View History

2018-06-25 03:09:10 +00:00
import { Link } from 'react-router-dom'
import { Button, FormGroup, ControlLabel, FormControl, HelpBlock } from 'react-bootstrap';
import React, { Component } from 'react';
import FieldGroup from './FieldGroup';
/**
* Class that renders a form to allow the user to create
* a tweet that is stored in the contract.
*
* @extends React.Component
*/
class DoTweet extends Component{
//#region Constructor
constructor(props, context) {
super(props, context);
// initial state
this.state = {
tweet: '',
tweetHasChanged: false,
isLoading: false,
error: ''
};
this.tweetInput = null;
}
//#endregion
//#region Component events
/**
* Handles the 'Tweet' button click event which
* sends a transaction to the contract to store a
* tweet for the current user.
*
* @returns {null}
*/
_handleClick = async (e) => {
// do not post tweet if there is a form error or user has not typed anything
if(this._getValidationState() === 'error' || !this.state.tweetHasChanged){
return e.preventDefault();
}
// show loading state
this.setState({ isLoading: true });
const { username, account, onAfterTweet } = this.props;
const tweet = DTwitter.methods.tweet(this.state.tweet);
try{
// estimate gas before sending tweet transaction
// send the tweet transaction plus a little extra gas in case the contract state
// has changed since we've done our gas estimate
// remove loading state
this.setState({ isLoading: false });
// tell parent we've updated a user and to re-fetch user details from the contract
onAfterTweet();
}
catch(err){
// remove loading state and show error message
this.setState({ isLoading: false, error: err.message });
}
}
/**
* When user changes an input value, record that in the state.
*
* @param {SyntheticEvent} cross-browser wrapper around the browsers native event
*
* @return {null}
*/
_handleChange(e) {
let state = {tweetHasChanged: true};
state[e.target.name] = e.target.value;
this.setState(state);
}
//#endregion
//#region Helper methods
/**
* Validates the form. Return null for no state change,
* 'success' if valid, and error' if invalid.
*
* @return {string} null for no state change, 'success'
* if valid, and error' if invalid
*/
_getValidationState() {
return ((this.state.tweet === '' && !this.state.tweetHasChanged) || (this.state.tweet.length > 0 && this.state.tweet.length <= 140)) ? null : 'error';
}
//#endregion
//#region React lifecycle events
componentDidMount(){
// set focus to tweet textarea after render
if(this.tweetInput) this.tweetInput.focus();
}
render(){
const validationState = this._getValidationState();
const isValid = validationState !== 'error';
const { isLoading, error, tweet, tweetHasChanged } = this.state;
let feedback = !isValid ? 'Tweet must be 140 characters or less' : '';
if(this.state.error) feedback = error;
return (
<form>
<FieldGroup
type="text"
value={ tweet }
placeholder="140 characters or less..."
onChange={ (e) => this._handleChange(e) }
name="tweet"
componentClass="textarea"
hasFeedback={true}
validationState={validationState}
inputRef={(input) => { this.tweetInput = input; }}
/>
<Button
bsStyle="primary"
disabled={ !isValid || Boolean(error) || !tweetHasChanged }
onClick={ (!isValid || Boolean(error) || !tweetHasChanged) ? null : (e) => this._handleClick(e) }
>{isLoading ? 'Loading...' : 'Post tweet'}</Button>
<FormGroup
controlId="formBasicText"
validationState={ validationState }
>
<HelpBlock>{ feedback }</HelpBlock>
</FormGroup>
</form>
);
}
//#endregion
}
export default DoTweet