/ app / js / components / UpdateUser.js
UpdateUser.js
  1  import { Button, FormGroup, ControlLabel, FormControl, HelpBlock, Image, Grid, Col, Row, PageHeader } from 'react-bootstrap';
  2  import { withRouter } from 'react-router-dom'
  3  import React, { Component } from 'react';
  4  import FieldGroup from './FieldGroup';
  5  
  6  class UpdateUser extends Component {
  7  
  8    //#region Constructor
  9    constructor(props, context) {
 10      super(props, context);
 11  
 12      // initial state
 13      this.state = {
 14        isLoading: false,
 15        picture: '',
 16        description: this.props.user.description,
 17        error: '',
 18        formState: null,
 19        formUpdated: false
 20      };
 21    }
 22    //#endregion
 23  
 24    //#region Component events
 25    /**
 26     * Handles the 'Update user' button click event which
 27     * sends a transaction to the contract to update the 
 28     * user's profile.
 29     * 
 30     * @returns {null}
 31     */
 32    _handleClick = async () => {
 33      // if the form has not been updated, do nothing
 34      if (!this.state.formUpdated) return;
 35      
 36      // show loading state
 37      this.setState({ isLoading: true });
 38  
 39      // if the user has updated their photo, try to upload it to ipfs
 40      // and use the resulting ipfs hash to send to the contract as part
 41      // of the user's profile.
 42      let hash = '';
 43      if (this.state.picture !== '') {
 44        try {
 45          // upload the file to ipfs and get the resulting hash
 46          hash = await EmbarkJS.Storage.uploadFile([this.inputPicture]);
 47        }
 48        catch (err) {
 49          // stop loading state and show user the error
 50          return this.setState({ isLoading: false, formState: 'error', error: err.message });
 51        }
 52      }
 53  
 54      const { account, user } = this.props;
 55      const { description } = this.state;
 56      const editAccount = DTwitter.methods.editAccount(web3.utils.keccak256(user.username), description, hash);
 57  
 58      // get a gas estimate for the transaction with the input username
 59      // and description
 60      const gasEstimate = await editAccount.estimateGas({ from: web3.eth.defaultAccount, gas: 10000000000 });
 61  
 62      try {
 63        // send the transaction with our gas estimate (plus a little bit more in case the contract)
 64        // state has changed since we got our estimate
 65        const result = await editAccount.send({ from: web3.eth.defaultAccount, gas: gasEstimate + 1000 });
 66        
 67        if (result.status && !Boolean(result.status.toString().replace('0x', ''))) {
 68          return this.setState({ isLoading: false, formState: 'error', formUpdated: false, error: 'Error executing transaction, transaction details: ' + JSON.stringify(result) });
 69        }
 70  
 71        // stop loading state, and render the form as successful
 72        this.setState({ isLoading: false, formState: 'success', formUpdated: false });
 73  
 74        // tell parent we've updated our user, so the current
 75        // user is re-fetched to get the user's details
 76        return this.props.onAfterUserUpdate();
 77      }
 78      catch (err) {
 79        // stop loading state and show user the error
 80        this.setState({ isLoading: false, formState: 'error', formUpdated: false, error: err.message });
 81      }
 82  
 83      return null;
 84    }
 85  
 86    /**
 87     * When user changes an input value, record that in the state.
 88     * Additionally, sets state that the form has been updated to 
 89     * allow for more fine validation control
 90     * 
 91     * @param {SyntheticEvent} cross-browser wrapper around the browser’s native event
 92     * 
 93     * @return {null}
 94     */
 95    _handleChange(e) {
 96      let state = { formUpdated: true };
 97      const input = e.target.name;
 98      const value = e.target.value;
 99  
100      state[input] = value;
101  
102      this.setState(state);
103    }
104    //#endergion
105  
106    //#region React lifecycle events
107    componentDidUpdate(prevProps){
108      if(this.props.user.description !== prevProps.user.description){
109        this.setState({description: this.props.user.description});
110      }
111    }
112  
113    render() {
114      const { isLoading, error, formState, formUpdated, description, picture } = this.state;
115      const { user } = this.props;
116      const feedback = formState === 'success' ? 'Saved' : error;
117      return (
118        <Grid>
119          <Row>
120            <Col xs={12}>
121              <PageHeader>Update { user.username } <small>{this.props.account}</small></PageHeader>
122            </Col>
123          </Row>
124          <Row>
125            <Col xs={12}>
126              <form onSubmit={ isLoading || !formUpdated ? null : (e) => this._handleClick(e) }>
127                <FieldGroup
128                  type="text"
129                  value={ user.username }
130                  disabled={true}
131                  name="username"
132                  label="Username"
133                />
134                <FieldGroup
135                  type="text"
136                  value={ description }
137                  placeholder="description"
138                  onChange={ (e) => this._handleChange(e) }
139                  name="description"
140                  label="Description"
141                  validationState={ formState }
142                />
143                <FieldGroup
144                  type="file"
145                  value={ picture }
146                  onChange={ (e) => this._handleChange(e) }
147                  name="picture"
148                  label="Profile picture"
149                  inputRef={ (input) => this.inputPicture = input }
150                  validationState={ formState }
151                />
152                <FormGroup>
153                  { user.picture.length ? <Image src={ user.picture } width="100" circle /> : '' }
154                </FormGroup>
155                <FormGroup>
156                  <Button
157                    bsStyle="primary"
158                    disabled={ isLoading || !formUpdated }
159                    onClick={ isLoading || !formUpdated ? null : (e) => this._handleClick(e) }
160                  >
161                    { isLoading ? 'Loading...' : 'Update profile' }
162                  </Button>
163                </FormGroup>
164                <FormGroup
165                  validationState={ formState }
166                >
167                  <HelpBlock>{ feedback }</HelpBlock>
168                </FormGroup>
169              </form>
170            </Col>
171          </Row>
172        </Grid>
173      );
174    }
175    //#endregion
176  }
177  
178  export default withRouter(UpdateUser);