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

140 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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