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);