app/components/EditProfile.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { grey400 } from 'material-ui/styles/colors';
import IconButton from 'material-ui/IconButton';
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
import SvgCamera from 'material-ui/svg-icons/image/photo-camera';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import EventListener, { withOptions } from 'react-event-listener';
import Dialog from 'material-ui/Dialog';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
// - Import app components
import ImgCover from 'ImgCover';
import DialogTitle from 'DialogTitle';
import ImageGallery from 'ImageGallery';
import FileAPI from 'FileAPI';
import UserAvatar from 'UserAvatar';
// - Import actions
import * as userActions from 'userActions';
import * as globalActions from 'globalActions';
import * as imageGalleryActions from 'imageGalleryActions';
export class EditProfile extends Component {
/**
* Component constructor
* @param {object} props is an object properties of component
*/
constructor(props) {
super(props);
this.state = {
// If it's true the winow is in small size
isSmall: false,
// User tag line input value
tagLineInput: props.info.tagLine || '',
// User full name input value
fullNameInput: props.info.fullName || '',
// Error message of full name input
fullNameInputError: '',
// User banner address
banner: this.props.banner || 'https://firebasestorage.googleapis.com/v0/b/open-social-33d92.appspot.com/o/images%2F751145a1-9488-46fd-a97e-04018665a6d3.JPG?alt=media&token=1a1d5e21-5101-450e-9054-ea4a20e06c57',
// User avatar address
avatar: this.props.avatar || '',
// It's true if the image galley for banner is open
openBanner: false,
// It's true if the image gallery for avatar is open
openAvatar: false
};
}
// Close image gallery of banner
handleCloseBannerGallery = () => {
this.setState({ openBanner: false });
}
// Open image gallery of banner
handleOpenBannerGallery = () => {
this.setState({ openBanner: true });
}
// Close image gallery of avatar
handleCloseAvatarGallery = () => {
this.setState({ openAvatar: false });
}
// Open image gallery of avatar
handleOpenAvatarGallery = () => {
this.setState({ openAvatar: true });
}
// Set banner image url
handleRequestSetBanner = (url) => {
this.setState({ banner: url });
}
// Set avatar image url
handleRequestSetAvatar = (fileName) => {
this.setState({ avatar: fileName });
}
// Handle change date
handleChangeDate = (evt, date) => {
this.setState({ birthdayInput: date });
}
/**
* Update profile on the server
*
* @memberof EditProfile
*/
handleUpdate = () => {
const {fullNameInput, tagLineInput, avatar, banner} = this.state;
if (this.state.fullNameInput.trim() === '') {
this.setState({ fullNameInputError: 'This field is required' });
}
else {
this.setState({ fullNameInputError: '' });
this.props.update({
fullName: fullNameInput,
tagLine: tagLineInput,
avatar: avatar,
banner: banner
});
}
}
/**
* Handle data on input change
* @param {event} evt is an event of inputs of element on change
*/
handleInputChange = (evt) => {
const target = evt.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({ [target.name]: value });
}
/**
* Handle resize event for window to change sidebar status
* @param {event} evt is the event is passed by winodw resize event
*/
handleResize = (evt) => {
const width = window.innerWidth;
if (width > 900) {
this.setState({ isSmall: false });
}
else {
this.setState({ isSmall: true });
}
}
componentDidMount = () => {
this.handleResize();
}
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render() {
const styles = {
avatar: {
border: '2px solid rgb(255, 255, 255)'
},
paper: {
width: '90%',
height: '100%',
margin: '0 auto',
display: 'block',
},
title: {
padding: '24px 24px 20px 24px',
font: '500 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif',
display: 'flex',
wordWrap: 'break-word',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
flexGrow: 1
},
actions: {
display: 'flex',
justifyContent: "flex-end",
padding: '24px 24px 20px'
},
updateButton: {
marginLeft: '10px'
},
box: {
padding: '0px 24px 20px 24px',
display: 'flex'
},
dialogGallery: {
width: '',
maxWidth: '530px',
borderRadius: "4px"
}
};
const iconButtonElement = (
<IconButton style={this.state.isSmall ? styles.iconButtonSmall : styles.iconButton} iconStyle={this.state.isSmall ? styles.iconButtonSmall : styles.iconButton} touch={true}>
<MoreVertIcon color={grey400} viewBox='10 0 24 24' />
</IconButton>
);
const RightIconMenu = () => (
<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem style={{ fontSize: "14px" }}>Reply</MenuItem>
<MenuItem style={{ fontSize: "14px" }}>Edit</MenuItem>
<MenuItem style={{ fontSize: "14px" }}>Delete</MenuItem>
</IconMenu>
);
return (
<div>
{/* Edit profile dialog */}
<Dialog
id='Edit-Profile'
modal={false}
open={this.props.open}
onRequestClose={this.props.onRequestClose}
autoScrollBodyContent={true}
bodyStyle={{ backgroundColor: "none", padding: 'none', borderTop: 'none', borderBottom: 'none' }}
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
contentStyle={{ backgroundColor: "none", maxWidth: '450px', maxHeight: 'none', height: '100%' }}
style={{ backgroundColor: "none", maxHeight: 'none', height: '100%' }}
>
{/* Banner */}
<div style={{ position: 'relative' }}>
<ImgCover width='100%' height='250px' borderRadius='2px' fileName={this.state.banner} />
<div className='g__circle-black' onClick={this.handleOpenBannerGallery} style={{ position: 'absolute', right: '10px', top: '10px' }}>
<SvgCamera style={{ fill: 'rgba(255, 255, 255, 0.88)', transform: 'translate(6px, 6px)' }} />
</div>
</div>
<div className='profile__edit'>
<EventListener
target="window"
onResize={this.handleResize}
/>
<div className='left'>
<div style={{ display: 'flex', justifyContent: 'center' }}>
{/* Avatar */}
<div className='g__circle-black' onClick={this.handleOpenAvatarGallery} style={{ position: 'absolute', left: '50%', display: 'inline-block', top: '52px', margin: '-18px' }}>
<SvgCamera style={{ fill: 'rgba(255, 255, 255, 0.88)', transform: 'translate(6px, 6px)' }} />
</div>
<UserAvatar fullName={(this.props.info ? this.props.info.fullName : '')} fileName={this.state.avatar} size={90} style={styles.avatar} />
</div>
<div className='info'>
<div className='fullName'>
{this.props.fullName}
</div>
</div>
</div>
</div>
{/* Edit user information box*/}
<Paper style={styles.paper} zDepth={1}>
<div style={styles.title}>Personal Information</div>
<div style={styles.box}>
<TextField
floatingLabelText="Full name"
onChange={this.handleInputChange}
name='fullNameInput'
errorText={this.state.fullNameInputError}
value={this.state.fullNameInput}
/>
</div>
<br />
<div style={styles.box}>
<TextField
floatingLabelText="Tag Line"
onChange={this.handleInputChange}
name='tagLineInput'
value={this.state.tagLineInput}
/>
</div>
<br />
<div style={styles.actions}>
<FlatButton label="CANCEL" onClick={this.props.onRequestClose} />
<RaisedButton label="UPDATE" primary={true} onClick={this.handleUpdate} style={styles.updateButton} />
</div>
</Paper>
<div style={{ height: '16px' }}></div>
</Dialog>
{/* Image gallery for banner*/}
<Dialog
title={<DialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />}
modal={false}
open={this.state.openBanner}
contentStyle={styles.dialogGallery}
onRequestClose={this.handleCloseBannerGallery}
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
autoDetectWindowHeight={false}
>
<ImageGallery set={this.handleRequestSetBanner} close={this.handleCloseBannerGallery} />
</Dialog>
{/* Image gallery for avatar */}
<Dialog
title={<DialogTitle title='Choose an avatar image' onRequestClose={this.handleCloseAvatarGallery} />}
modal={false}
open={this.state.openAvatar}
contentStyle={styles.dialogGallery}
onRequestClose={this.handleCloseAvatarGallery}
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
autoDetectWindowHeight={false}
>
<ImageGallery set={this.handleRequestSetAvatar} close={this.handleCloseAvatarGallery} />
</Dialog>
</div>
)
}
}
/**
* Map dispatch to props
* @param {func} dispatch is the function to dispatch action to reducers
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapDispatchToProps = (dispatch, ownProps) => {
return {
update: (info) => dispatch(userActions.dbUpdateUserInfo(info)),
onRequestClose: () => dispatch(userActions.closeEditProfile())
}
}
/**
* Map state to props
* @param {object} state is the obeject from redux store
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapStateToProps = (state, ownProps) => {
return {
open: state.user.openEditProfile,
info: state.user.info[state.authorize.uid],
avatarURL: state.imageGallery.imageURLList
}
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(EditProfile)