app/components/PostWrite.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ListItem } from 'material-ui/List';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import { grey400, grey800 } from 'material-ui/styles/colors';
import IconButton from 'material-ui/IconButton';
import TextField from 'material-ui/TextField';
import MenuItem from 'material-ui/MenuItem';
import SvgRemoveImage from 'material-ui/svg-icons/content/remove-circle';
import SvgCamera from 'material-ui/svg-icons/image/photo-camera';
import IconMenu from 'material-ui/IconMenu';
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
// - Import app components
import ImageGallery from 'ImageGallery';
import Img from 'Img';
import UserAvatar from 'UserAvatar';
// - Import API
import * as AuthAPI from 'AuthAPI';
import * as PostAPI from 'PostAPI';
// - Import actions
import * as imageGalleryActions from 'imageGalleryActions';
import * as postActions from 'postActions';
export class PostWrite extends Component {
/**
* Component constructor
* @param {object} props is an object properties of component
*/
constructor(props) {
super(props);
this.state = {
// Post text.
postText: this.props.edit ? this.props.text : '',
// The URL image of the post.
image: this.props.edit ? this.props.image : '',
// The path identifier of image on the server.
imageFullPath: this.props.edit ? this.props.imageFullPath : '',
// If it's true gallery will be open.
galleryOpen: false,
// If it's true post button will be disabled.
disabledPost: true,
// If it's true comment will be disabled on post.
disableComments: this.props.edit ? this.props.disableComments : false,
// If it's true share will be disabled on post.
disableSharing: this.props.edit ? this.props.disableSharing : false
};
}
/**
* Toggle comments of the post to disable/enable
*
* @memberof PostWrite
*/
handleToggleComments = () => {
this.setState({
disableComments: !this.state.disableComments,
disabledPost: false
});
}
/**
* Toggle sharing of the post to disable/enable
*
* @memberof PostWrite
*/
handleToggleSharing = () => {
this.setState({
disableSharing: !this.state.disableSharing,
disabledPost: false
});
}
/**
* Romove the image of post
*
* @memberof PostWrite
*/
handleRemoveImage = () => {
this.setState({
image: '',
imageFullPath: '',
disabledPost: false
});
}
/**
* Handle send post to the server
* @param {event} evt passed by clicking on the post button
*/
handlePost = (evt) => {
const {
image,
imageFullPath,
disableComments,
disableSharing,
postText } = this.state;
const {
id,
avatar,
name,
edit,
onRequestClose,
post,
update } = this.props;
const tags = PostAPI.getContentTags(postText);
// In edit status we should fire update if not we should fire post function
if (!edit) {
if (image !== '') {
post({
body: postText,
tags: tags,
image: image,
imageFullPath: imageFullPath,
avatar: avatar,
name: name,
disableComments: disableComments,
disableSharing: disableSharing
}, onRequestClose);
}
else {
post({
body: postText,
tags: tags,
avatar: avatar,
name: name,
disableComments: disableComments,
disableSharing: disableSharing
}, onRequestClose);
}
}
// In edit status we pass post to update functions
else {
update({
id: id,
body: postText,
tags: tags,
image: image,
imageFullPath: imageFullPath,
disableComments: disableComments,
disableSharing: disableSharing
}, onRequestClose);
}
}
// Set post image url
onRequestSetImage = (url, fullPath) => {
this.setState({
image: url,
imageFullPath: fullPath,
disabledPost: false
});
}
/**
* When the post text changed
* @param {event} evt is an event passed by change post text callback funciton
* @param {string} data is the post content which user writes
*/
handleOnChange = (evt, data) => {
if (data.length === 0 || data.trim() === '' || (this.props.edit && data.trim() === this.props.text)) {
this.setState({
postText: data,
disabledPost: true
});
}
else {
this.setState({
postText: data,
disabledPost: false
});
}
}
// Close image gallery
handleCloseGallery = () => {
this.setState({ galleryOpen: false });
}
// Open image gallery
handleOpenGallery = () => {
this.setState({ galleryOpen: true });
}
componentWillReceiveProps(nextProps) {
if (!nextProps.open) {
this.setState({
// Post text
postText: this.props.edit ? this.props.text : '',
// The image of the post
image: this.props.edit ? this.props.image : '',
// If it's true gallery will be open
galleryOpen: false,
// If it's true post button will be disabled
disabledPost: true,
// If it's true comment will be disabled on post
disableComments: this.props.edit ? this.props.disableComments : false,
// If it's true share will be disabled on post
disableSharing: this.props.edit ? this.props.disableSharing : false
});
}
}
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render() {
const iconButtonElement = (
<IconButton
touch={true}
tooltipPosition="bottom-left"
>
<MoreVertIcon color={grey400} />
</IconButton>
);
const rightIconMenu = (
<IconMenu iconButtonElement={iconButtonElement} style={{transform: 'rotate(90deg)'}} >
<MenuItem onClick={this.handleToggleComments} style={{ fontSize: "14px" }}>{!this.state.disableComments ? 'Disable comments' : 'Enable comments'} </MenuItem>
<MenuItem onClick={this.handleToggleSharing} style={{ fontSize: "14px" }}>{!this.state.disableSharing ? 'Disable sharing' : 'Enable sharing'}</MenuItem>
</IconMenu>
);
const postAvatar = (<UserAvatar fullName={this.props.name} fileName={this.props.avatar} style={{ top: "8px" }} size={40} />);
const author = (
<div>
<span style={{
fontSize: "14px",
paddingRight: "10px",
fontWeight: 400,
color: "rgba(0,0,0,0.87)",
textOverflow: "ellipsis",
overflow: "hidden",
paddingLeft: "50px",
lineHeight: "25px"
}}>{this.props.name}</span>
</div>
);
const writeActions = [
<FlatButton
label="Cancel"
primary={true}
keyboardFocused={false}
onTouchTap={this.props.onRequestClose}
style={{ color: grey800 }}
/>,
<FlatButton
label={this.props.edit ? 'UPDATE' : 'POST'}
primary={true}
keyboardFocused={false}
onTouchTap={this.handlePost}
disabled={this.state.disabledPost}
/>
];
const galleryActions = [
<FlatButton
label="Cancel"
primary={true}
keyboardFocused={false}
onTouchTap={this.handleCloseGallery}
style={{ color: grey800 }}
/>
];
const styles = {
dialog: {
width: '',
maxWidth: '530px',
borderRadius: "4px"
}
};
return (
<div style={this.props.style}>
{this.props.children}
<Dialog
id={this.props.id || 0}
actions={writeActions}
modal={false}
open={this.props.open}
contentStyle={styles.dialog}
onRequestClose={this.props.onRequestClose}
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
bodyStyle={{ padding: 0 }}
autoDetectWindowHeight={false}
actionsContainerStyle={{ borderTop: "1px solid rgb(224, 224, 224)" }}
>
<ListItem
disabled={true}
leftAvatar={postAvatar}
rightIconButton={rightIconMenu}
primaryText={author}
style={{ padding: "16px 4px 30px 16px" }}
/>
<div style={{ display: "flex", flexDirection: "column", flexGrow: 1, overflow: "hidden" }}>
<div style={{ position: "relative", flexDirection: "column", display: "flex", flexGrow: 1, overflow: "hidden", overflowY: "auto", maxHeight: "300px" }}>
<TextField
value={this.state.postText}
onChange={this.handleOnChange}
hintText="What is new with you?"
underlineShow={false}
multiLine={true}
hintStyle={{ fontWeight: 200, fontSize: "16px" }}
textareaStyle={{ fontWeight: 200, fontSize: "14px" }}
style={{ margin: "0 16px", flexShrink: 0, width: "initial", flexGrow: 1 }}
/>
{(this.state.image && this.state.image !== '')
? (<div>
<div style={{ position: "relative", overflowY: "hidden", overflowX: "auto" }}>
<ul style={{ position: "relative", whiteSpace: "nowrap", padding: "0 0 0 16px", margin: "8px 0 0 0", paddingRight: "16px", verticalAlign: "bottom", flexShrink: 0, listStyleType: "none" }}>
<div style={{ display: "flex", position: "relative" }}>
<span onClick={this.handleRemoveImage} style={{
position: "absolute", width: "28px", backgroundColor: "rgba(255, 255, 255, 0.22)",
height: "28px", right: 12, top: 4, cursor: "pointer", borderRadius: "50%",
display: "flex", alignItems: "center", justifyContent: "center"
}}>
<SvgRemoveImage hoverColor="rgba(0, 0, 0, 0.65)" style={{ color: "rgba(0, 0, 0, 0.53)" }} />
</span>
<div style={{ display: "inline-block", width: "100%", marginRight: "8px", transition: "transform .25s" }}>
<li style={{ width: "100%", margin: 0, verticalAlign: "bottom", position: "static" }}>
<Img fileName={this.state.image} style={{ width: "100%", height: "auto" }} />
</li>
</div>
</div>
</ul>
</div>
</div>) : ''}
</div>
<div style={{ flexShrink: 0, boxFlex: 0, flexGrow: 0, maxHeight: "48px", width: "100%" }}>
<div style={{ flexDirection: "row", display: "flex" }}>
<div onClick={this.handleOpenGallery} style={{ outline: "none", width: "48px", zIndex: 0, overflow: "hidden", position: "relative", textAlign: "center", transition: "background .3s", border: 0, borderRadius: "50%", display: "inlineBlock", height: "48px" }}>
<span style={{ top: "15px", display: "block", position: "relative", cursor: "pointer" }}>
<SvgCamera color="grey" />
</span>
</div>
</div>
</div>
</div>
</Dialog>
<Dialog
actions={galleryActions}
modal={false}
open={this.state.galleryOpen}
contentStyle={styles.dialog}
onRequestClose={this.handleCloseGallery}
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
autoDetectWindowHeight={false}
>
<ImageGallery set={this.onRequestSetImage} close={this.handleCloseGallery} />
</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 {
post: (post, callBack) => dispatch(postActions.dbAddImagePost(post, callBack)),
update: (post, callBack) => dispatch(postActions.dbUpdatePost(post, callBack))
}
}
/**
* 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 {
postImageState: state.imageGallery.status,
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
name: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : ''
}
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(PostWrite)