[Update Package] Update material UI package which changed the UI code too.

This commit is contained in:
Qolzam
2018-02-01 13:36:28 +07:00
parent 44200f53b4
commit cff405849f
73 changed files with 1999 additions and 1148 deletions

View File

@@ -32,7 +32,8 @@
"inversify": "^4.6.0",
"keycode": "^2.1.9",
"lodash": "^4.17.4",
"material-ui": "^0.19.4",
"material-ui": "^1.0.0-beta.31",
"material-ui-icons": "^1.0.0-beta.17",
"moment": "^2.18.1",
"morgan": "^1.8.1",
"node-env-file": "^0.1.8",
@@ -62,16 +63,17 @@
"save": "^2.3.0",
"script-loader": "^0.7.0",
"style-loader": "^0.16.1",
"typeface-roboto": "0.0.50",
"url-loader": "^0.5.8",
"uuid": "^3.0.1"
},
"devDependencies": {
"@types/classnames": "^2.2.3",
"@types/lodash": "^4.14.77",
"@types/material-ui": "^0.18.2",
"@types/moment": "^2.13.0",
"@types/node": "^8.0.33",
"@types/prop-types": "^15.5.2",
"@types/react": "^16.0.10",
"@types/react": "^16.0.35",
"@types/react-dom": "^16.0.1",
"@types/react-event-listener": "^0.4.4",
"@types/react-redux": "^5.0.10",

View File

@@ -217,14 +217,11 @@ export const dbUpdateCircle = (newCircle: Circle) => {
let uid: string = getState().authorize.uid
// Write the new data simultaneously in the list
let circle: Circle = getState().circle.userTies[uid][newCircle.id!]
let updatedCircle: Circle = {
name: newCircle.name || circle.name,
isSystem : false
}
let circle: Circle = {...getState().circle.circleList[newCircle.id!]}
circle.name = newCircle.name
return circleService.updateCircle(uid, newCircle.id!, circle)
.then(() => {
dispatch(updateCircle({ id: newCircle.id, ...updatedCircle }))
dispatch(updateCircle({ id: newCircle.id, ...circle }))
}, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message))
})

View File

@@ -14,10 +14,15 @@ import { CommentActionType } from 'constants/commentActionType'
import * as globalActions from 'actions/globalActions'
import * as notifyActions from 'actions/notifyActions'
import * as postActions from 'actions/postActions'
import * as serverActions from 'actions/serverActions'
import { ICommentService } from 'core/services/comments'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
import StringAPI from 'api/StringAPI'
import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestModel } from 'models/server'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
/**
* Get service providers
@@ -82,9 +87,16 @@ export const dbGetComments = (ownerUserId: string, postId: string) => {
const state = getState()
let uid: string = getState().authorize.uid
if (uid) {
// Set server request status to {Sent}
const getCommentsRequest = createGetCommentsRequest(postId)
dispatch(serverActions.sendRequest(getCommentsRequest))
return commentService.getComments(postId, (comments: {[postId: string]: {[commentId: string]: Comment}}) => {
// Set server request status to {OK}
getCommentsRequest.status = ServerRequestStatusType.OK
dispatch(serverActions.sendRequest(getCommentsRequest))
/**
* Workout getting the number of post's comment and getting three last comments
*/
@@ -174,6 +186,19 @@ export const dbDeleteComment = (id?: string | null, postId?: string) => {
}
/**
* Create get comments server request model
*/
const createGetCommentsRequest = (postId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CommentGetComments, postId)
return new ServerRequestModel(
ServerRequestType.CommentGetComments,
requestId,
'',
ServerRequestStatusType.Sent
)
}
/* _____________ CRUD State _____________ */
/**

View File

@@ -3,21 +3,38 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { push } from 'react-router-redux'
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
import { List, ListItem } from 'material-ui/List'
import SvgGroup from 'material-ui/svg-icons/action/group-work'
import { grey } from 'material-ui/colors'
import List, {
ListItem,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
ListSubheader
} from 'material-ui/List'
import SvgGroup from 'material-ui-icons/groupWork'
import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
import IconMenu from 'material-ui/IconMenu'
import MoreVertIcon from 'material-ui-icons/moreVert'
import TextField from 'material-ui/TextField'
import MenuItem from 'material-ui/MenuItem'
import { MenuList, MenuItem } from 'material-ui/Menu'
import { withStyles } from 'material-ui/styles'
import { Manager, Target, Popper } from 'react-popper'
import Grow from 'material-ui/transitions/Grow'
import ClickAwayListener from 'material-ui/utils/ClickAwayListener'
import classNames from 'classnames'
import IconButtonElement from 'layouts/IconButtonElement'
import Dialog from 'material-ui/Dialog'
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle
} from 'material-ui/Dialog'
import Divider from 'material-ui/Divider'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/RaisedButton'
import SvgClose from 'material-ui/svg-icons/navigation/close'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
import SvgClose from 'material-ui-icons/close'
import AppBar from 'material-ui/AppBar'
import Paper from 'material-ui/Paper'
import Collapse from 'material-ui/transitions/Collapse'
// - Import app components
import UserAvatar from 'components/userAvatar'
@@ -32,6 +49,24 @@ import { ICircleComponentState } from './ICircleComponentState'
import { Circle, UserTie } from 'core/domain/circles'
import { Profile } from 'core/domain/users/profile'
const styles = (theme: any) => ({
root: {
width: '100%',
paddingRight: '0px',
backgroundColor: theme.palette.background.paper
},
popperOpen: {
zIndex: 10
},
popperClose: {
pointerEvents: 'none',
zIndex: 0
},
dialogPaper: {
minWidth: 400
}
})
/**
* Create component class
*/
@@ -52,6 +87,14 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
},
settingContent: {
maxWidth: '400px'
},
listMenu: {
color: 'rgba(0,0,0,0.87)',
fontSize: '16px',
marginRight: '8px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden'
}
}
@@ -59,7 +102,7 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
* Component constructor
* @param {object} props is an object properties of component
*/
constructor (props: ICircleComponentProps) {
constructor(props: ICircleComponentProps) {
super(props)
// Defaul state
@@ -75,7 +118,12 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
/**
* Save operation will be disable if user doesn't meet requirement
*/
disabledSave: false
disabledSave: false,
/**
* Whether meu is open
*/
isMenuOpen: false
}
// Binding functions to `this`
@@ -99,6 +147,24 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
})
}
/**
* Handle close menu
*/
handleCloseMenu = () => {
this.setState({
isMenuOpen: false
})
}
/**
* Handle open menu
*/
handleOpenMenu = () => {
this.setState({
isMenuOpen: true
})
}
/**
* Update user's circle
*
@@ -139,49 +205,74 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
let usersParsed: any = []
if (usersOfCircle) {
console.trace('usersOfCircle',usersOfCircle)
Object.keys(usersOfCircle).forEach((userId, index) => {
const { fullName } = usersOfCircle[userId]
let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : ''
usersParsed.push(<ListItem
key={`${this.props.id}.${userId}`}
style={this.styles.userListItem as any}
value={2}
primaryText={fullName}
leftAvatar={<UserAvatar fullName={fullName!} fileName={avatar} />}
onClick={() => this.props.goTo!(`/${userId}`)}
/>)
usersParsed.push(
<ListItem
button
key={`${this.props.id}.${userId}`}
style={this.styles.userListItem as any}
value={2}
onClick={() => this.props.goTo!(`/${userId}`)}
>
<UserAvatar fullName={fullName!} fileName={avatar} />
<ListItemText inset primary={fullName} />
</ListItem>)
})
return usersParsed
}
}
/**
* Right icon menue of circle
*
*
* @memberof CircleComponent
*/
// tslint:disable-next-line:member-ordering
rightIconMenu: any = (
<IconMenu iconButtonElement={IconButtonElement} style={this.styles.rightIconMenu as any}>
<MenuItem primaryText='Delete circle' onClick={this.handleDeleteCircle} />
<MenuItem primaryText='Circle settings' onClick={this.props.openCircleSettings} />
</IconMenu>
)
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render () {
render() {
const { circle, classes } = this.props
const { isMenuOpen } = this.state
/**
* Right icon menue of circle
*
*/
// tslint:disable-next-line:member-ordering
const rightIconMenu = (
<Manager>
<Target>
<IconButton
aria-owns={isMenuOpen! ? 'circle-menu' : null}
aria-haspopup='true'
onClick={this.handleOpenMenu}
>
<MoreVertIcon />
</IconButton>
</Target>
<Popper
placement='bottom-start'
eventsEnabled={isMenuOpen}
className={classNames({ [classes.popperClose]: !isMenuOpen }, { [classes.popperOpen]: isMenuOpen })}
>
<ClickAwayListener onClickAway={this.handleCloseMenu}>
<Grow in={isMenuOpen} id='circle-menu' style={{ transformOrigin: '0 0 0' }}>
<Paper>
<MenuList role='menu'>
<MenuItem onClick={this.handleDeleteCircle} > Delete circle </MenuItem>
<MenuItem onClick={this.props.openCircleSettings}> Circle settings </MenuItem>
</MenuList>
</Paper>
</Grow>
</ClickAwayListener>
</Popper>
</Manager>
)
const {circle} = this.props
const circleTitle = (
<div>
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<div style={{ paddingRight: '10px' }}>
<SvgClose onClick={this.props.closeCircleSettings} hoverColor={grey400} style={{ cursor: 'pointer' }} />
<SvgClose onClick={this.props.closeCircleSettings} style={{ cursor: 'pointer' }} />
</div>
<div style={{
color: 'rgba(0,0,0,0.87)',
@@ -191,45 +282,53 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
Circle settings
</div>
<div style={{ marginTop: '-9px' }}>
<FlatButton label='SAVE' primary={true} disabled={this.state.disabledSave} onClick={this.handleUpdateCircle} />
<Button color='primary' disabled={this.state.disabledSave} onClick={this.handleUpdateCircle} > SAVE </Button>
</div>
</div>
<Divider />
</div>
)
return (
<div>
<div>
<ListItem
key={this.props.id}
style={{ backgroundColor: '#fff', borderBottom: '1px solid rgba(0,0,0,0.12)', height: '72px', padding: '12px 0' }}
primaryText={<span style={{ color: 'rgba(0,0,0,0.87)', fontSize: '16px', marginRight: '8px', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }}>{this.props.circle.name}</span>}
leftIcon={<SvgGroup style={{ width: '40px', height: '40px', transform: 'translate(0px, -9px)', fill: '#bdbdbd' }} />}
rightIconButton={!circle.isSystem ? this.rightIconMenu : null}
initiallyOpen={false}
className={classes.root}
key={this.props.id + '-CircleComponent'}
onClick={this.handleToggleCircle}
open={this.state.open}
nestedItems={this.userList()}
>
<Dialog
key={this.props.id}
title={circleTitle}
modal={false}
open={this.props.openSetting!}
onRequestClose={this.props.closeCircleSettings}
overlayStyle={this.styles.settingOverlay as any}
contentStyle={this.styles.settingContent as any}
>
<div>
<TextField
hintText='Circle name'
floatingLabelText='Circle name'
onChange={this.handleChangeCircleName}
value={this.state.circleName}
/>
</div>
</Dialog>
<ListItemIcon className={classes.icon}>
<SvgGroup />
</ListItemIcon>
<ListItemText inset primary={<span style={this.styles}>{this.props.circle.name}</span>} />
<ListItemSecondaryAction>
{ circle.isSystem ? null : rightIconMenu }
</ListItemSecondaryAction>
</ListItem>
</div>
<Collapse component='li' in={this.state.open} timeout='auto' unmountOnExit>
<List disablePadding>
{this.userList()}
</List>
</Collapse>
<Dialog
key={this.props.id}
open={this.props.openSetting!}
onClose={this.props.closeCircleSettings}
classes={{
paper: classes.dialogPaper
}}
>
<DialogTitle >{circleTitle}</DialogTitle>
<DialogContent>
<TextField
fullWidth
autoFocus
placeholder='Circle name'
label='Circle name'
onChange={this.handleChangeCircleName}
value={this.state.circleName}
/>
</DialogContent>
</Dialog>
</div>
)
}
}
@@ -259,13 +358,13 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICircleComponentProps) => {
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
const {circle, authorize, server} = state
const {userTies} = circle
const { circle, authorize, server } = state
const { userTies } = circle
const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle
let usersOfCircle: {[userId: string]: UserTie} = {}
const circleId = ownProps.circle.id!
let usersOfCircle: { [userId: string]: UserTie } = {}
Object.keys(userTies).forEach((userTieId) => {
const theUserTie = userTies[userTieId] as UserTie
if (theUserTie.circleIdList!.indexOf(ownProps.id) > -1) {
@@ -277,11 +376,11 @@ const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
})
return {
usersOfCircle,
openSetting: state.circle ? (currentCircle ? (currentCircle.openCircleSettings || false) : false) : false,
openSetting: (state.circle && state.circle.openSetting && state.circle.openSetting[circleId]) ? state.circle.openSetting[circleId] : false,
userInfo: state.user.info
}
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(CircleComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CircleComponent as any) as any)

View File

@@ -78,4 +78,9 @@ export interface ICircleComponentProps {
* @memberof ICircleComponentProps
*/
openCircleSettings?: () => any
/**
* Styles
*/
classes?: any
}

View File

@@ -24,4 +24,9 @@ export interface ICircleComponentState {
* @memberof ICircleComponentState
*/
disabledSave: boolean
/**
* Whether circle menu is open
*/
isMenuOpen: boolean
}

View File

@@ -9,19 +9,24 @@ import Linkify from 'react-linkify'
import { Comment } from 'core/domain/comments'
// - Import material UI libraries
import { List, ListItem } from 'material-ui/List'
import Divider from 'material-ui/Divider'
import Paper from 'material-ui/Paper'
import FlatButton from 'material-ui/FlatButton'
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
import Button from 'material-ui/Button'
import { grey } from 'material-ui/colors'
import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
import IconMenu from 'material-ui/IconMenu'
import MenuItem from 'material-ui/MenuItem'
import MoreVertIcon from 'material-ui-icons/moreVert'
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List'
import Menu, { MenuList, MenuItem } from 'material-ui/Menu'
import TextField from 'material-ui/TextField'
import { withStyles } from 'material-ui/styles'
import { Manager, Target, Popper } from 'react-popper'
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import Grow from 'material-ui/transitions/Grow'
import ClickAwayListener from 'material-ui/utils/ClickAwayListener'
import classNames from 'classnames'
// - Import app components
import UserAvatarComponent from 'components/userAvatar'
import UserAvatar from 'components/userAvatar'
// - Import API
@@ -35,10 +40,58 @@ import * as userActions from 'actions/userActions'
import { ICommentComponentProps } from './ICommentComponentProps'
import { ICommentComponentState } from './ICommentComponentState'
const styles = (theme: any) => ({
textField: {
fontWeight: 100,
fontSize: '14px'
},
header: {
padding: '2px 3px 3px 10px'
},
popperOpen: {
zIndex: 11
},
popperClose: {
pointerEvents: 'none',
zIndex: 0
},
iconButton: {
top: 0,
display: 'flex',
right: 4,
flexDirection: 'row-reverse',
position: 'absolute'
},
commentBody: {
color: 'black',
fontWeight: 100,
fontSize: '12px',
height: '100%',
border: 'none',
width: '100%',
outline: 'none',
resize: 'none'
},
rightIconMenuItem: {
fontSize: 12,
fontWeight: 300,
paddingLeft: 6,
paddingRight: 6,
paddingTop: 0,
paddingBottom: 0
},
moreIcon: {
width: '0.6em',
height: '0.6em'
}
})
/**
* Create component class
*/
export class CommentComponent extends Component<ICommentComponentProps,ICommentComponentState> {
export class CommentComponent extends Component<ICommentComponentProps, ICommentComponentState> {
static propTypes = {
/**
@@ -62,16 +115,8 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @memberof CommentComponent
*/
styles = {
comment: {
marginBottom: '12px'
},
iconButton: {
width: 16,
height: 16
},
author: {
fontSize: '13px',
fontSize: '10px',
paddingRight: '10px',
fontWeight: 400,
color: 'rgba(0,0,0,0.87)',
@@ -79,18 +124,6 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
overflow: 'hidden'
},
commentBody: {
fontSize: '13px',
lineHeight: '20px',
color: 'rgba(0,0,0,0.87)',
fontWeight: 300,
height: '',
display: 'block'
},
rightIconMenuItem: {
fontSize: '14px'
},
textarea: {
fontWeight: 100,
fontSize: '14px',
@@ -146,7 +179,11 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
/**
* If it's true the post owner is the logged in user which this post be long to the comment
*/
isPostOwner: false
isPostOwner: false,
/**
* The anchor of comment menu element
*/
openMenu: false
}
@@ -164,8 +201,8 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @param {event} evt is an event passed by clicking on edit button
*/
handleEditComment = (evt: any) => {
this.inputText.style.height = this.divComment.clientHeight + 'px'
this.props.openEditor()
this.setState({openMenu: false})
this.props.openEditor!()
}
/**
@@ -177,7 +214,7 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
this.setState({
text: this.state.initialText
})
this.props.closeEditor()
this.props.closeEditor!()
}
/**
@@ -185,10 +222,10 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @param {event} evt is an event passed by clicking on post button
*/
handleUpdateComment = (evt: any) => {
const {comment} = this.props
const { comment } = this.props
comment.editorStatus = undefined
comment.text = this.state.text
this.props.update(comment)
this.props.update!(comment)
this.setState({
initialText: this.state.text
})
@@ -202,7 +239,6 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
*/
handleOnChange = (evt: any) => {
const data = evt.target.value
this.inputText.style.height = evt.target.scrollHeight + 'px'
if (data.length === 0 || data.trim() === '' || data.trim() === this.state.initialText) {
this.setState({
text: data,
@@ -223,14 +259,28 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @param {string} id comment identifire
* @param {string} postId post identifier which comment belong to
*/
handleDelete = (evt: any, id?: string| null, postId?: string) => {
this.props.delete(id, postId)
handleDelete = (evt: any, id?: string | null, postId?: string) => {
this.props.delete!(id, postId)
}
/**
* Handle comment menu
*/
handleCommentMenu = (event: any) => {
this.setState({ openMenu: true })
}
/**
* Handle close request for comment menu
*/
handleCloseCommentMenu = () => {
this.setState({ openMenu: false })
}
componentWillMount () {
const {userId} = this.props.comment
if (!this.props.isCommentOwner && !this.props.info[userId!]) {
this.props.getUserInfo()
const { userId } = this.props.comment
if (!this.props.isCommentOwner && !this.props.info![userId!]) {
this.props.getUserInfo!()
}
}
@@ -243,56 +293,94 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
/**
* Comment object from props
*/
const {comment} = this.props
const { comment, classes, fullName, avatar } = this.props
const iconButtonElement = (
<IconButton style={this.styles.iconButton} iconStyle={this.styles.iconButton}
touch={true}
>
<MoreVertIcon color={grey400} viewBox='9 0 24 24' />
</IconButton>
)
const { openMenu } = this.state
const RightIconMenu = () => (
<IconMenu iconButtonElement={iconButtonElement} style={{ display: 'block', position: 'absolute', top: '0px', right: '4px' }}>
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem>
{this.props.isCommentOwner ? (<MenuItem style={this.styles.rightIconMenuItem} onClick={this.handleEditComment}>Edit</MenuItem>) : ''}
{(this.props.isCommentOwner || this.props.isPostOwner) ? ( <MenuItem style={{ fontSize: '14px' }} onClick={(evt) => this.handleDelete(evt, comment.id, comment.postId)}>Delete</MenuItem>) : ''}
</IconMenu>
<Manager
className={classes.iconButton}
>
<Target>
<IconButton
aria-owns={openMenu! ? 'comment-menu' : null}
aria-haspopup='true'
onClick={this.handleCommentMenu}
>
<MoreVertIcon className={classes.moreIcon} />
</IconButton>
</Target>
<Popper
placement='bottom-start'
eventsEnabled={openMenu!}
className={classNames({ [classes.popperClose]: !openMenu! }, { [classes.popperOpen]: openMenu! })}
>
<ClickAwayListener onClickAway={this.handleCloseCommentMenu}>
<Grow in={openMenu!} id='comment-menu' style={{ transformOrigin: '0 0 0' }}>
<Paper>
<MenuList role='menu'>
<MenuItem className={classes.rightIconMenuItem}>Reply</MenuItem>
{this.props.isCommentOwner ? (<MenuItem className={classes.rightIconMenuItem} onClick={this.handleEditComment}>Edit</MenuItem>) : ''}
{(this.props.isCommentOwner || this.props.isPostOwner) ? (<MenuItem className={classes.rightIconMenuItem} onClick={(evt: any) => this.handleDelete(evt, comment.id, comment.postId)}>Delete</MenuItem>) : ''}
</MenuList>
</Paper>
</Grow>
</ClickAwayListener>
</Popper>
</Manager>
)
const Author = () => (
<div style={{ marginTop: '-11px' }}>
<span style={this.styles.author as any}>{comment.userDisplayName}</span><span style={{
<div>
<NavLink to={`/${userId}`}> <span style={this.styles.author as any}>{comment.userDisplayName}</span></NavLink><span style={{
fontWeight: 100,
fontSize: '10px'
fontSize: '8px'
}}>{moment.unix(comment.creationDate!).fromNow()}</span>
</div>
)
const { userId, editorStatus } = comment
const commentBody = (
<p style={this.styles.commentBody as any}>{comment.text}</p>
<div style={{ outline: 'none', flex: 'auto', flexGrow: 1 }}>
{ editorStatus ? <TextField
placeholder={'Add a comment...'}
multiline
autoFocus
rowsMax='4'
InputProps={{
disableUnderline: true,
autoFocus: true,
fullWidth: true
}}
value={this.state.text}
onChange={this.handleOnChange}
className={classes.textField}
fullWidth={true}
/> : <div className={classNames('animate2-top10', classes.commentBody)}>{this.state.text}</div>}
<div style={{ display: (editorStatus ? 'flex' : 'none'), flexDirection: 'row-reverse' }}>
<Button color='primary' disabled={this.state.editDisabled}
style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }}
onClick={this.handleUpdateComment} > Update </Button>
<Button color='primary' style={this.styles.cancel as any} onClick={this.handleCancelEdit} > Cancel </Button>
</div>
</div>
)
const {userId} = comment
return (
<div className='animate-top' style={this.styles.comment} key={comment.id!}>
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', padding: '', display: (!this.state.display ? 'block' : 'none') }}>
<div style={{ marginLeft: '0px', padding: '16px 56px 0px 72px', position: 'relative' }}>
<NavLink to={`/${userId}`}><UserAvatarComponent fullName={this.props.fullName} fileName={this.props.avatar} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', position: 'absolute', top: '8px', left: '16px' }} size={36} /></NavLink>
<NavLink to={`/${userId}`}> <Author /></NavLink>
{(!this.props.isCommentOwner && !this.props.isPostOwner && this.props.disableComments ) ? '' : (<RightIconMenu />)}
<div style={{ outline: 'none', marginLeft: '16px', flex: 'auto', flexGrow: 1 }}>
<textarea ref={this.textareaRef} className='animate2-top10' style={ Object.assign({}, this.styles.textarea, { display: (this.props.comment.editorStatus ? 'block' : 'none') })} onChange={this.handleOnChange} value={this.state.text!}></textarea>
<Linkify properties={{target: '_blank', style: {color: 'blue'}}}>
<div ref={this.divCommentRef} className='animate2-top10' style={{ fontWeight: 100, fontSize: '14px', height: '100%', border: 'none', width: '100%', outline: 'none', resize: 'none', display: (!this.props.comment.editorStatus ? 'block' : 'none') }}>{this.state.text}</div>
</Linkify>
</div>
</div>
<div style={{ display: (this.props.comment.editorStatus ? 'flex' : 'none'), flexDirection: 'row-reverse' }}>
<FlatButton primary={true} disabled={this.state.editDisabled} label='Update' style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }} onClick={this.handleUpdateComment} />
<FlatButton primary={true} label='Cancel' style={this.styles.cancel as any} onClick={this.handleCancelEdit} />
</div>
<div className='animate-top' key={comment.id!}>
<Paper elevation={0} className='animate2-top10'
style={{ position: 'relative', padding: '', display: (!this.state.display ? 'block' : 'none') }}>
<Card elevation={0}>
<CardHeader
className={classes.header}
title={editorStatus ? '' : <Author />}
subheader={commentBody}
avatar={<NavLink to={`/${userId}`}><UserAvatar fullName={fullName!} fileName={avatar!} size={24} /></NavLink>}
action={(!this.props.isCommentOwner && !this.props.isPostOwner && this.props.disableComments) || editorStatus ? '' : (<RightIconMenu />)}
>
</CardHeader>
</Card>
</Paper>
</div>
@@ -308,16 +396,13 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
*/
const mapDispatchToProps = (dispatch: any, ownProps: ICommentComponentProps) => {
return {
delete: (id: string| null, postId: string) => dispatch(commentActions.dbDeleteComment(id, postId)),
delete: (id: string | null, postId: string) => dispatch(commentActions.dbDeleteComment(id, postId)),
update: (comment: Comment) => {
console.log('====================================')
console.log(comment)
console.log('====================================')
dispatch(commentActions.dbUpdateComment(comment))
},
openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
closeEditor: () => dispatch(commentActions.closeCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
getUserInfo: () => dispatch(userActions.dbGetUserInfoByUserId(ownProps.comment.userId!,''))
getUserInfo: () => dispatch(userActions.dbGetUserInfoByUserId(ownProps.comment.userId!, ''))
}
}
@@ -328,7 +413,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentComponentProps) =>
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: any) => {
const {uid} = state.authorize
const { uid } = state.authorize
const avatar = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].avatar || '' : ''
const fullName = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].fullName || '' : ''
return {
@@ -341,4 +426,4 @@ const mapStateToProps = (state: any, ownProps: any) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(CommentComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CommentComponent as any) as any)

View File

@@ -16,7 +16,7 @@ export interface ICommentComponentProps {
* @type {Function}
* @memberof ICommentComponentProps
*/
openEditor: Function
openEditor?: Function
/**
* Close comment editor
@@ -24,7 +24,7 @@ export interface ICommentComponentProps {
* @type {Function}
* @memberof ICommentComponentProps
*/
closeEditor: () => any
closeEditor?: () => any
/**
* Current user is comment owner {true} or not {false}
@@ -32,7 +32,7 @@ export interface ICommentComponentProps {
* @type {boolean}
* @memberof ICommentComponentProps
*/
isCommentOwner: boolean
isCommentOwner?: boolean
/**
* Current user is post owner {true} or not {false}
@@ -47,21 +47,21 @@ export interface ICommentComponentProps {
*
* @memberof ICommentComponentProps
*/
update: (comment: Comment) => any
update?: (comment: Comment) => any
/**
* Delete comment
*
* @memberof ICommentComponentProps
*/
delete: (id?: string| null, postId?: string) => any
delete?: (id?: string| null, postId?: string) => any
/**
* Get user profile
*
* @memberof ICommentComponentProps
*/
getUserInfo: () => void
getUserInfo?: () => void
/**
* User profile
@@ -69,7 +69,7 @@ export interface ICommentComponentProps {
* @type {{[userId: string]: Profile}}
* @memberof ICommentComponentProps
*/
info: {[userId: string]: Profile}
info?: {[userId: string]: Profile}
/**
* User full name
@@ -77,7 +77,7 @@ export interface ICommentComponentProps {
* @type {string}
* @memberof ICommentComponentProps
*/
fullName: string
fullName?: string
/**
* User avatar address
@@ -85,7 +85,7 @@ export interface ICommentComponentProps {
* @type {string}
* @memberof Comment
*/
avatar: string
avatar?: string
/**
* Writing comment on the post is disabled {true} or not false
@@ -93,6 +93,11 @@ export interface ICommentComponentProps {
* @type {boolean}
* @memberof ICommentComponentProps
*/
disableComments: boolean
disableComments?: boolean
/**
* Styles
*/
classes: any
}

View File

@@ -15,7 +15,7 @@ export interface ICommentComponentState {
* @type {string}
* @memberof ICommentComponentProps
*/
text?: string | null
text: string
/**
* Comment is in edit state {true} or not {false}
@@ -40,4 +40,9 @@ export interface ICommentComponentState {
* @memberof ICommentComponentState
*/
display?: boolean
/**
* Wheter comment menu is open
*/
openMenu?: boolean
}

View File

@@ -2,25 +2,73 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import { NavLink } from 'react-router-dom'
import { connect } from 'react-redux'
import moment from 'moment'
import Paper from 'material-ui/Paper'
import FlatButton from 'material-ui/FlatButton'
import Button from 'material-ui/Button'
import TextField from 'material-ui/TextField'
import Divider from 'material-ui/Divider'
import { ListItem } from 'material-ui/List'
import { grey400, darkBlack, lightBlack, tealA400 } from 'material-ui/styles/colors'
import LinearProgress from 'material-ui/LinearProgress'
import { ListItemIcon, ListItemText, ListItem } from 'material-ui/List'
import { grey, teal } from 'material-ui/colors'
import { LinearProgress } from 'material-ui/Progress'
import { withStyles } from 'material-ui/styles'
import { Manager, Target, Popper } from 'react-popper'
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import Grow from 'material-ui/transitions/Grow'
import ClickAwayListener from 'material-ui/utils/ClickAwayListener'
import classNames from 'classnames'
// - Import actions
import * as commentActions from 'actions/commentActions'
// - Import app components
import CommentListComponent from 'components/CommentList'
import UserAvatarComponent from 'components/userAvatar'
import UserAvatar from 'components/userAvatar'
import { ICommentGroupComponentProps } from './ICommentGroupComponentProps'
import { ICommentGroupComponentState } from './ICommentGroupComponentState'
import { Comment } from 'core/domain/comments/comment'
import { ServerRequestModel } from 'models/server';
import StringAPI from 'api/StringAPI';
import { ServerRequestType } from 'constants/serverRequestType';
import { ServerRequestStatusType } from 'actions/serverRequestStatusType';
const styles = (theme: any) => ({
textField: {
fontWeight: 100,
fontSize: '14px'
},
header: {
padding: '2px 3px 3px 10px'
},
commentBody: {
color: 'black',
fontWeight: 100,
fontSize: '12px',
height: '100%',
border: 'none',
width: '100%',
outline: 'none',
resize: 'none'
},
author: {
fontSize: '10px',
paddingRight: '10px',
fontWeight: 400,
color: 'rgba(0,0,0,0.87)',
textOverflow: 'ellipsis',
overflow: 'hidden'
},
noUnderline: {
display: 'none'
},
postButton: {
flexDirection: 'row-reverse'
}
})
/**
* Create component class
@@ -28,29 +76,29 @@ import { Comment } from 'core/domain/comments/comment'
export class CommentGroupComponent extends Component<ICommentGroupComponentProps, ICommentGroupComponentState> {
static propTypes = {
/**
* If it's true comment box will be open
*/
/**
* If it's true comment box will be open
*/
open: PropTypes.bool,
/**
* If it's true the comment is disable to write
*/
/**
* If it's true the comment is disable to write
*/
disableComments: PropTypes.bool,
/**
* The post identifier which comment belong to
*/
/**
* The post identifier which comment belong to
*/
postId: PropTypes.string,
/**
* If it's true the post owner is the logged in user which this post be long to the comment
*/
/**
* If it's true the post owner is the logged in user which this post be long to the comment
*/
isPostOwner: PropTypes.bool,
/**
* Toggle on show/hide comment by passing callback from parent component
*/
/**
* Toggle on show/hide comment by passing callback from parent component
*/
onToggleRequest: PropTypes.func,
/**
* The user identifier of the post owner which comment belong to
*/
/**
* The user identifier of the post owner which comment belong to
*/
ownerPostUserId: PropTypes.string
}
@@ -66,11 +114,29 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
zIndex: 5
},
writeCommentTextField: {
width: '100%'
width: '100%',
fontWeight: 100,
fontSize: '14px'
},
progressbar: {
height: '1.5px',
backgroundColor: 'rgb(245, 243, 243)'
backgroundColor: 'rgb(245, 243, 243)',
color: teal['A400']
},
secondaryText: {
fontSize: '13px',
lineHeight: '20px',
color: 'rgba(0,0,0,0.87)',
fontWeight: 300,
whiteSpace: 'pre-wrap'
},
primaryText: {
fontSize: '13px',
paddingRight: '10px',
fontWeight: 400,
color: 'rgba(0,0,0,0.87)',
textOverflow: 'ellipsis',
overflow: 'hidden'
}
}
@@ -121,7 +187,8 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
* @param {event} evt is an event passed by change comment text callback funciton
* @param {string} data is the comment text which user writes
*/
handleOnChange = (evt: any, data: any) => {
handleChange = (event: any) => {
const data = event.target.value
this.setState({ commentText: data })
if (data.length === 0 || data.trim() === '') {
this.setState({
@@ -142,10 +209,11 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
* @return {DOM} list of comments' DOM
*/
commentList = () => {
const {classes} = this.props
let comments = this.props.commentSlides
if (comments) {
comments = _.fromPairs(_.toPairs(comments)
.sort((a: any, b: any) => parseInt(b[1].creationDate,10) - parseInt(a[1].creationDate,10)))
.sort((a: any, b: any) => parseInt(b[1].creationDate, 10) - parseInt(a[1].creationDate, 10)))
let parsedComments: Comment[] = []
Object.keys(comments).forEach((commentId) => {
parsedComments.push({
@@ -160,34 +228,41 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
parsedComments.push(parsedComments[0])
}
return parsedComments.map((comment, index) => {
const {userInfo} = this.props
const { userInfo } = this.props
const commentAvatar = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].avatar || '' : ''
const commentFullName = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].fullName || '' : ''
return (<ListItem key={index} style={this.styles.commentItem as any} innerDivStyle={{ padding: '6px 16px 16px 72px' }}
leftAvatar={<UserAvatarComponent fullName={commentFullName} fileName={commentAvatar} style={{ top: '8px' }} size={36} />}
secondaryText={<div style={{ height: '' }}>
<span style={{
fontSize: '13px',
paddingRight: '10px',
fontWeight: 400,
color: 'rgba(0,0,0,0.87)',
textOverflow: 'ellipsis',
overflow: 'hidden'
}}>
{comment.userDisplayName}:
</span>
<span style={{
fontSize: '13px',
lineHeight: '20px',
color: 'rgba(0,0,0,0.87)',
fontWeight: 300,
whiteSpace: 'pre-wrap'
}}>{comment.text}</span>
</div>}
secondaryTextLines={2}
/>
const commentBody = (
<div style={{ outline: 'none', flex: 'auto', flexGrow: 1 }}>
<div className={classNames('animate2-top10', classes.commentBody)} >
{comment.text}
</div>
</div>
)
const Author = () => (
<div>
<NavLink to={`/${comment.userId!}`}> <span className={classes.author}>{comment.userDisplayName}</span></NavLink><span style={{
fontWeight: 100,
fontSize: '8px'
}}>{moment.unix(comment.creationDate!).fromNow()}</span>
</div>
)
return (
<Paper key={comment.id! + '-index:' + index} elevation={0} className='animate2-top10'>
<Card elevation={0}>
<CardHeader
className={classes.header}
title={ <Author />}
avatar={<UserAvatar fullName={commentFullName!} fileName={commentAvatar!} size={24} />}
subheader={commentBody}
>
</CardHeader>
</Card>
</Paper>
)
})
}
@@ -198,47 +273,62 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
* @return {react element} return the DOM which rendered by component
*/
render () {
const {comments} = this.props
const { comments, classes, postId, fullName, avatar, getCommentsRequest, open, commentSlides } = this.props
/**
* Comment list box
*/
const commentWriteBox = (<div>
<Divider />
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', overflowY: 'auto', padding: '12px 16px', display: (this.props.open ? 'block' : 'none') }}>
<div style={{ display: 'flex' }}>
<UserAvatarComponent fullName={this.props.fullName!} fileName={this.props.avatar!} style={{ flex: 'none', margin: '4px 0px' }} size={36} />
<div style={{ outline: 'none', marginLeft: '16px', flex: 'auto', flexGrow: 1 }}>
<TextField
value={this.state.commentText}
onChange={this.handleOnChange}
hintText='Add a comment...'
underlineShow={false}
multiLine={true}
rows={1}
hintStyle={{ fontWeight: 100, fontSize: '14px' }}
rowsMax={4}
textareaStyle={{ fontWeight: 100, fontSize: '14px' }}
style={this.styles.writeCommentTextField}
/>
</div>
<Paper key={postId! + '-commentwrite'} elevation={0} className='animate2-top10'>
<Card elevation={0}>
<CardHeader
className={classes.header}
avatar={<UserAvatar fullName={fullName!} fileName={avatar!} size={24} />}
subheader={<TextField
autoFocus
placeholder={'Add a comment...'}
multiline
rowsMax='4'
InputProps={{
disableUnderline: true,
autoFocus: true,
fullWidth: true
}}
value={this.state.commentText}
onChange={this.handleChange}
className={classes.textField}
fullWidth={true}
/>}
>
</CardHeader>
<CardActions className={classes.postButton} >
<Button color='primary' disabled={this.state.postDisable} onClick={this.handlePostComment}>
Post
</Button>
</CardActions>
</Card>
</Paper>
</div>
<FlatButton primary={true} disabled={this.state.postDisable} label='Post' style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }} onClick={this.handlePostComment} />
</Paper>
</div>)
)
const showComments = ( comments && Object.keys(comments).length > 0
? (<Paper elevation={0} style={open ? { display: 'block', padding: '0px 0px' } : { display: 'none', padding: '12px 16px' }}>
<CommentListComponent comments={comments!} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments} />
</Paper>)
: '')
const loadComments = (( getCommentsRequest === undefined || (getCommentsRequest && getCommentsRequest!.status !== ServerRequestStatusType.OK)) ? <LinearProgress style={this.styles.progressbar} mode='indeterminate' /> : showComments)
/**
* Return Elements
*/
return (
<div>
<div style={this.props.commentSlides && Object.keys(this.props.commentSlides).length > 0 ? { display: 'block' } : { display: 'none' }}>
<div key={postId + '-comments'}>
<div style={commentSlides && Object.keys(commentSlides).length > 0 ? { display: 'block' } : { display: 'none' }}>
<Divider />
<Paper zDepth={0} className='animate-top' style={!this.props.open ? { display: 'block' } : { display: 'none' }}>
<Paper elevation={0} className='animate-top' style={!open ? { display: 'block' } : { display: 'none' }}>
<div style={{ position: 'relative', height: '60px' }} >
<FlatButton label=' ' style={this.styles.toggleShowList} fullWidth={true} onClick={this.props.onToggleRequest} />
<Button style={this.styles.toggleShowList} fullWidth={true} onClick={this.props.onToggleRequest} > {' '}</Button>
<div className='comment__list-show'>
{this.commentList()}
@@ -247,20 +337,14 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
</div>
</Paper>
{
!comments
? this.props.open ? <LinearProgress style={this.styles.progressbar} mode='indeterminate' color={tealA400} /> : ''
: (Object.keys(comments).length > 0
? (<Paper zDepth={0} style={this.props.open ? { display: 'block', padding: '0px 0px' } : { display: 'none', padding: '12px 16px' }}>
<CommentListComponent comments={comments} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
</Paper>)
: '')
open ? loadComments : ''
}
</div>
{
!this.props.disableComments
? commentWriteBox
: ''
(!this.props.disableComments && open )
? commentWriteBox
: ''
}
</div>
)
@@ -276,7 +360,7 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps) => {
return {
send: (text: string, postId: string, callBack: Function) => {
dispatch(commentActions.dbAddComment(ownProps.ownerPostUserId,{
dispatch(commentActions.dbAddComment(ownProps.ownerPostUserId, {
postId: postId,
text: text
}, callBack))
@@ -291,11 +375,13 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => {
const {post, user, authorize} = state
const {ownerPostUserId, postId} = ownProps
const { post, user, authorize, server } = state
const {request} = server
const { ownerPostUserId, postId } = ownProps
const commentSlides = post.userPosts[ownerPostUserId] && post.userPosts[ownerPostUserId][postId] ? post.userPosts[ownerPostUserId][postId].comments : {}
const getCommentsRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CommentGetComments, postId)] : null
return {
getCommentsRequest,
commentSlides,
avatar: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].avatar || '' : '',
fullName: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].fullName || '' : '',
@@ -305,4 +391,4 @@ const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(CommentGroupComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CommentGroupComponent as any) as any)

View File

@@ -1,5 +1,6 @@
import { Profile } from 'core/domain/users'
import { Comment } from 'core/domain/comments'
import { ServerRequestModel } from 'models/server'
export interface ICommentGroupComponentProps {
/**
@@ -98,4 +99,14 @@ export interface ICommentGroupComponentProps {
*/
send?: (commentText: string, postId: string, clearCommentWrite: () => void) => any
/**
* Get post comments request payload
*/
getCommentsRequest?: ServerRequestModel
/**
* Styles
*/
classes?: any
}

View File

@@ -2,7 +2,7 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { List, ListItem } from 'material-ui/List'
import List, { ListItem, ListItemText } from 'material-ui/List'
// - Import app components
import CommentComponent from 'components/Comment'
@@ -68,7 +68,7 @@ export class CommentListComponent extends Component<ICommentListComponentProps,
return sortedComments.map((comment: Comment, index: number, array: Comment) => {
return <CommentComponent key={comment.id} comment={comment} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
return <CommentComponent key={comment.id!} comment={comment} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
})
@@ -84,8 +84,7 @@ export class CommentListComponent extends Component<ICommentListComponentProps,
const styles: any = {
list: {
width: '100%',
maxHeight: 450,
overflowY: 'auto'
maxHeight: 450
}
}

View File

@@ -2,25 +2,33 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
import { grey } from 'material-ui/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 MoreVertIcon from 'material-ui-icons/moreVert'
import SvgCamera from 'material-ui-icons/photoCamera'
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List'
import Menu, { MenuList, MenuItem } from 'material-ui/Menu'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
import EventListener, { withOptions } from 'react-event-listener'
import Dialog from 'material-ui/Dialog'
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle
} from 'material-ui/Dialog'
import Divider from 'material-ui/Divider'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import Input, { InputLabel } from 'material-ui/Input'
import { FormControl, FormHelperText } from 'material-ui/Form'
import { withStyles } from 'material-ui/styles'
// - Import app components
import ImgCover from 'components/imgCover'
import UserAvatarComponent from 'components/userAvatar'
import ImageGallery from 'components/imageGallery'
import DialogTitle from 'layouts/DialogTitle'
import AppDialogTitle from 'layouts/dialogTitle'
// - Import API
import FileAPI from 'api/FileAPI'
@@ -34,6 +42,12 @@ import { IEditProfileComponentProps } from './IEditProfileComponentProps'
import { IEditProfileComponentState } from './IEditProfileComponentState'
import { Profile } from 'core/domain/users'
const styles = (theme: any) => ({
dialogTitle: {
padding: 0
}
})
/**
* Create component class
*/
@@ -277,20 +291,20 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
*/
render () {
const {classes} = this.props
const iconButtonElement = (
<IconButton style={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton} iconStyle={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton}
touch={true}
>
<MoreVertIcon color={grey400} viewBox='10 0 24 24' />
<IconButton style={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton}>
<MoreVertIcon style={{...(this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton),color: grey[400]}} viewBox='10 0 24 24' />
</IconButton>
)
const RightIconMenu = () => (
<IconMenu iconButtonElement={iconButtonElement}>
<div>
{iconButtonElement}
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem>
<MenuItem style={{ fontSize: '14px' }}>Edit</MenuItem>
<MenuItem style={{ fontSize: '14px' }}>Delete</MenuItem>
</IconMenu>
</div>
)
return (
@@ -299,15 +313,10 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
{/* Edit profile dialog */}
<Dialog
key='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%' }}
onClose={this.props.onRequestClose}
>
<DialogContent>
{/* Banner */}
<div style={{ position: 'relative' }}>
<ImgCover width='100%' height='250px' borderRadius='2px' fileName={this.state.banner} />
@@ -323,7 +332,7 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
<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' }}>
<div className='g__circle-black' onClick={this.handleOpenAvatarGallery} style={{zIndex: 1, 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>
@@ -340,61 +349,59 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
</div>
{/* Edit user information box*/}
<Paper style={this.styles.paper} zDepth={1}>
<Paper style={this.styles.paper} elevation={1}>
<div style={this.styles.title as any}>Personal Information</div>
<div style={this.styles.box}>
<TextField
floatingLabelText='Full name'
onChange={this.handleInputChange}
name='fullNameInput'
errorText={this.state.fullNameInputError}
value={this.state.fullNameInput}
/>
<FormControl aria-describedby='fullNameInputError'>
<InputLabel htmlFor='fullNameInput'>Full name</InputLabel>
<Input id='fullNameInput'
onChange={this.handleInputChange}
name='fullNameInput'
value={this.state.fullNameInput} />
<FormHelperText id='fullNameInputError'>{this.state.fullNameInputError}</FormHelperText>
</FormControl>
</div>
<br />
<div style={this.styles.box}>
<TextField
floatingLabelText='Tag Line'
onChange={this.handleInputChange}
name='tagLineInput'
value={this.state.tagLineInput}
/>
<FormControl aria-describedby='tagLineInputError'>
<InputLabel htmlFor='tagLineInput'>Tagline</InputLabel>
<Input id='tagLineInput'
onChange={this.handleInputChange}
name='tagLineInput'
value={this.state.tagLineInput} />
<FormHelperText id='tagLineInputError'>{this.state.fullNameInputError}</FormHelperText>
</FormControl>
</div>
<br />
<div style={this.styles.actions as any}>
<FlatButton label='CANCEL' onClick={this.props.onRequestClose} />
<RaisedButton label='UPDATE' primary={true} onClick={this.handleUpdate} style={this.styles.updateButton} />
<Button onClick={this.props.onRequestClose} > CANCEL </Button>
<Button raised color='primary' onClick={this.handleUpdate} style={this.styles.updateButton}> UPDATE </Button>
</div>
</Paper>
<div style={{ height: '16px' }}></div>
</DialogContent>
</Dialog>
{/* Image gallery for banner*/}
<Dialog
title={<DialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />}
modal={false}
open={this.state.openBanner}
contentStyle={this.styles.dialogGallery}
onRequestClose={this.handleCloseBannerGallery}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
autoDetectWindowHeight={false}
onClose={this.handleCloseBannerGallery}
>
<DialogTitle className={classes.dialogTitle}>
<AppDialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />
</DialogTitle>
<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={this.styles.dialogGallery}
onRequestClose={this.handleCloseAvatarGallery}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
autoDetectWindowHeight={false}
onClose={this.handleCloseAvatarGallery}
>
<DialogTitle className={classes.dialogTitle}>
<AppDialogTitle title='Choose an avatar image' onRequestClose={this.handleCloseAvatarGallery} />
</DialogTitle>
<ImageGallery set={this.handleRequestSetAvatar} close={this.handleCloseAvatarGallery} />
</Dialog>
@@ -433,4 +440,4 @@ const mapStateToProps = (state: any, ownProps: IEditProfileComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(EditProfileComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(EditProfileComponent as any) as any)

View File

@@ -55,4 +55,9 @@ export interface IEditProfileComponentProps {
* @memberof IEditProfileComponentProps
*/
onRequestClose?: () => void
/**
* Styles
*/
classes: any
}

View File

@@ -5,9 +5,8 @@ import { NavLink, withRouter } from 'react-router-dom'
import { push } from 'react-router-redux'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import FlatButton from 'material-ui/FlatButton'
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
import RaisedButton from 'material-ui/Button'
import Button from 'material-ui/Button'
// - Import actions
import * as authorizeActions from 'actions/authorizeActions'
@@ -74,7 +73,7 @@ export class EmailVerificationComponent extends Component<IEmailVerificationComp
}}>Green</h1>
<div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} >
<Paper style={paperStyle} elevation={1} rounded={false} >
<div style={{ padding: '48px 40px 36px' }}>
<div style={{
paddingLeft: '40px',
@@ -94,8 +93,8 @@ export class EmailVerificationComponent extends Component<IEmailVerificationComp
An verificiation email has been already sent to you. Please check your inbox. If you couldn't see the emai, please resend email verification.
</p>
<div style={this.styles.buttons}>
<RaisedButton style={this.styles.homeButton} label='Home' primary={true} onClick={() => this.props.homePage()} />
<RaisedButton label='Send Email Verification' primary={true} onClick={() => this.props.sendEmailVerification()} />
<Button raised style={this.styles.homeButton} label='Home' color='primary' onClick={() => this.props.homePage()} />
<Button raised label='Send Email Verification' color='primary' onClick={() => this.props.sendEmailVerification()} />
</div>
<div>
</div>

View File

@@ -6,14 +6,15 @@ import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
import { connect } from 'react-redux'
import { push } from 'react-router-redux'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import { MenuList, MenuItem } from 'material-ui/Menu'
import { ListItemIcon, ListItemText } from 'material-ui/List'
import Divider from 'material-ui/Divider'
import SvgArrowLeft from 'material-ui/svg-icons/hardware/keyboard-arrow-left'
import SvgHome from 'material-ui/svg-icons/action/home'
import SvgFeedback from 'material-ui/svg-icons/action/feedback'
import SvgSettings from 'material-ui/svg-icons/action/settings'
import SvgAccountCircle from 'material-ui/svg-icons/action/account-circle'
import SvgPeople from 'material-ui/svg-icons/social/people'
import SvgArrowLeft from 'material-ui-icons/keyboardArrowLeft'
import SvgHome from 'material-ui-icons/home'
import SvgFeedback from 'material-ui-icons/feedback'
import SvgSettings from 'material-ui-icons/settings'
import SvgAccountCircle from 'material-ui-icons/accountCircle'
import SvgPeople from 'material-ui-icons/people'
// - Import app components
import Sidebar from 'components/sidebar'
@@ -105,7 +106,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
}
componentWillMount () {
const {global, clearData, loadData, authed, defaultDataEnable, isVerifide, goTo } = this.props
const { global, clearData, loadData, authed, defaultDataEnable, isVerifide, goTo } = this.props
if (!authed) {
goTo!('/login')
return
@@ -129,29 +130,71 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
* @memberof Home
*/
render () {
const {loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback} = this.props
const { loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback } = this.props
return (
<div id='home'>
<HomeHeader sidebar={this.state.sidebarOpen} sidebarStatus={this.state.sidebarStatus} />
<Sidebar overlay={this.sidebarOverlay} open={this.sidebar} status={this.sidebarStatus}>
<SidebarContent>
<Menu style={{ color: 'rgb(117, 117, 117)', width: '210px' }}>
<MenuList style={{ color: 'rgb(117, 117, 117)', width: '210px' }}>
{this.state.sidebarOverlay
? <div><MenuItem onClick={this.handleCloseSidebar} primaryText={<span style={{ color: 'rgb(117, 117, 117)' }} className='sidebar__title'>Green</span>} rightIcon={<SvgArrowLeft viewBox='0 3 24 24' style={{ color: '#fff', marginLeft: '15px', width: '32px', height: '32px', cursor: 'pointer' }} />} /><Divider /></div>
? <div>
<MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
<ListItemIcon>
<SvgArrowLeft viewBox='0 3 24 24' style={{ color: '#fff', marginLeft: '15px', width: '32px', height: '32px', cursor: 'pointer' }} />
</ListItemIcon>
<ListItemText inset
primary={<span style={{ color: 'rgb(117, 117, 117)' }}
className='sidebar__title'>Green</span>} />
</MenuItem>
<Divider /></div>
: ''
}
<NavLink to='/'><MenuItem primaryText='Home' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgHome />} /></NavLink>
<NavLink to={`/${this.props.uid}`}><MenuItem primaryText='Profile' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgAccountCircle />} /></NavLink>
<NavLink to='/people'><MenuItem primaryText='People' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgPeople />} /></NavLink>
<NavLink to='/'>
<MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
<ListItemIcon>
<SvgHome />
</ListItemIcon>
<ListItemText inset primary='Home' />
</MenuItem>
</NavLink>
<NavLink to={`/${this.props.uid}`}>
<MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
<ListItemIcon>
<SvgAccountCircle />
</ListItemIcon>
<ListItemText inset primary='Profile' />
</MenuItem>
</NavLink>
<NavLink to='/people'>
<MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
<ListItemIcon>
<SvgPeople />
</ListItemIcon>
<ListItemText inset primary='People' />
</MenuItem>
</NavLink>
<Divider />
<NavLink to='/settings'><MenuItem primaryText='Settings' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgSettings />} /></NavLink>
<MenuItem primaryText='Send feedback' onClick={() => showSendFeedback()} style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgFeedback />} />
</Menu>
<NavLink to='/settings'>
<MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
<ListItemIcon>
<SvgSettings />
</ListItemIcon>
<ListItemText inset primary='Settings' />
</MenuItem>
</NavLink>
<MenuItem onClick={() => showSendFeedback()} style={{ color: 'rgb(117, 117, 117)' }}>
<ListItemIcon>
<SvgFeedback />
</ListItemIcon>
<ListItemText inset primary='Send feedback' />
</MenuItem>
</MenuList>
</SidebarContent>
<SidebarMain>
<HomeRouter enabled={loaded!} data={{ mergedPosts, loadDataStream, hasMorePosts}} />
<HomeRouter enabled={loaded!} data={{ mergedPosts, loadDataStream, hasMorePosts }} />
</SidebarMain>
</Sidebar>
@@ -166,7 +209,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
return {
loadDataStream:
(page: number, limit: number) => dispatch(postActions.dbGetPosts(page,limit)),
(page: number, limit: number) => dispatch(postActions.dbGetPosts(page, limit)),
loadData: () => {
dispatch(postActions.dbGetPosts())
dispatch(imageGalleryActions.dbGetImageGallery())
@@ -216,9 +259,9 @@ const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
const hasMorePosts = post.stream.hasMoreData
Object.keys(followingUsers).forEach((userId) => {
let newPosts = post.userPosts ? post.userPosts[userId] : {}
_.merge(mergedPosts,newPosts)
_.merge(mergedPosts, newPosts)
})
_.merge(mergedPosts,posts)
_.merge(mergedPosts, posts)
return {
authed: authorize.authed,
isVerifide: authorize.isVerifide,

View File

@@ -2,17 +2,20 @@
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
import { connect } from 'react-redux'
import SvgDehaze from 'material-ui/svg-icons/image/dehaze'
import { green700, grey400, blue500 } from 'material-ui/styles/colors'
import { Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarTitle } from 'material-ui/Toolbar'
import SvgDehaze from 'material-ui-icons/dehaze'
import { grey, blue } from 'material-ui/colors'
import Toolbar from 'material-ui/Toolbar'
import IconButton from 'material-ui/IconButton'
import RaisedButton from 'material-ui/RaisedButton'
import Popover, { PopoverAnimationVertical } from 'material-ui/Popover'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import Popover from 'material-ui/Popover'
import AppBar from 'material-ui/AppBar'
import Menu, { MenuList, MenuItem } from 'material-ui/Menu'
import Paper from 'material-ui/Paper'
import NotificationsIcon from 'material-ui/svg-icons/social/notifications'
import NotificationsIcon from 'material-ui-icons/notifications'
import EventListener, { withOptions } from 'react-event-listener'
import Tooltip from 'material-ui/Tooltip'
import Typography from 'material-ui/Typography'
import { Manager, Target, Popper } from 'react-popper'
import { withStyles } from 'material-ui/styles'
// - Import components
import UserAvatarComponent from 'components/userAvatar'
@@ -24,20 +27,19 @@ import { authorizeActions } from 'actions'
import { IHomeHeaderComponentProps } from './IHomeHeaderComponentProps'
import { IHomeHeaderComponentState } from './IHomeHeaderComponentState'
const styles = {
root: {
backgroundColor: '#a5792a'
},
flex: {
flex: 1
}
}
// - Create HomeHeader component class
export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHomeHeaderComponentState> {
export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps, IHomeHeaderComponentState> {
styles = {
toolbarStyle: {
transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
boxSizing: 'border-box',
fontFamily: 'Roboto, sans-serif',
position: 'fixed',
zIndex: '1101',
width: '100%',
top: '0px',
boxShadow: '0 1px 8px rgba(0,0,0,.3)'
},
avatarStyle: {
margin: 5,
cursor: 'pointer'
@@ -49,7 +51,7 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
* Component constructor
* @param {object} props is an object properties of component
*/
constructor (props: IHomeHeaderComponentProps) {
constructor(props: IHomeHeaderComponentProps) {
super(props)
// Default state
@@ -89,10 +91,10 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
// On click toggle sidebar
onToggleSidebar = () => {
if (this.props.sidebarStatus) {
this.props.sidebar!(false,'onToggle')
this.props.sidebar!(false, 'onToggle')
} else {
this.props.sidebar!(true,'onToggle')
this.props.sidebar!(true, 'onToggle')
}
}
@@ -120,9 +122,6 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
* @memberof HomeHeader
*/
handleAvatarTouchTap = (event: any) => {
// This prevents ghost click.
event.preventDefault()
this.setState({
openAvatarMenu: true,
anchorEl: event.currentTarget
@@ -147,7 +146,8 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
*/
handleRequestClose = () => {
this.setState({
openAvatarMenu: false
openAvatarMenu: false,
anchorEl: null
})
}
@@ -183,69 +183,79 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
// Render app DOM component
render () {
const { classes } = this.props
return (
<Toolbar style={this.styles.toolbarStyle as any}>
<EventListener
target='window'
onResize={this.handleResize}
onKeyUp={this.handleKeyUp}
/>
{/* Left side */}
<ToolbarGroup firstChild={true}>
<AppBar position='fixed' color='secondary'>
<Toolbar>
<EventListener
target='window'
onResize={this.handleResize}
onKeyUp={this.handleKeyUp}
/>
{/* Left side */}
<IconButton iconStyle={{ color: '#fff' }} onClick={this.onToggleSidebar} >
<SvgDehaze style={{ color: '#fff', marginLeft: '15px', cursor: 'pointer' }} />
<IconButton onClick={this.onToggleSidebar} >
<SvgDehaze color='primary' style={{ cursor: 'pointer' }} />
</IconButton>
{/* Header title */}
<ToolbarTitle style={{ color: '#fff', marginLeft: '15px' }} text='Green' />
{this.state.showTitle ? <div className='homeHeader__page'>{this.props.title}</div> : ''}
</ToolbarGroup>
<ToolbarGroup>
<Typography type='title' color='primary' style={{ marginLeft: '15px' }} >
Green
</Typography>
<div className='homeHeader__title-root'>
{this.state.showTitle ? <div className='homeHeader__title'>{this.props.title}</div> : ''}
</div>
</ToolbarGroup>
{/* Notification */}
<ToolbarGroup lastChild={true}>
{/* Notification */}
<div className='homeHeader__right'>
{this.props.notifyCount! > 0 ? (<IconButton tooltip='Notifications' onTouchTap={this.handleNotifyTouchTap}>
<div className='homeHeader__notify'>
<div className='title'>{this.props.notifyCount}</div>
</div>
</IconButton>)
: (<IconButton tooltip='Notifications' onTouchTap={this.handleNotifyTouchTap}>
<NotificationsIcon color='rgba(255, 255, 255, 0.87)' />
</IconButton>)}
<Notify open={this.state.openNotifyMenu} anchorEl={this.state.anchorEl} onRequestClose={this.handleCloseNotify}/>
<Manager>
<Target>
{this.props.notifyCount! > 0 ? (
<Tooltip title='Notifications'>
<IconButton onClick={this.handleNotifyTouchTap}>
<div className='homeHeader__notify'>
<div className='title'>{this.props.notifyCount}</div>
</div>
</IconButton>
</Tooltip>)
: (<Tooltip title='Notifications'>
<IconButton onClick={this.handleNotifyTouchTap}>
<NotificationsIcon style={{ color: 'rgba(255, 255, 255, 0.87)' }} />
</IconButton>
</Tooltip>)}
</Target>
<Notify open={this.state.openNotifyMenu} anchorEl={this.state.anchorEl} onRequestClose={this.handleCloseNotify} />
</Manager>
{/* User avatar*/}
<UserAvatarComponent
onTouchTap={this.handleAvatarTouchTap}
onClick={this.handleAvatarTouchTap}
fullName={this.props.fullName!}
fileName={this.props.avatar!}
size={32}
style={this.styles.avatarStyle}
/>
<Popover
<Menu
open={this.state.openAvatarMenu}
anchorEl={this.state.anchorEl}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
onRequestClose={this.handleRequestClose}
>
<Menu>
<MenuItem style={{ backgroundColor: 'white', color: blue500, fontSize: '14px' }} primaryText='MY ACCOUNT' />
<MenuItem primaryText='LOGOUT' style={{ fontSize: '14px' }} onClick={this.handleLogout.bind(this)} />
anchorEl={this.state.anchorEl!}
anchorOrigin={{
vertical: 'top',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
onClose={this.handleRequestClose}>
<MenuItem style={{ backgroundColor: 'white', color: blue[500], fontSize: '14px' }} > MY ACCOUNT </MenuItem>
<MenuItem style={{ fontSize: '14px' }} onClick={this.handleLogout.bind(this)} > LOGOUT </MenuItem>
</Menu>
</Popover>
</Menu>
</div>
</ToolbarGroup>
</Toolbar>
</Toolbar>
</AppBar >
)
}
}
@@ -261,10 +271,10 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IHomeHeaderComponentPr
const mapStateToProps = (state: any, ownProps: IHomeHeaderComponentProps) => {
let notifyCount = state.notify.userNotifies
? Object
.keys(state.notify.userNotifies)
.filter((key) => !state.notify.userNotifies[key].isSeen).length
: 0
? Object
.keys(state.notify.userNotifies)
.filter((key) => !state.notify.userNotifies[key].isSeen).length
: 0
return {
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : '',
@@ -274,4 +284,4 @@ const mapStateToProps = (state: any, ownProps: IHomeHeaderComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(HomeHeaderComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(HomeHeaderComponent as any) as any)

View File

@@ -60,4 +60,9 @@ export interface IHomeHeaderComponentProps {
* @memberof IHomeHeaderComponentProps
*/
sidebar?: (status: boolean, source: string) => void
/**
* Material ui theme style
*/
classes?: any
}

View File

@@ -31,5 +31,5 @@ export interface IHomeHeaderComponentState {
* @type {*}
* @memberof IHomeHeaderComponentState
*/
anchorEl?: HTMLElement
anchorEl?: HTMLElement | null
}

View File

@@ -2,16 +2,14 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { GridList, GridTile } from 'material-ui/GridList'
import GridList, { GridListTile, GridListTileBar } from 'material-ui/GridList'
import IconButton from 'material-ui/IconButton'
import Subheader from 'material-ui/Subheader'
import StarBorder from 'material-ui/svg-icons/toggle/star-border'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import SvgUpload from 'material-ui/svg-icons/file/cloud-upload'
import SvgAddImage from 'material-ui/svg-icons/image/add-a-photo'
import SvgDelete from 'material-ui/svg-icons/action/delete'
import { grey200, grey600 } from 'material-ui/styles/colors'
import FlatButton from 'material-ui/FlatButton'
import StarBorder from 'material-ui-icons/starBorder'
import Button from 'material-ui/Button'
import SvgUpload from 'material-ui-icons/cloudUpload'
import SvgAddImage from 'material-ui-icons/addAPhoto'
import SvgDelete from 'material-ui-icons/delete'
import { grey } from 'material-ui/colors'
import uuid from 'uuid'
// - Import actions
@@ -52,7 +50,8 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
overflowY: 'auto'
},
uploadButton: {
verticalAlign: 'middle'
verticalAlign: 'middle',
fontWeight: 100
},
uploadInput: {
cursor: 'pointer',
@@ -66,11 +65,13 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
},
deleteImage: {
marginLeft: '5px',
cursor: 'pointer'
cursor: 'pointer',
color: 'white'
},
addImage: {
marginRight: '5px',
cursor: 'pointer'
cursor: 'pointer',
color: 'white'
}
}
@@ -125,7 +126,7 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
const { resizedImage, fileName } = event.detail
const {uploadImage} = this.props
uploadImage(resizedImage,fileName)
uploadImage!(resizedImage,fileName)
}
/**
@@ -150,11 +151,9 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
return this.props.images!.map((image: Image, index) => {
return (<GridTile
return (
<GridListTile
key={image.id!}
title={<SvgDelete hoverColor={grey200} color='white' style={this.styles.deleteImage as any} onClick={evt => this.handleDeleteImage(evt, image.id!)} />}
subtitle={<span></span>}
actionIcon={<SvgAddImage hoverColor={grey200} color='white' style={this.styles.addImage as any} onClick={evt => this.handleSetImage(evt, image.URL,image.fullPath)} />}
>
<div>
<div style={{ overflowY: 'hidden', overflowX: 'auto' }}>
@@ -171,7 +170,15 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
</ul>
</div>
</div>
</GridTile>)
<GridListTileBar
title={<SvgDelete style={this.styles.deleteImage as any} onClick={evt => this.handleDeleteImage(evt, image.id!)} />}
titlePosition='top'
actionIcon={
<SvgAddImage style={this.styles.addImage as any} onClick={evt => this.handleSetImage(evt, image.URL,image.fullPath)} />
}
actionPosition='left'
/>
</GridListTile>)
})
}
@@ -193,21 +200,24 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
cellHeight={180}
style={this.styles.gridList as any}
>
<GridTile >
<GridListTile key='upload-image-gallery' >
<div style={{ display: 'flex', backgroundColor: 'rgba(222, 222, 222, 0.52)', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
<FlatButton
label='Upload Photo'
labelStyle={{ fontWeight: 100 }}
labelPosition='before'
style={this.styles.uploadButton}
containerElement='label'
>
<input type='file' onChange={this.onFileChange} accept='image/*' style={this.styles.uploadInput as any} />
</FlatButton>
<input
accept='image/*'
style={this.styles.uploadInput as any}
id='raised-button-file'
onChange={this.onFileChange}
type='file'
/>
<label htmlFor='raised-button-file'>
<Button raised component='span' style={this.styles.uploadButton as any}>
Upload
</Button>
</label>
</div>
</GridTile>
</GridListTile>
{this.imageList()}
</GridList>
</div>

View File

@@ -16,4 +16,9 @@ export interface IImgComponentProps {
*/
style?: {}
/**
* Styles
*/
classes?: any
}

View File

@@ -2,7 +2,8 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import SvgImage from 'material-ui/svg-icons/image/image'
import SvgImage from 'material-ui-icons/image'
import { withStyles } from 'material-ui/styles'
// - Import app components
@@ -13,6 +14,15 @@ import * as imageGalleryActions from 'actions/imageGalleryActions'
import { IImgComponentProps } from './IImgComponentProps'
import { IImgComponentState } from './IImgComponentState'
const styles = (theme: any) => ({
image: {
verticalAlign: 'top',
maxWidth: '100%',
minWidth: '100%',
width: '100%'
}
})
/**
* Create component class
*/
@@ -77,9 +87,10 @@ export class ImgComponent extends Component<IImgComponentProps,IImgComponentStat
let { fileName, style } = this.props
let { isImageLoaded } = this.state
const {classes} = this.props
return (
<div>
<img onLoad={this.handleLoadImage} src={fileName || ''} style={isImageLoaded ? style : { display: 'none' }} />
<img className={classes.image} onLoad={this.handleLoadImage} src={fileName || ''} style={isImageLoaded ? style : { display: 'none' }} />
<div style={Object.assign({},{ backgroundColor: 'white' }, isImageLoaded ? { display: 'none' } : this.styles.loding) }>
<div style={this.styles.loadingContent as any}>
<SvgImage style={this.styles.loadingImage} />
@@ -117,4 +128,4 @@ const mapStateToProps = (state: any, ownProps: IImgComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(ImgComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ImgComponent as any)as any)

View File

@@ -2,7 +2,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import SvgImage from 'material-ui/svg-icons/image/image'
import SvgImage from 'material-ui-icons/image'
// - Import app components

View File

@@ -21,4 +21,9 @@ export interface ILoginComponentProps {
* @memberof ILoginComponentProps
*/
signupPage?: () => any
/**
* Styles
*/
classes?: any
}

View File

@@ -5,25 +5,35 @@ import { NavLink, withRouter } from 'react-router-dom'
import { push } from 'react-router-redux'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import FlatButton from 'material-ui/FlatButton'
import FontIcon from 'material-ui/FontIcon'
import RaisedButton from 'material-ui/Button'
import Button from 'material-ui/Button'
import IconButton from 'material-ui/IconButton'
import Divider from 'material-ui/Divider'
import ActionAndroid from 'material-ui/svg-icons/action/android'
import ActionAndroid from 'material-ui-icons/android'
import { withStyles } from 'material-ui/styles'
// - Import actions
import * as authorizeActions from 'actions/authorizeActions'
import { ILoginComponentProps } from './ILoginComponentProps'
import { ILoginComponentState } from './ILoginComponentState'
import { firebaseAuth } from 'data/firebaseClient'
import { OAuthType } from 'core/domain/authorize'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
}
})
// - Create Login component class
export class LoginComponent extends Component<ILoginComponentProps,ILoginComponentState> {
styles = {
singinOptions: {
paddingBottom: 10
paddingBottom: 10,
justifyContent: 'space-around',
display: 'flex'
},
divider: {
marginBottom: 10,
@@ -124,6 +134,7 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
* @return {react element} return the DOM which rendered by component
*/
render () {
const {classes} = this.props
const paperStyle = {
minHeight: 370,
@@ -148,7 +159,7 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
}}>Green</h1>
<div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} >
<Paper style={paperStyle} elevation={1} >
<div style={{ padding: '48px 40px 36px' }}>
<div style={{
paddingLeft: '40px',
@@ -164,38 +175,37 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
margin: 0
}} className='zoomOutLCorner animated'>Sign in</h2>
</div>
<div style={this.styles.singinOptions}>
<FlatButton
icon={<div className='icon-fb icon'></div>}
onClick={() => loginWithOAuth(OAuthType.FACEBOOK)}
/>
<FlatButton
icon={<div className='icon-google icon'></div>}
onClick={() => loginWithOAuth(OAuthType.GOOGLE)}
/>
<FlatButton
icon={<div className='icon-github icon'></div>}
onClick={() => loginWithOAuth(OAuthType.GITHUB)}
/>
<div style={this.styles.singinOptions as any}>
<IconButton
onClick={() => loginWithOAuth!(OAuthType.FACEBOOK)}
><div className='icon-fb icon'></div></IconButton>
<IconButton
onClick={() => loginWithOAuth!(OAuthType.GOOGLE)}
> <div className='icon-google icon'></div> </IconButton>
<IconButton
onClick={() => loginWithOAuth!(OAuthType.GITHUB)}
> <div className='icon-github icon'></div> </IconButton>
</div>
<Divider style={this.styles.divider} />
<TextField
className={classes.textField}
autoFocus
onChange={this.handleInputChange}
errorText={this.state.emailInputError}
helperText={this.state.emailInputError}
error={this.state.emailInputError.trim() !== ''}
name='emailInput'
floatingLabelStyle={{ fontSize: '15px' }}
floatingLabelText='Email'
label='Email'
type='email'
tabIndex={1}
/><br />
<TextField
className={classes.textField}
onChange={this.handleInputChange}
errorText={this.state.passwordInputError}
helperText={this.state.passwordInputError}
error={this.state.passwordInputError.trim() !== ''}
name='passwordInput'
floatingLabelStyle={{ fontSize: '15px' }}
floatingLabelText='Password'
label='Password'
type='password'
tabIndex={2}
/><br />
@@ -203,10 +213,10 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
<br />
<div className='login__button-box'>
<div>
<FlatButton label='Create an account' onClick={this.props.signupPage} tabIndex={4} />
<Button onClick={this.props.signupPage} tabIndex={4}> Create an account </Button>
</div>
<div >
<RaisedButton label='Login' primary={true} onClick={this.handleForm} tabIndex={3} />
<Button raised color='primary' onClick={this.handleForm} tabIndex={3} > Login </Button>
</div>
</div>
<span style={this.styles.restPassword as any}>Have you forgot your password? <NavLink to='/resetPassword' style={this.styles.restPasswordLink}>reset your password</NavLink></span>
@@ -249,4 +259,4 @@ const mapStateToProps = (state: any, ownProps: ILoginComponentProps) => {
}
// - Connect component to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(LoginComponent as any))
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(LoginComponent as any) as any))

View File

@@ -109,4 +109,9 @@ export interface IMasterComponentProps {
*/
hideMasterLoading?: () => any
/**
* Whether send feesback box is visible
*/
sendFeedbackStatus?: boolean
}

View File

@@ -1,11 +1,11 @@
/// <reference types="@types/material-ui" />
// - Import react components
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Route, Switch, NavLink, withRouter, Redirect } from 'react-router-dom'
import { push } from 'react-router-redux'
import Snackbar from 'material-ui/Snackbar'
import LinearProgress from 'material-ui/LinearProgress'
import { LinearProgress } from 'material-ui/Progress'
// - Import components
@@ -125,12 +125,12 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
*/
public render () {
const { progress, global, loaded, guest, uid } = this.props
const { progress, global, loaded, guest, uid, sendFeedbackStatus } = this.props
const { loading, isVerifide } = this.state
return (
<div id='master'>
<SendFeedback />
{sendFeedbackStatus ? <SendFeedback /> : ''}
<div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}>
<LinearProgress mode='determinate' value={progress.percent} />
</div>
@@ -196,7 +196,9 @@ const mapDispatchToProps = (dispatch: any, ownProps: IMasterComponentProps) => {
*/
const mapStateToProps = (state: any) => {
const { authorize, global, user, post, comment, imageGallery, vote, notify, circle } = state
const { sendFeedbackStatus } = global
return {
sendFeedbackStatus,
guest: authorize.guest,
uid: authorize.uid,
authed: authorize.authed,

View File

@@ -1,8 +1,7 @@
// - Import react components
import React, { Component } from 'react'
import CircularProgress from 'material-ui/CircularProgress'
import { CircularProgress } from 'material-ui/Progress'
import Dialog from 'material-ui/Dialog'
import RefreshIndicator from 'material-ui/RefreshIndicator'
import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps'
import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
@@ -24,16 +23,16 @@ export default class MasterLoadingComponent extends Component<IMasterLoadingComp
return (
<div className='mLoading__loading' style={{ display: (activeLoading ? 'flex' : 'none') }}>
<RefreshIndicator
size={50}
left={70}
top={0}
status='loading'
/>
<CircularProgress
color='secondary'
size={50}
mode='determinate'
value={25}
min={0}
max={50}
/>
</div>
)
}

View File

@@ -40,6 +40,11 @@ export interface INotifyComponentProps {
* @type {*}
* @memberof INotifyComponentProps
*/
anchorEl: any
anchorEl?: any
/**
* Material ui styles
*/
classes?: any
}

View File

@@ -2,7 +2,14 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import Popover, { PopoverAnimationVertical } from 'material-ui/Popover'
import classNames from 'classnames'
import { Manager, Target, Popper } from 'react-popper'
import ClickAwayListener from 'material-ui/utils/ClickAwayListener'
import Grow from 'material-ui/transitions/Grow'
import { withStyles } from 'material-ui/styles'
import Typography from 'material-ui/Typography'
import Paper from 'material-ui/Paper'
import List, { ListItem, ListItemSecondaryAction, ListItemText } from 'material-ui/List'
// - Import app components
import NotifyItem from 'components/notifyItem'
@@ -15,10 +22,30 @@ import { INotifyComponentProps } from './INotifyComponentProps'
import { INotifyComponentState } from './INotifyComponentState'
import { Notification } from 'core/domain/notifications'
const styles = (theme: any) => ({
root: {
width: 360,
maxWidth: 360,
backgroundColor: '#efefef',
minHeight: 376,
display: 'flex'
},
noNotify: {
color: '#888888',
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
width: '100%'
},
popperClose: {
pointerEvents: 'none'
}
})
/**
* Create component class
*/
export class NotifyComponent extends Component<INotifyComponentProps,INotifyComponentState> {
export class NotifyComponent extends Component<INotifyComponentProps, INotifyComponentState> {
static propTypes = {
/**
@@ -60,7 +87,7 @@ export class NotifyComponent extends Component<INotifyComponentProps,INotifyComp
let parsedDOM: any[] = []
if (notifications) {
Object.keys(notifications).forEach((key) => {
const {notifierUserId} = notifications![key]
const { notifierUserId } = notifications![key]
parsedDOM.push(
<NotifyItem
key={key}
@@ -68,7 +95,7 @@ export class NotifyComponent extends Component<INotifyComponentProps,INotifyComp
fullName={(info![notifierUserId] ? info![notifierUserId].fullName || '' : '')}
avatar={(info![notifierUserId] ? info![notifierUserId].avatar || '' : '')}
id={key}
isSeen={(notifications![key] ? notifications![key].isSeen || false : false )}
isSeen={(notifications![key] ? notifications![key].isSeen || false : false)}
url={(notifications![key] ? notifications![key].url || '' : '')}
notifierUserId={notifierUserId}
closeNotify={onRequestClose}
@@ -84,25 +111,27 @@ export class NotifyComponent extends Component<INotifyComponentProps,INotifyComp
* @return {react element} return the DOM which rendered by component
*/
render () {
let { open, anchorEl, onRequestClose } = this.props
let { open, anchorEl, onRequestClose, classes } = this.props
const noNotify = ( <div className={classes.noNotify}>
All caught up! </div>)
const items = this.notifyItemList()
return (
<Popover
className='homeHeader__notify-menu'
open={open}
anchorEl={anchorEl}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
onRequestClose={onRequestClose}
<Popper
placement='bottom-start'
eventsEnabled={open}
className={classNames({ [classes.popperClose]: !open })}
>
<div className='container'>
<div className='title'>Green </div>
<div className='content'>
{this.notifyItemList()}
</div>
</div>
</Popover>
<ClickAwayListener onClickAway={onRequestClose}>
<Grow in={open} style={{ transformOrigin: '0 0 0' }}>
<Paper className={classes.root} elevation={4} >
{items.length > 0 ? <List>{items}</List> : noNotify}
</Paper>
</Grow>
</ClickAwayListener>
</Popper>
)
}
}
@@ -133,4 +162,4 @@ const mapStateToProps = (state: any, ownProps: INotifyComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(NotifyComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(NotifyComponent as any) as any)

View File

@@ -83,4 +83,9 @@ export interface INotifyItemComponentProps {
* @memberof INotifyItemComponentProps
*/
seenNotify?: (notificationId: string) => any
/**
* Material ui styles
*/
classes?: any
}

View File

@@ -4,8 +4,10 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { NavLink } from 'react-router-dom'
import { push } from 'react-router-redux'
import SvgClose from 'material-ui/svg-icons/navigation/close'
import { grey400 } from 'material-ui/styles/colors'
import SvgClose from 'material-ui-icons/close'
import { grey } from 'material-ui/colors'
import { withStyles } from 'material-ui/styles'
import List, { ListItem, ListItemSecondaryAction, ListItemText } from 'material-ui/List'
// - Import app components
import UserAvatar from 'components/userAvatar'
@@ -18,6 +20,20 @@ import * as notifyActions from 'actions/notifyActions'
import { INotifyItemComponentProps } from './INotifyItemComponentProps'
import { INotifyItemComponentState } from './INotifyItemComponentState'
const styles = (theme: any) => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper
},
closeButton: {color: 'black'},
closeIcon: {width: 12, height: 12},
listItem: {
backgroundColor: 'white',
marginBottom: '6px'
}
})
/**
* Create component class
*/
@@ -90,13 +106,12 @@ export class NotifyItemComponent extends Component<INotifyItemComponentProps,INo
* @return {react element} return the DOM which rendered by component
*/
render () {
let { description, fullName, avatar, isSeen, id, goTo,closeNotify, notifierUserId, url, deleteNotiy } = this.props
let { description, fullName, avatar, isSeen, id, goTo,closeNotify, notifierUserId, url, deleteNotiy, classes } = this.props
return (
<div className='item' style={isSeen ? { opacity: 0.6 } : {}} key={id}>
<div className='avatar'>
<NavLink
<ListItem key={notifierUserId} dense button className={classes.listItem} style={isSeen ? { opacity: 0.6 } : {}}>
<NavLink
to={`/${notifierUserId}`}
onClick={(evt) => {
evt.preventDefault()
@@ -106,21 +121,20 @@ export class NotifyItemComponent extends Component<INotifyItemComponentProps,INo
>
<UserAvatar fullName={fullName} fileName={avatar} />
</NavLink>
</div>
<div className='info'>
<NavLink to={url} onClick={this.handleSeenNotify}>
<ListItemText primary={<NavLink to={url} onClick={this.handleSeenNotify}>
<div className='user-name'>
{fullName}
</div>
<div className='description'>
{description}
</div>
</NavLink>
</NavLink>} />
<ListItemSecondaryAction className={classes.closeButton}>
<div onClick={() => deleteNotiy!(id)}>
<SvgClose className={classes.closeIcon} style={{ cursor: 'pointer' }} />
</div>
<div className='close' onClick={() => deleteNotiy!(id)}>
<SvgClose hoverColor={grey400} style={{ cursor: 'pointer' }} />
</div>
</div>
</ListItemSecondaryAction>
</ListItem>
)
}
@@ -153,4 +167,4 @@ const mapStateToProps = (state: any, ownProps: INotifyItemComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(NotifyItemComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(NotifyItemComponent as any) as any )

View File

@@ -1,4 +1,8 @@
export interface IPeopleComponentState {
/**
* Tab index
*/
tabIndex: number
}

View File

@@ -3,9 +3,11 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import PropTypes from 'prop-types'
import { Tabs, Tab } from 'material-ui/Tabs'
import { grey50, grey200, grey400, grey600, cyan500 } from 'material-ui/styles/colors'
import Tabs, { Tab } from 'material-ui/Tabs'
import { grey, cyan } from 'material-ui/colors'
import { push } from 'react-router-redux'
import AppBar from 'material-ui/AppBar'
import Typography from 'material-ui/Typography'
// - Import app components
import FindPeople from 'components/findPeople'
@@ -21,6 +23,14 @@ import * as globalActions from 'actions/globalActions'
import { IPeopleComponentProps } from './IPeopleComponentProps'
import { IPeopleComponentState } from './IPeopleComponentState'
const TabContainer = (props: any) => {
return (
<Typography component='div' style={{ padding: 8 * 3 }}>
{props.children}
</Typography>
)
}
/**
* Create component class
*/
@@ -36,16 +46,41 @@ export class PeopleComponent extends Component<IPeopleComponentProps,IPeopleComp
*/
constructor (props: IPeopleComponentProps) {
super(props)
const {tab} = this.props.match.params
// Defaul state
this.state = {
tabIndex: this.getTabIndexByNav(tab)
}
// Binding functions to `this`
}
/**
* Hadle on tab change
*/
handleChangeTab = (event: any, value: any) => {
const {circlesLoaded, goTo, setHeaderTitle} = this.props
this.setState({ tabIndex: value })
switch (value) {
case 0:
goTo!('/people')
setHeaderTitle!('People')
break
case 1:
goTo!('/people/circles')
setHeaderTitle!('Circles')
break
case 2:
goTo!('/people/followers')
setHeaderTitle!('Followers')
break
default:
break
}
}
componentWillMount () {
const { setHeaderTitle} = this.props
const {tab} = this.props.match.params
@@ -91,48 +126,44 @@ export class PeopleComponent extends Component<IPeopleComponentProps,IPeopleComp
}
const {circlesLoaded, goTo, setHeaderTitle} = this.props
const {tab} = this.props.match.params
let tabIndex = 0
switch (tab) {
case undefined:
case '':
tabIndex = 0
break
case 'circles':
tabIndex = 1
break
case 'followers':
tabIndex = 2
break
default:
break
}
const {tabIndex} = this.state
return (
<div style={styles.people}>
<Tabs inkBarStyle={{backgroundColor: grey50}} initialSelectedIndex={tabIndex} >
<Tab label='Find People' onActive={() => {
goTo!('/people')
setHeaderTitle!('People')
}} >
{circlesLoaded ? <FindPeople /> : ''}
</Tab>
<Tab label='Following' onActive={() => {
goTo!('/people/circles')
setHeaderTitle!('Circles')
}} >
{circlesLoaded ? <Following/> : ''}
{circlesLoaded ? <YourCircles/> : ''}
</Tab>
<Tab label='Followers' onActive={() => {
goTo!('/people/followers')
setHeaderTitle!('Followers')
}}>
{circlesLoaded ? <Followers /> : ''}
</Tab>
<AppBar position='static' color='default'>
<Tabs indicatorColor= {grey[50]}
onChange={this.handleChangeTab}
value={tabIndex} centered
textColor='primary'
>
<Tab label='Find People' />
<Tab label='Following' />
<Tab label='Followers' />
</Tabs>
</AppBar>
{tabIndex === 0 && <TabContainer>{circlesLoaded ? <FindPeople /> : ''}</TabContainer>}
{tabIndex === 1 && <TabContainer>
{circlesLoaded ? <Following/> : ''}
{circlesLoaded ? <YourCircles/> : ''}
</TabContainer>}
{tabIndex === 2 && <TabContainer>{circlesLoaded ? <Followers /> : ''}</TabContainer>}
</div>
)
}
/**
* Get tab index by navigation name
*/
private getTabIndexByNav: (navName: string) => number = (navName: string) => {
let tabIndex = 0
switch (navName) {
case 'circles':
return 1
case 'followers':
return 2
default:
return 0
}
}
}
/**

View File

@@ -114,4 +114,9 @@ export interface IPostComponentProps {
* @memberof ICommentGroupComponentProps
*/
commentList?: {[commentId: string]: Comment}
/**
* Styles
*/
classes?: any
}

View File

@@ -38,5 +38,18 @@ export interface IPostComponentState {
*/
openPostWrite: boolean
/**
* Open the comment group
*/
openCommentGroup?: () => void
/**
* Post menu anchor element
*/
postMenuAnchorEl?: any
/**
* Whether post menu open
*/
isPostMenuOpen?: boolean
}

View File

@@ -6,27 +6,33 @@ import { push } from 'react-router-redux'
import PropTypes from 'prop-types'
import moment from 'moment'
import Linkify from 'react-linkify'
import copy from 'copy-to-clipboard'
// - Material UI
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import SvgShare from 'material-ui/svg-icons/social/share'
import SvgLink from 'material-ui/svg-icons/content/link'
import SvgComment from 'material-ui/svg-icons/communication/comment'
import SvgFavorite from 'material-ui/svg-icons/action/favorite'
import SvgFavoriteBorder from 'material-ui/svg-icons/action/favorite-border'
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import Typography from 'material-ui/Typography'
import SvgShare from 'material-ui-icons/share'
import SvgLink from 'material-ui-icons/link'
import SvgComment from 'material-ui-icons/comment'
import SvgFavorite from 'material-ui-icons/favorite'
import SvgFavoriteBorder from 'material-ui-icons/favoriteBorder'
import Checkbox from 'material-ui/Checkbox'
import FlatButton from 'material-ui/FlatButton'
import Button from 'material-ui/Button'
import Divider from 'material-ui/Divider'
import { grey200, grey400, grey600 } from 'material-ui/styles/colors'
import { grey } from 'material-ui/colors'
import Paper from 'material-ui/Paper'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import { MenuList, MenuItem } from 'material-ui/Menu'
import TextField from 'material-ui/TextField'
import Dialog from 'material-ui/Dialog'
import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
import IconMenu from 'material-ui/IconMenu'
import MoreVertIcon from 'material-ui-icons/moreVert'
import { ListItemIcon, ListItemText } from 'material-ui/List'
import { withStyles } from 'material-ui/styles'
import { Manager, Target, Popper } from 'react-popper'
import Grow from 'material-ui/transitions/Grow'
import ClickAwayListener from 'material-ui/utils/ClickAwayListener'
import classNames from 'classnames'
import reactStringReplace from 'react-string-replace'
@@ -45,33 +51,68 @@ import * as globalActions from 'actions/globalActions'
import { IPostComponentProps } from './IPostComponentProps'
import { IPostComponentState } from './IPostComponentState'
const styles = (theme: any) => ({
iconButton: {
width: 27,
marginLeft: 5
},
vote: {
display: 'flex',
flex: 1
},
voteCounter: {
color: 'rgb(134, 129, 129)',
fontSize: 10,
fontWeight: 100,
padding: 2
},
commentCounter: {
color: 'rgb(134, 129, 129)',
fontSize: 10,
fontWeight: 100,
padding: 4
},
popperOpen: {
zIndex: 10
},
popperClose: {
pointerEvents: 'none',
zIndex: 0
},
shareLinkPaper: {
minHeight: 80,
padding: 10,
minWidth: 460
},
clipboard: {
fontSize: '18px',
textAlign: 'center',
marginTop: '10px',
color: '#1e882d',
fontWeight: 100
},
postBody: {
wordWrap: 'break-word',
color: 'rgba(0, 0, 0, 0.87)',
fontSize: '0.875rem',
fontWeight: 400,
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
lineHeight: '1.46429em'
},
image: {
width: '100%',
height: 500
}
})
// - Create component class
export class PostComponent extends Component<IPostComponentProps,IPostComponentState> {
export class PostComponent extends Component<IPostComponentProps, IPostComponentState> {
styles = {
counter: {
lineHeight: '36px',
color: '#777',
fontSize: '12px',
marginRight: '6px'
},
postBody: {
wordWrap: 'break-word'
},
dialog: {
width: '',
maxWidth: '530px',
borderRadius: '4px'
},
rightIconMenu: {
position: 'absolute',
right: 18,
top: 8
},
iconButton: {
width: 24,
height: 24
}
}
@@ -82,7 +123,7 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
*/
constructor (props: IPostComponentProps) {
super(props)
const {post} = props
const { post } = props
this.state = {
/**
* Post text
@@ -119,7 +160,15 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
/**
* If it's true, post write will be open
*/
openPostWrite: false
openPostWrite: false,
/**
* Post menu anchor element
*/
postMenuAnchorEl: null,
/**
* Whether post menu open
*/
isPostMenuOpen: false
}
// Binding functions to this
@@ -140,10 +189,10 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @param {event} evt passed by clicking on comment slide show
*/
handleOpenComments = () => {
const { getPostComments, commentList, post} = this.props
const {id, ownerUserId} = post
const { getPostComments, commentList, post } = this.props
const { id, ownerUserId } = post
if (!commentList) {
getPostComments(ownerUserId!, id!)
getPostComments!(ownerUserId!, id!)
}
this.setState({
openComments: !this.state.openComments
@@ -181,10 +230,31 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @memberof Post
*/
handleDelete = () => {
const {post} = this.props
const { post } = this.props
this.props.delete!(post.id!)
}
/**
* Open post menu
*/
openPostMenu = (event: any) => {
console.log(event.currentTarget)
this.setState({
postMenuAnchorEl: event.currentTarget,
isPostMenuOpen: true
})
}
/**
* Close post menu
*/
closePostMenu = (event: any) => {
this.setState({
postMenuAnchorEl: event.currentTarget,
isPostMenuOpen: false
})
}
/**
* Show copy link
*
@@ -205,6 +275,8 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @memberof Post
*/
handleOpenShare = () => {
const {post} = this.props
copy(`${location.origin}/${post.ownerUserId}/posts/${post.id}`)
this.setState({
shareOpen: true
})
@@ -264,77 +336,114 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @return {react element} return the DOM which rendered by component
*/
render () {
const {post ,setHomeTitle, goTo, fullName, isPostOwner, commentList, avatar} = this.props
const { post, setHomeTitle, goTo, fullName, isPostOwner, commentList, avatar, classes } = this.props
const { postMenuAnchorEl, isPostMenuOpen } = this.state
const RightIconMenu = () => (
<IconMenu iconButtonElement={IconButtonElement} style={{ display: 'block', position: 'absolute', top: '0px', right: '4px' }}>
<MenuItem primaryText='Edit' onClick={this.handleOpenPostWrite} />
<MenuItem primaryText='Delete' onClick={this.handleDelete} />
<MenuItem primaryText={post.disableComments ? 'Enable comments' : 'Disable comments'} onClick={() => this.props.toggleDisableComments!(!post.disableComments)} />
<MenuItem primaryText={post.disableSharing ? 'Enable sharing' : 'Disable sharing'} onClick={() => this.props.toggleSharingComments!(!post.disableSharing)} />
</IconMenu>
<Manager>
<Target>
<IconButton
aria-owns={isPostMenuOpen! ? 'post-menu' : null}
aria-haspopup='true'
onClick={this.openPostMenu.bind(this)}
>
<MoreVertIcon />
</IconButton>
</Target>
<Popper
placement='bottom-start'
eventsEnabled={isPostMenuOpen!}
className={classNames({ [classes.popperClose]: !isPostMenuOpen! }, { [classes.popperOpen]: isPostMenuOpen! })}
>
<ClickAwayListener onClickAway={this.closePostMenu}>
<Grow in={isPostMenuOpen!} id='post-menu' style={{ transformOrigin: '0 0 0' }}>
<Paper>
<MenuList role='menu'>
<MenuItem onClick={this.handleOpenPostWrite} > Edit </MenuItem>
<MenuItem onClick={this.handleDelete} > Delete </MenuItem>
<MenuItem
onClick={() => this.props.toggleDisableComments!(!post.disableComments)} >
{post.disableComments ? 'Enable comments' : 'Disable comments'}
</MenuItem>
<MenuItem
onClick={() => this.props.toggleSharingComments!(!post.disableSharing)} >
{post.disableSharing ? 'Enable sharing' : 'Disable sharing'}
</MenuItem>
</MenuList>
</Paper>
</Grow>
</ClickAwayListener>
</Popper>
</Manager>
)
const {ownerUserId, ownerDisplayName, creationDate, image, body} = post
const { ownerUserId, ownerDisplayName, creationDate, image, body } = post
// Define variables
return (
<Card>
<CardHeader
title={<NavLink to={`/${ownerUserId}`}>{ownerDisplayName}</NavLink>}
subtitle={moment.unix(creationDate!).fromNow() + ' | public'}
subheader={moment.unix(creationDate!).fromNow() + ' | public'}
avatar={<NavLink to={`/${ownerUserId}`}><UserAvatar fullName={fullName!} fileName={avatar!} size={36} /></NavLink>}
action={isPostOwner ? <RightIconMenu /> : ''}
>
{isPostOwner ? ( <div style={this.styles.rightIconMenu as any}><RightIconMenu /></div>) : ''}
</CardHeader>
{image ? (
<CardMedia>
<CardMedia image={image}>
<Img fileName={image} />
</CardMedia>) : ''}
<CardText style={this.styles.postBody}>
<Linkify properties={{target: '_blank', style: {color: 'blue'}}}>
{reactStringReplace(body,/#(\w+)/g, (match: string, i: string) => (
<NavLink
style={{color: 'green'}}
key={match + i}
to={`/tag/${match}`}
onClick ={evt => {
evt.preventDefault()
goTo!(`/tag/${match}`)
setHomeTitle!(`#${match}`)
}}
>
#{match}
<CardContent className={classes.postBody}>
<Linkify properties={{ target: '_blank', style: { color: 'blue' } }}>
{reactStringReplace(body, /#(\w+)/g, (match: string, i: string) => (
<NavLink
style={{ color: 'green' }}
key={match + i}
to={`/tag/${match}`}
onClick={evt => {
evt.preventDefault()
goTo!(`/tag/${match}`)
setHomeTitle!(`#${match}`)
}}
>
#{match}
</NavLink>
</NavLink>
))}
))}
</Linkify>
</CardText>
</CardContent>
<CardActions>
<div style={{ margin: '16px 8px', display: 'flex', justifyContent: 'space-between' }}>
<div style={{ display: 'flex' }}>
{/*<FloatingActionButton style={{ margin: "0 8px" }} zDepth={1} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: "36px", width: "36px" }} zDepth={1} secondary={false}>*/}
<div className='g__circle' onClick={this.handleVote}>
<div className={classes.vote}>
<IconButton
className={classes.iconButton}
onClick={this.handleVote}
aria-label='Love'>
<Checkbox
checkedIcon={<SvgFavorite style={{fill: '#4CAF50'}}/>}
uncheckedIcon={<SvgFavoriteBorder style={{fill: '#757575'}} />}
checked={this.props.currentUserVote}
style={{transform: 'translate(6px, 6px)'}}
/>
</div>
<div style={this.styles.counter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div>
</div>
<div style={{ display: 'flex' }}>
{!post.disableComments ? (<div style={{display: 'inherit'}}><FloatingActionButton onClick={this.handleOpenComments} style={{ margin: '0 8px' }} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: '36px', width: '36px' }} zDepth={1} secondary={false}>
<SvgComment viewBox='0 -9 24 34' style={{ height: '30px', width: '30px' }} /> 3
</FloatingActionButton>
<div style={this.styles.counter}>{post.commentCounter! > 0 ? post.commentCounter : ''} </div></div>) : ''}
{!post.disableSharing ? (<FloatingActionButton onClick={this.handleOpenShare} style={{ margin: '0 8px' }} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: '36px', width: '36px' }} zDepth={1} secondary={false}>
<SvgShare viewBox='0 -9 24 34' style={{ height: '30px', width: '30px' }} />
</FloatingActionButton>) : ''}
</div>
className={classes.iconButton}
checkedIcon={<SvgFavorite style={{ fill: '#4CAF50' }} />}
icon={<SvgFavoriteBorder style={{ fill: '#757575' }} />}
checked={this.props.currentUserVote}
/>
<div className={classes.voteCounter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div>
</IconButton>
</div>
{!post.disableComments ?
(<div style={{ display: 'inherit' }}><IconButton
className={classes.iconButton}
onClick={this.handleOpenComments}
aria-label='Comment'>
<SvgComment />
<div className={classes.commentCounter}>{post.commentCounter! > 0 ? post.commentCounter : ''} </div>
</IconButton>
</div>) : ''}
{!post.disableSharing ? (<IconButton
className={classes.iconButton}
onClick={this.handleOpenShare}
aria-label='Comment'>
<SvgShare />
</IconButton>) : ''}
</CardActions>
<CommentGroup open={this.state.openComments} comments={commentList} ownerPostUserId={post.ownerUserId!} onToggleRequest={this.handleOpenComments} isPostOwner={this.props.isPostOwner!} disableComments={post.disableComments!} postId={post.id!} />
@@ -342,29 +451,33 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
{/* Copy link dialog*/}
<Dialog
title='Share On'
modal={false}
open={this.state.shareOpen}
onRequestClose={this.handleCloseShare}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
contentStyle={this.styles.dialog}
autoDetectWindowHeight={false}
actionsContainerStyle={{ borderTop: '1px solid rgb(224, 224, 224)' }}
onClose={this.handleCloseShare}
>
{!this.state.openCopyLink
? (<Paper >
<Menu>
<MenuItem primaryText='Copy Link' leftIcon={<SvgLink />} onClick={this.handleCopyLink} />
</Menu>
</Paper>)
: <TextField fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} />
}
<Paper className={classes.shareLinkPaper}>
{!this.state.openCopyLink
? (<MenuList>
<MenuItem onClick={this.handleCopyLink} >
<ListItemIcon>
<SvgLink />
</ListItemIcon>
<ListItemText inset primary='Copy Link' />
</MenuItem>
</MenuList>)
: <div>
<TextField autoFocus fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} />
<Typography className={classNames('animate-top', classes.clipboard)} type='headline' component='h2'>
Link has been copied to clipboard ...
</Typography>
</div>}
</Paper>
</Dialog>
<PostWrite
open={this.state.openPostWrite}
onRequestClose={this.handleClosePostWrite}
edit={true}
postModel= {post}
open={this.state.openPostWrite}
onRequestClose={this.handleClosePostWrite}
edit={true}
postModel={post}
/>
</Card>
@@ -380,10 +493,10 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @return {object} props of component
*/
const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
const {post} = ownProps
const { post } = ownProps
return {
vote: () => dispatch(voteActions.dbAddVote(post.id!,post.ownerUserId!)),
unvote: () => dispatch(voteActions.dbDeleteVote(post.id!, post.ownerUserId!)) ,
vote: () => dispatch(voteActions.dbAddVote(post.id!, post.ownerUserId!)),
unvote: () => dispatch(voteActions.dbDeleteVote(post.id!, post.ownerUserId!)),
delete: (id: string) => dispatch(postActions.dbDeletePost(id)),
toggleDisableComments: (status: boolean) => {
post.disableComments = status
@@ -391,11 +504,11 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
},
toggleSharingComments: (status: boolean) => {
post.disableSharing = status
dispatch(postActions.dbUpdatePost({id: post.id!, disableSharing: status},(x: any) => x))
dispatch(postActions.dbUpdatePost({ id: post.id!, disableSharing: status }, (x: any) => x))
},
goTo: (url: string) => dispatch(push(url)),
setHomeTitle: (title: string) => dispatch(globalActions.setHeaderTitle(title || '')),
getPostComments: (ownerUserId: string, postId: string) => dispatch(commentActions.dbGetComments(ownerUserId,postId))
getPostComments: (ownerUserId: string, postId: string) => dispatch(commentActions.dbGetComments(ownerUserId, postId))
}
}
@@ -407,13 +520,12 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: IPostComponentProps) => {
const {post, vote, authorize, comment} = state
const {uid} = authorize
const { post, vote, authorize, comment } = state
const { uid } = authorize
let currentUserVote = ownProps.post.votes ? ownProps.post.votes[uid] : false
const postModel = post.userPosts[ownProps.post.ownerUserId!][ownProps.post.id!]
const postOwner = (post.userPosts[uid] ? Object.keys(post.userPosts[uid]).filter((key) => { return ownProps.post.id === key }).length : 0)
const commentList: { [commentId: string]: Comment } = comment.postComments[ownProps.post.id!]
return {
commentList,
avatar: state.user.info && state.user.info[ownProps.post.ownerUserId!] ? state.user.info[ownProps.post.ownerUserId!].avatar || '' : '',
@@ -425,4 +537,4 @@ const mapStateToProps = (state: any, ownProps: IPostComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(PostComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(PostComponent as any) as any)

View File

@@ -70,4 +70,9 @@ export interface IPostWriteComponentProps {
* @memberof IPostWriteComponentProps
*/
update?: (post: Post, callback: Function) => any
/**
* Styles
*/
classes?: any
}

View File

@@ -28,6 +28,11 @@ export interface IPostWriteComponentState {
/**
* If it's true share will be disabled on post
*/
disableSharing: boolean
disableSharing: boolean,
/**
* Whether menu is open
*/
menuOpen: boolean
}

View File

@@ -2,19 +2,37 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { List, ListItem } from 'material-ui/List'
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import List, {
ListItem,
ListItemAvatar,
ListItemIcon,
ListItemSecondaryAction,
ListItemText
} from 'material-ui/List'
import Paper from 'material-ui/Paper'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/RaisedButton'
import { grey400, grey800, darkBlack, lightBlack } from 'material-ui/styles/colors'
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle
} from 'material-ui/Dialog'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
import { grey } from 'material-ui/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 Tooltip from 'material-ui/Tooltip'
import { MenuList, MenuItem } from 'material-ui/Menu'
import SvgRemoveImage from 'material-ui-icons/removeCircle'
import SvgCamera from 'material-ui-icons/photoCamera'
import MoreVertIcon from 'material-ui-icons/moreVert'
import { withStyles } from 'material-ui/styles'
import { Manager, Target, Popper } from 'react-popper'
import Grow from 'material-ui/transitions/Grow'
import ClickAwayListener from 'material-ui/utils/ClickAwayListener'
import classNames from 'classnames'
// - Import app components
import ImageGallery from 'components/imageGallery'
@@ -31,37 +49,72 @@ import { IPostWriteComponentProps } from './IPostWriteComponentProps'
import { IPostWriteComponentState } from './IPostWriteComponentState'
import { Post } from 'core/domain/posts'
const styles = (theme: any) => ({
backdrop: {
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: '-1',
position: 'fixed',
willChange: 'opacity',
backgroundColor: 'rgba(251, 249, 249, 0.5)',
WebkitTapHighlightColor: 'transparent'
},
root: {
padding: 0,
paddingTop: 0
},
dialogRoot: {
paddingTop: 0
},
popperOpen: {
zIndex: 10
},
popperClose: {
pointerEvents: 'none',
zIndex: 0
},
author: {
paddingRight: 70
}
})
// - Create PostWrite component class
export class PostWriteComponent extends Component<IPostWriteComponentProps,IPostWriteComponentState> {
export class PostWriteComponent extends Component<IPostWriteComponentProps, IPostWriteComponentState> {
/**
* Component constructor
* @param {object} props is an object properties of component
*/
constructor (props: IPostWriteComponentProps) {
constructor(props: IPostWriteComponentProps) {
super(props)
const {postModel} = props
const { postModel } = props
// Default state
this.state = {
/**
* Post text
*/
postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '' ) : '',
postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '') : '',
/**
* The URL image of the post
*/
image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '' ) : '',
image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '') : '',
/**
* The path identifier of image on the server
*/
imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '' ) : '',
imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '') : '',
/**
* If it's true gallery will be open
*/
galleryOpen: false,
/**
* Whether menu is open
*/
menuOpen: false,
/**
* If it's true post button will be disabled
*/
@@ -143,13 +196,13 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
const {
id,
ownerAvatar,
ownerDisplayName,
edit,
onRequestClose,
post,
update,
postModel
ownerAvatar,
ownerDisplayName,
edit,
onRequestClose,
post,
update,
postModel
} = this.props
if (image === '' && postText.trim() === '') {
this.setState({
@@ -217,7 +270,8 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
* @param {event} evt is an event passed by change post text callback funciton
* @param {string} data is the post content which user writes
*/
handleOnChange = (event: any, data: any) => {
handleOnChange = (event: any) => {
const data = event.target.value
this.setState({ postText: data })
if (data.length === 0 || data.trim() === '' || (this.props.edit && data.trim() === this.props.text)) {
this.setState({
@@ -251,37 +305,55 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
})
}
componentWillReceiveProps (nextProps: IPostWriteComponentProps) {
/**
* Handle open more menu
*/
handleOpenMenu = () => {
this.setState({
menuOpen: true
})
}
/**
* Handle close more menu
*/
handleCloseMenu = () => {
this.setState({
menuOpen: false
})
}
componentWillReceiveProps(nextProps: IPostWriteComponentProps) {
if (!nextProps.open) {
const {postModel} = this.props
const { postModel } = this.props
this.setState({
/**
* Post text
*/
postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '' ) : '',
/**
* The URL image of the post
*/
image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '' ) : '',
/**
* The path identifier of image on the server
*/
imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '' ) : '',
/**
* If it's true gallery will be open
*/
/**
* Post text
*/
postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '') : '',
/**
* The URL image of the post
*/
image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '') : '',
/**
* The path identifier of image on the server
*/
imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '') : '',
/**
* If it's true gallery will be open
*/
galleryOpen: false,
/**
* If it's true post button will be disabled
*/
/**
* If it's true post button will be disabled
*/
disabledPost: true,
/**
* If it's true comment will be disabled on post
*/
/**
* If it's true comment will be disabled on post
*/
disableComments: this.props.edit && postModel ? postModel.disableComments! : false,
/**
* If it's true share will be disabled on post
*/
/**
* If it's true share will be disabled on post
*/
disableSharing: this.props.edit && postModel ? postModel.disableSharing! : false
})
@@ -292,28 +364,44 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render () {
render() {
const iconButtonElement = (
<IconButton
touch={true}
tooltip='more'
tooltipPosition='bottom-left'
>
<MoreVertIcon color={grey400} />
</IconButton>
)
const { classes } = this.props
const { menuOpen } = this.state
const rightIconMenu = (
<IconMenu iconButtonElement={iconButtonElement}>
<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>
<Manager>
<Target>
<Tooltip id='tooltip-icon' title='more' placement='bottom-start'>
<IconButton
onClick={this.handleOpenMenu}
>
<MoreVertIcon />
</IconButton>
</Tooltip>
</Target>
<Popper
placement='bottom-start'
eventsEnabled={menuOpen}
className={classNames({ [classes.popperClose]: !menuOpen }, { [classes.popperOpen]: menuOpen })}
>
<ClickAwayListener onClickAway={this.handleCloseMenu}>
<Grow in={menuOpen} style={{ transformOrigin: '0 0 0' }}>
<Paper>
<MenuList role='menu'>
<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>
</MenuList>
</Paper>
</Grow>
</ClickAwayListener>
</Popper>
</Manager>
)
let postAvatar = <UserAvatarComponent fullName={this.props.ownerDisplayName!} fileName={this.props.ownerAvatar!} style={{ top: '8px' }} size={40} />
let postAvatar = <UserAvatarComponent fullName={this.props.ownerDisplayName!} fileName={this.props.ownerAvatar!} size={36} />
let author = (
<div>
<div className={classes.author}>
<span style={{
fontSize: '14px',
paddingRight: '10px',
@@ -321,7 +409,6 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
color: 'rgba(0,0,0,0.87)',
textOverflow: 'ellipsis',
overflow: 'hidden',
paddingLeft: '50px',
lineHeight: '25px'
}}>{this.props.ownerDisplayName}</span><span style={{
fontWeight: 100,
@@ -330,32 +417,32 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
</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}
/>
]
/**
* Provide post image
*/
const loadImage = (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 style={{ color: 'rgba(0, 0, 0, 0.53)' }} />
</span>
const galleryActions = [
<FlatButton
label='Cancel'
primary={true}
keyboardFocused={false}
onTouchTap={this.handleCloseGallery}
style={{ color: grey800 }}
/>
]
<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>) : ''
const styles = {
dialog: {
@@ -369,88 +456,94 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
<div style={this.props.style}>
{this.props.children}
<Dialog
BackdropProps={{className: classes.backdrop} as any}
key={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)' }}
onClose={this.props.onRequestClose}
>
<DialogContent
className={classes.root}
style={{paddingTop: 0}}
<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}
rows={2}
hintStyle={{ fontWeight: 200, fontSize: '14px' }}
textareaStyle={{ fontWeight: 200, fontSize: '14px' }}
style={{ margin: '0 16px', flexShrink: 0, width: 'initial', flexGrow: 1 }}
<Card elevation={0}>
<CardHeader
title={author}
avatar={postAvatar}
action={rightIconMenu}
>
</CardHeader>
<CardContent>
<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
autoFocus
value={this.state.postText}
onChange={this.handleOnChange}
placeholder='What is new with you?'
multiline
rows={2}
rowsMax={4}
style={{ fontWeight: 200, fontSize: '14px', 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>
{loadImage}
</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 style={{ color: 'grey' }} />
</span>
</div>
</div>
</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>
</CardContent>
</Card>
</DialogContent>
<DialogActions>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
onClick={this.props.onRequestClose}
style={{ color: grey[800] }}
>
Cancel
</Button>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
onClick={this.handlePost}
disabled={this.state.disabledPost}
>
{this.props.edit ? 'UPDATE' : 'POST'}
</Button>
</DialogActions>
</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}
onClose={this.handleCloseGallery}
>
<ImageGallery set={this.onRequestSetImage} close={this.handleCloseGallery} />
<DialogContent>
<ImageGallery set={this.onRequestSetImage} close={this.handleCloseGallery} />
</DialogContent>
<DialogActions>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
onClick={this.handleCloseGallery}
style={{ color: grey[800] }}
>
Cancel
</Button>
</DialogActions>
</Dialog>
</div>
@@ -486,4 +579,4 @@ const mapStateToProps = (state: any, ownProps: IPostWriteComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(PostWriteComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(PostWriteComponent as any) as any)

View File

@@ -3,8 +3,8 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/RaisedButton'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
// - Import app components
import ProfileHeader from 'components/profileHeader'

View File

@@ -2,8 +2,8 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { grey400 } from 'material-ui/styles/colors'
import SvgClose from 'material-ui/svg-icons/navigation/close'
import FlatButton from 'material-ui/FlatButton'
import SvgClose from 'material-ui-icons/close'
import Button from 'material-ui/Button'
import Divider from 'material-ui/Divider'
import { IDialogTitleComponentProps } from './IDialogTitleComponentProps'
import { IDialogTitleComponentState } from './IDialogTitleComponentState'
@@ -82,7 +82,7 @@ export default class DialogTitleComponent extends Component<IDialogTitleComponen
{title || ''}
</div>
{ buttonLabel ? (<div style={{ marginTop: '-9px' }}>
<FlatButton label={buttonLabel || ''} primary={true} disabled={disabledButton ? disabledButton : false} onClick={onClickButton || (x => x)} />
<Button label={buttonLabel || ''} color='primary' disabled={disabledButton ? disabledButton : false} onClick={onClickButton || (x => x)} />
</div>) : ''}
</div>
<Divider />

View File

@@ -2,13 +2,12 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
import { grey } from 'material-ui/colors'
import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
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 MoreVertIcon from 'material-ui-icons/moreVert'
import { MenuList, MenuItem } from 'material-ui/Menu'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
import EventListener, { withOptions } from 'react-event-listener'
import { Parallax, Background } from 'react-parallax'
@@ -152,19 +151,18 @@ export class ProfileHeaderComponent extends Component<IProfileHeaderComponentPro
}
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 style={this.state.isSmall ? styles.iconButtonSmall : styles.iconButton}>
<MoreVertIcon style={{...(this.state.isSmall ? styles.iconButtonSmall : styles.iconButton), color: grey[400]}} viewBox='10 0 24 24' />
</IconButton>
)
const RightIconMenu = () => (
<IconMenu iconButtonElement={iconButtonElement}>
<div>
{iconButtonElement}
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem>
<MenuItem style={{ fontSize: '14px' }}>Edit</MenuItem>
<MenuItem style={{ fontSize: '14px' }}>Delete</MenuItem>
</IconMenu>
</div>
)
const {isAuthedUser} = this.props
@@ -200,7 +198,11 @@ export class ProfileHeaderComponent extends Component<IProfileHeaderComponentPro
</div>
</div>
<div className='right'>
{isAuthedUser ? (<div style={this.state.isSmall ? styles.editButtonSmall : styles.editButton}><RaisedButton label='EDIT PROFILE' onClick={this.props.openEditor} /></div>) : ''}
{isAuthedUser ? (<div style={this.state.isSmall ? styles.editButtonSmall : styles.editButton}>
<Button raised onClick={this.props.openEditor}>
EDIT PROFILE
</Button>
</div>) : ''}
</div>
</div>
{isAuthedUser ? (<EditProfile

View File

@@ -13,4 +13,9 @@ export interface IResetPasswordComponentProps {
* @memberof IResetPasswordComponentProps
*/
loginPage?: () => void
/**
* Styles
*/
classes?: any
}

View File

@@ -5,15 +5,27 @@ import { NavLink, withRouter } from 'react-router-dom'
import { push } from 'react-router-redux'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import FlatButton from 'material-ui/FlatButton'
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
import RaisedButton from 'material-ui/Button'
import Button from 'material-ui/Button'
import { withStyles } from 'material-ui/styles'
import Typography from 'material-ui/Typography'
// - Import actions
import * as authorizeActions from 'actions/authorizeActions'
import { IResetPasswordComponentProps } from './IResetPasswordComponentProps'
import { IResetPasswordComponentState } from './IResetPasswordComponentState'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
},
caption: {
marginTop: 30
}
})
/**
* Create component class
*
@@ -77,6 +89,8 @@ export class ResetPasswordComponent extends Component<IResetPasswordComponentPro
*/
render () {
const {classes} = this.props
const paperStyle = {
minHeight: 370,
width: 450,
@@ -98,7 +112,7 @@ export class ResetPasswordComponent extends Component<IResetPasswordComponentPro
}}>Green</h1>
<div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} >
<Paper style={paperStyle} elevation={1}>
<div style={{ padding: '48px 40px 36px' }}>
<div style={{
paddingLeft: '40px',
@@ -116,24 +130,29 @@ export class ResetPasswordComponent extends Component<IResetPasswordComponentPro
</div>
<TextField
className={classes.textField}
autoFocus
onChange={this.handleInputChange}
errorText={this.state.emailInputError}
helperText={this.state.emailInputError}
error={this.state.emailInputError.trim() !== ''}
name='emailInput'
floatingLabelStyle={{ fontSize: '15px' }}
floatingLabelText='Email'
label='Email'
type='email'
/><br />
<br />
<br />
<div className='settings__button-box'>
<div>
<FlatButton label='Back' onClick={this.props.loginPage} />
<Button onClick={this.props.loginPage}> Back </Button>
</div>
<div>
<RaisedButton label='Reset password' primary={true} onClick={this.handleForm} />
<Button raised color='primary' onClick={this.handleForm}> Reset password </Button>
</div>
</div>
<Typography className={classes.caption} type='caption' component='p'>
Please put your email and click on "RESET PASSWORD". If not click on "BACK".
We will send you an email contains the link of the reset password.
</Typography>
</div>
</Paper>
</div>
@@ -170,4 +189,4 @@ const mapStateToProps = (state: any, ownProps: IResetPasswordComponentProps) =>
}
// - Connect component to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ResetPasswordComponent as any))
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ResetPasswordComponent as any) as any))

View File

@@ -16,15 +16,15 @@ export interface ISendFeedbackComponentProps {
/**
* Hide feedback form
*/
hideFeedback: () => any
hideFeedback?: () => any
/**
* The server request of send feedback
*/
sendFeedbackRequest: ServerRequestModel
sendFeedbackRequest?: ServerRequestModel
/**
* Current user profile
*/
currentUser: Profile
currentUser?: Profile
}

View File

@@ -5,10 +5,11 @@ import { connect } from 'react-redux'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import IconButton from 'material-ui/IconButton'
import SvgHappy from 'material-ui/svg-icons/image/tag-faces'
import SvgSad from 'material-ui/svg-icons/action/face'
import SvgClose from 'material-ui/svg-icons/content/clear'
import RefreshIndicator from 'material-ui/RefreshIndicator'
import SvgHappy from 'material-ui-icons/tagFaces'
import SvgSad from 'material-ui-icons/face'
import SvgClose from 'material-ui-icons/clear'
import { CircularProgress } from 'material-ui/Progress'
import Tooltip from 'material-ui/Tooltip'
// - Import app components
@@ -37,7 +38,7 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
* Component constructor
* @param {object} props is an object properties of component
*/
constructor(props: ISendFeedbackComponentProps) {
constructor (props: ISendFeedbackComponentProps) {
super(props)
// Defaul state
@@ -75,44 +76,47 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state
return (
<div>
<div className='main-box'>
<TextField
hintText='What do you feel?'
multiLine={true}
placeholder='What do you feel?'
multiline
onChange={this.handleFeedText}
rows={2}
rowsMax={4}
/><br />
autoFocus
fullWidth
/>
<br />
<div className='buttons'>
<Tooltip title='Sad' placement='bottom-start'>
<IconButton
tooltip='sad'
iconClassName='flaticon-sad-2 icon__svg'
tooltipPosition='bottom-left'
className='flaticon-sad-2 icon__svg'
onClick={() => this.handleSendFeed(FeedType.Sad)}
>
</IconButton>
</Tooltip>
<Tooltip title='Acceptable' placement='bottom-start'>
<IconButton
tooltip='acceptable'
iconClassName='flaticon-neutral icon__svg'
tooltipPosition='bottom-left'
className='flaticon-neutral icon__svg'
onClick={() => this.handleSendFeed(FeedType.Acceptable)}
>
</IconButton>
</Tooltip>
<Tooltip title='Happy' placement='bottom-start'>
<IconButton
tooltip='happy'
iconClassName='flaticon-happy-2 icon__svg'
tooltipPosition='bottom-left'
className='flaticon-happy-2 icon__svg'
onClick={() => this.handleSendFeed(FeedType.Happy)}
>
</IconButton>
</Tooltip>
<Tooltip title='Awesome' placement='bottom-start'>
<IconButton
tooltip='awesome'
iconClassName='flaticon-happy icon__svg'
tooltipPosition='bottom-left'
className='flaticon-happy icon__svg'
onClick={() => this.handleSendFeed(FeedType.Awesome)}
>
</IconButton>
</Tooltip>
</div>
</div >)
}
@@ -123,13 +127,14 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
Your feedback is sending!
</p>
<div className='icon'>
<RefreshIndicator
size={50}
left={70}
top={0}
status='loading'
/>
<CircularProgress
color='secondary'
size={50}
mode='determinate'
value={25}
min={0}
max={50}
/>
</div>
</div>)
}
@@ -169,21 +174,21 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render() {
render () {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state
return (
<div className='sendFeedback__content animate__up' style={{ display: (sendFeedbackStatus ? 'block' : 'none') }}>
<div className='sendFeedback__content animate__up'>
<Paper className='paper' >
<div className='close'>
<Tooltip title='Cancel' placement='bottom-start'>
<IconButton
tooltip='cancel'
tooltipPosition='bottom-left'
onClick={() => hideFeedback()}
onClick={() => hideFeedback!()}
>
<SvgClose />
</IconButton>
</Tooltip >
</div>
{this.getFeedbackForm()}

View File

@@ -5,12 +5,17 @@ export interface ISettingComponentProps {
*
* @memberof ISettingComponentProps
*/
login: (email: string, password: string) => any
login?: (email: string, password: string) => any
/**
* Redirect to home page
*
* @memberof ISettingComponentProps
*/
homePage: () => void
homePage?: () => void
/**
* Styles
*/
classes?: any
}

View File

@@ -5,14 +5,22 @@ import { NavLink, withRouter } from 'react-router-dom'
import { push } from 'react-router-redux'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/Button'
import Button from 'material-ui/Button'
import { withStyles } from 'material-ui/styles'
// - Import actions
import * as authorizeActions from 'actions/authorizeActions'
import { ISettingComponentProps } from './ISettingComponentProps'
import { ISettingComponentState } from './ISettingComponentState'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
}
})
/**
* Create component class
*
@@ -98,7 +106,7 @@ export class SettingComponent extends Component<ISettingComponentProps,ISettingC
}
if (!error) {
this.props.login(
this.props.login!(
this.state.passwordInput,
this.state.confirmInput
)
@@ -112,6 +120,7 @@ export class SettingComponent extends Component<ISettingComponentProps,ISettingC
*/
render () {
const {classes} = this.props
const paperStyle = {
minHeight: 370,
width: 450,
@@ -133,7 +142,7 @@ export class SettingComponent extends Component<ISettingComponentProps,ISettingC
}}>Green</h1>
<div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} >
<Paper style={paperStyle} elevation={1} >
<div style={{ padding: '48px 40px 36px' }}>
<div style={{
paddingLeft: '40px',
@@ -142,38 +151,41 @@ export class SettingComponent extends Component<ISettingComponentProps,ISettingC
<h2 style={{
textAlign: 'left',
paddingTop: '16px',
paddingTop: '10px',
fontSize: '24px',
fontWeight: 400,
lineHeight: '32px',
margin: 0
}}>Change Password</h2>
}} className='zoomOutLCorner animated'>Change Password</h2>
</div>
<TextField
autoFocus
className={classes.textField}
onChange={this.handleInputChange}
errorText={this.state.passwordInputError}
helperText={this.state.passwordInputError}
name='passwordInput'
floatingLabelStyle={{ fontSize: '15px' }}
floatingLabelText='New password'
label='New password'
type='password'
error={this.state.passwordInputError.trim() !== ''}
/><br />
<TextField
className={classes.textField}
onChange={this.handleInputChange}
errorText={this.state.confirmInputError}
helperText={this.state.confirmInputError}
name='confirmInput'
floatingLabelStyle={{ fontSize: '15px' }}
floatingLabelText='Confirm password'
label='Confirm password'
type='password'
error={this.state.confirmInputError.trim() !== ''}
/><br />
<br />
<br />
<div className='settings__button-box'>
<div>
<FlatButton label='Home' onClick={this.props.homePage} />
<Button onClick={this.props.homePage} > Home </Button>
</div>
<div>
<RaisedButton label='Change password' primary={true} onClick={this.handleForm} />
<Button raised color='primary' onClick={this.handleForm}> Change password </Button>
</div>
</div>
@@ -216,4 +228,4 @@ const mapStateToProps = (state: any, ownProps: ISettingComponentProps) => {
}
// - Connect component to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(SettingComponent as any))
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(SettingComponent as any) as any))

View File

@@ -141,7 +141,7 @@ export class SidebarComponent extends Component<ISidebarComponentProps,ISidebarC
this.props.status(false)
}
this.props.overlay((sizeCondition(width)) ? false : true)
this.props.overlay(!(sizeCondition(width)) && this.state.open ? true : false)
}
/**
@@ -174,12 +174,12 @@ export class SidebarComponent extends Component<ISidebarComponentProps,ISidebarC
}else {
this.setState({
overlayOpen: true,
overlay: true
overlay: this.state.open
})
}
} else {
this.setState({ sidebarClass: 'sidebar sidebar__over', overlay: true })
this.props.overlay(true)
this.setState({ sidebarClass: 'sidebar sidebar__over', overlay: this.state.open })
this.props.overlay(this.state.open!)
}
}

View File

@@ -30,7 +30,7 @@ export default class SidebarMainComponent extends Component<ISidebarMainComponen
render () {
return (
<div className='home__main' style={this.props.cstyle} >
<div style={{height: '64px', width: '100%'}}></div>
<div style={{height: '80px', width: '100%'}}></div>
{this.props.children}
</div>
)

View File

@@ -5,19 +5,24 @@ export interface ISignupComponentProps {
*
* @memberof ISignupComponentState
*/
showError: (message: string) => any
showError?: (message: string) => any
/**
* Register user
*
* @memberof ISignupComponentState
*/
register: (data: any) => any
register?: (data: any) => any
/**
* Login
*
* @memberof ISignupComponentState
*/
loginPage: () => any
loginPage?: () => any
/**
* Styles
*/
classes?: any
}

View File

@@ -5,8 +5,9 @@ import { push } from 'react-router-redux'
import { NavLink, withRouter } from 'react-router-dom'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/Button'
import Button from 'material-ui/Button'
import { withStyles } from 'material-ui/styles'
// - Import actions
import * as authorizeActions from 'actions/authorizeActions'
@@ -19,6 +20,14 @@ import { ISignupComponentProps } from './ISignupComponentProps'
import { ISignupComponentState } from './ISignupComponentState'
import { UserRegisterModel } from 'models/users/userRegisterModel'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
}
})
// - Create Signup component class
export class SignupComponent extends Component<ISignupComponentProps,ISignupComponentState> {
@@ -38,7 +47,6 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
passwordInputError: '',
confirmInput: '',
confirmInputError: ''
}
// Binding function to `this`
this.handleForm = this.handleForm.bind(this)
@@ -145,7 +153,7 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
}
if (!error) {
register({
register!({
email: emailInput,
password: passwordInput,
fullName: fullNameInput
@@ -159,6 +167,8 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
* @return {react element} return the DOM which rendered by component
*/
render () {
const {classes} = this.props
const paperStyle = {
minHeight: 500,
width: 450,
@@ -182,7 +192,7 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
}}>Green</h1>
<div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} >
<Paper style={paperStyle} elevation={1} >
<div style={{padding: '48px 40px 36px'}}>
<div style={{paddingLeft: '40px',
paddingRight: '40px'}}>
@@ -197,44 +207,49 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
</div>
<TextField
className={classes.textField}
autoFocus
onChange={this.handleInputChange}
errorText={this.state.fullNameInputError}
helperText={this.state.fullNameInputError}
error={this.state.fullNameInputError.trim() !== ''}
name='fullNameInput'
floatingLabelStyle={{fontSize: '15px'}}
floatingLabelText='Full Name'
label='Full Name'
type='text'
/><br />
<TextField
className={classes.textField}
onChange={this.handleInputChange}
errorText={this.state.emailInputError}
helperText={this.state.emailInputError}
error={this.state.emailInputError.trim() !== ''}
name='emailInput'
floatingLabelStyle={{fontSize: '15px'}}
floatingLabelText='Email'
label='Email'
type='email'
/><br />
<TextField
className={classes.textField}
onChange={this.handleInputChange}
errorText={this.state.passwordInputError }
helperText={this.state.passwordInputError }
error={this.state.passwordInputError.trim() !== ''}
name='passwordInput'
floatingLabelStyle={{fontSize: '15px'}}
floatingLabelText='Password'
label='Password'
type='password'
/><br />
<TextField
className={classes.textField}
onChange={this.handleInputChange}
errorText={this.state.confirmInputError }
helperText={this.state.confirmInputError }
error={this.state.confirmInputError.trim() !== ''}
name='confirmInput'
floatingLabelStyle={{fontSize: '15px'}}
floatingLabelText='Confirm Password'
label='Confirm Password'
type='password'
/><br />
<br />
<div className='signup__button-box'>
<div>
<FlatButton label='Login' onClick={this.props.loginPage} />
<Button onClick={this.props.loginPage}> Login </Button>
</div>
<div>
<RaisedButton label='Create' primary={true} onClick={this.handleForm}/>
<Button raised color='primary' onClick={this.handleForm}> Create </Button>
</div>
</div>
@@ -281,4 +296,4 @@ const mapStateToProps = (state: any,ownProps: ISignupComponentProps) => {
}
// - Connect component to redux store
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(SignupComponent as any) as any)
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(withStyles(styles)(SignupComponent as any) as any))

View File

@@ -3,13 +3,11 @@ import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card'
import FlatButton from 'material-ui/FlatButton'
import { grey400, grey800, darkBlack, lightBlack,tealA400 } from 'material-ui/styles/colors'
import CircularProgress from 'material-ui/CircularProgress'
import SvgCamera from 'material-ui/svg-icons/image/photo-camera'
import Button from 'material-ui/Button'
import { grey,teal } from 'material-ui/colors'
import SvgCamera from 'material-ui-icons/photoCamera'
import Paper from 'material-ui/Paper'
import { List, ListItem } from 'material-ui/List'
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List'
import InfiniteScroll from 'react-infinite-scroller'
// - Import app components
@@ -50,11 +48,10 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
styles = {
postWritePrimaryText : {
color: grey400,
color: grey[400],
cursor: 'text'
},
postWtireItem : {
padding: '7px 0px',
fontWeight: '200'
}
}
@@ -220,15 +217,18 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
<div className='grid-cell animate-top' style= {{maxWidth: '530px', minWidth: '280px'}}>
{displayWriting && !tag
? (<PostWriteComponent open={this.state.openPostWrite} onRequestClose={this.handleClosePostWrite} edit={false} >
<Paper zDepth={2} style={{ height: '68px', width: '100%' }}>
<Paper elevation={2}>
<ListItem
primaryText={<span style={this.styles.postWritePrimaryText as any}> What's new with you? </span>}
leftAvatar={<UserAvatarComponent fullName={this.props.fullName!} fileName={this.props.avatar!} size={36} />}
rightIcon={<SvgCamera />}
<ListItem button
style={this.styles.postWtireItem as any}
onTouchTap={this.handleOpenPostWrite}
/>
onClick={this.handleOpenPostWrite}
>
<UserAvatarComponent fullName={this.props.fullName!} fileName={this.props.avatar!} size={36} />
<ListItemText inset primary={<span style={this.styles.postWritePrimaryText as any}> What's new with you? </span>} />
<ListItemIcon>
<SvgCamera />
</ListItemIcon>
</ListItem>
</Paper>
<div style={{ height: '16px' }}></div>

View File

@@ -19,5 +19,5 @@ export interface IUserAvatarComponentProps {
/**
* Trigger on touch tap
*/
onTouchTap?: Function
onClick?: (event: any) => anys
}

View File

@@ -40,7 +40,7 @@ export class UserAvatarComponent extends Component<IUserAvatarComponentProps,IUs
/**
* Trigger on touch tap
*/
onTouchTap: PropTypes.func
onClick: PropTypes.func
}
@@ -64,13 +64,13 @@ export class UserAvatarComponent extends Component<IUserAvatarComponentProps,IUs
* @return {react element} return the DOM which rendered by component
*/
render () {
let { fileName, fullName, style, size, onTouchTap } = this.props
let { fileName, fullName, style, size, onClick } = this.props
return (
<div style={{display: 'inherit'}}>
{(fileName && fileName !== '' && fileName !== 'noImage' )
? ( <Avatar backgroundColor='#ffffff' src={fileName} size={size || 36} style={style} onTouchTap={onTouchTap} />)
: (<Avatar backgroundColor='#00bcd4' size={size || 36} style={style} onTouchTap={onTouchTap}>{fullName ? fullName.slice(0, 1) : ''}</Avatar>) }
? ( <Avatar src={fileName ? fileName : ' '} style={{...style, backgroundColor: '#ffffff', width: size || 36, height: size || 36}} onClick={onClick} />)
: (<Avatar style={{...style, backgroundColor: '#00bcd4', width: size || 36, height: size || 36}} onClick={onClick}>{fullName ? fullName.slice(0, 1) : ''}</Avatar>) }
</div>
)
}

View File

@@ -155,4 +155,9 @@ export interface IUserBoxComponentProps {
* Whether the select circles box for referer user is open
*/
isSelecteCirclesOpen?: boolean
/**
* Styles
*/
classes?: any
}

View File

@@ -5,17 +5,25 @@ import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { push } from 'react-router-redux'
import Paper from 'material-ui/Paper'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/RaisedButton'
import Popover, { PopoverAnimationVertical } from 'material-ui/Popover'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
import Menu, { MenuItem } from 'material-ui/Menu'
import Checkbox from 'material-ui/Checkbox'
import TextField from 'material-ui/TextField'
import { Dialog } from 'material-ui'
import SvgAdd from 'material-ui/svg-icons/content/add'
import Tooltip from 'material-ui/Tooltip'
import { withStyles } from 'material-ui/styles'
import List, { ListItem, ListItemSecondaryAction, ListItemText } from 'material-ui/List'
import Divider from 'material-ui/Divider'
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
withMobileDialog
} from 'material-ui/Dialog'
import SvgAdd from 'material-ui-icons/add'
import IconButton from 'material-ui/IconButton'
import { grey400, grey800, darkBlack, lightBlack } from 'material-ui/styles/colors'
import { grey } from 'material-ui/colors'
// - Import app components
import UserAvatar from 'components/userAvatar'
@@ -34,6 +42,24 @@ import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { ServerRequestModel } from 'models/server'
const styles = (theme: any) => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper
},
dialogContent: {
paddingTop: '5px',
padding: '0px 5px 5px 5px'
},
circleName: {
fontSize: '1rem'
},
space: {
height: 20
}
})
/**
* Create component class
*/
@@ -81,7 +107,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/
constructor (props: IUserBoxComponentProps) {
super(props)
const { userBelongCircles, circles,userId } = this.props
const { userBelongCircles, circles, userId } = this.props
// Defaul state
this.state = {
/**
@@ -114,9 +140,9 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Handle follow user
*/
handleDoneAddCircle = () => {
const { userId, user , addUserToCircle, selectedCircles, deleteFollowingUser} = this.props
const { userId, user, addUserToCircle, selectedCircles, deleteFollowingUser } = this.props
const { avatar, fullName } = user
const {disabledDoneCircles} = this.state
const { disabledDoneCircles } = this.state
if (!disabledDoneCircles) {
if (selectedCircles!.length > 0) {
addUserToCircle!(selectedCircles!, { avatar, userId, fullName })
@@ -132,7 +158,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
onFollowUser = (event: any) => {
// This prevents ghost click
event.preventDefault()
const {isFollowed, followUser, followingCircleId, userId, user, followRequest } = this.props
const { isFollowed, followUser, followingCircleId, userId, user, followRequest } = this.props
if (followRequest && followRequest.status === ServerRequestStatusType.Sent) {
return
@@ -149,7 +175,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Handle request close for add circle box
*/
onRequestCloseAddCircle = () => {
const {setSelectedCircles, userId, userBelongCircles, closeSelectCircles} = this.props
const { setSelectedCircles, userId, userBelongCircles, closeSelectCircles } = this.props
setSelectedCircles!(userId, userBelongCircles!)
closeSelectCircles!(userId)
this.setState({
@@ -164,7 +190,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Handle request open for add circle box
*/
onRequestOpenAddCircle = () => {
const { openSelectCircles, userId} = this.props
const { openSelectCircles, userId } = this.props
openSelectCircles!(userId)
}
@@ -218,7 +244,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Create a circle list of user which belong to
*/
circleList = () => {
let { circles, userId, userBelongCircles, selectedCircles } = this.props
let { circles, userId, userBelongCircles, selectedCircles, classes } = this.props
if (circles) {
@@ -226,19 +252,16 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
let isBelong = selectedCircles ? selectedCircles!.indexOf(circleId) > -1 : false
// Create checkbox for selected/unselected circle
return <Checkbox
key={`${circleId}-${userId}`}
style={{ padding: '10px' }}
label={circles![circleId].name}
labelStyle={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%'
}}
onCheck={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId)}
checked={isBelong}
/>
return (
<ListItem key={`${circleId}-${userId}`} dense className={classes.listItem}>
<ListItemText className={classes.circleName} primary={circles![circleId].name} />
<ListItemSecondaryAction>
<Checkbox
onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId)}
checked={isBelong}
/>
</ListItemSecondaryAction>
</ListItem>)
})
return parsedDate
@@ -271,28 +294,11 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* @return {react element} return the DOM which rendered by component
*/
render () {
const {disabledDoneCircles} = this.state
const { isFollowed, followRequest, userId, isSelecteCirclesOpen, addToCircleRequest, deleteFollowingUserRequest } = this.props
const writeActions = [
<FlatButton
label='Cancel'
primary={true}
keyboardFocused={false}
onTouchTap={this.onRequestCloseAddCircle}
style={{ color: grey800 }}
/>,
<FlatButton
label='Done'
primary={true}
keyboardFocused={false}
disabled={disabledDoneCircles || (addToCircleRequest ? addToCircleRequest!.status === ServerRequestStatusType.Sent : false)}
onTouchTap={this.handleDoneAddCircle}
/>
]
const { disabledDoneCircles } = this.state
const { isFollowed, followRequest, userId, isSelecteCirclesOpen, addToCircleRequest, deleteFollowingUserRequest, classes } = this.props
return (
<Paper key={userId} style={this.styles.paper} zDepth={1} className='grid-cell'>
<Paper key={userId} style={this.styles.paper} elevation={1} className='grid-cell'>
<div style={{
display: 'flex',
flexDirection: 'column',
@@ -316,54 +322,69 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
</div>
</div>
<div style={this.styles.followButton as any}>
<FlatButton
label={!isFollowed ? 'Follow'
: (this.props.belongCirclesCount! > 1 ? `${this.props.belongCirclesCount} Circles` : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : 'Follow'))}
primary={true}
onTouchTap={this.onFollowUser}
<Button
color='primary'
onClick={this.onFollowUser}
disabled={
(followRequest ? followRequest.status === ServerRequestStatusType.Sent : false) ||
(deleteFollowingUserRequest ? deleteFollowingUserRequest.status === ServerRequestStatusType.Sent : false)
}
/>
>
{!isFollowed ? 'Follow'
: (this.props.belongCirclesCount! > 1 ? `${this.props.belongCirclesCount} Circles` : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : 'Follow'))}
</Button>
</div>
</div>
<Dialog
key={this.props.userId || 0}
actions={writeActions}
modal={false}
open={isSelecteCirclesOpen === true}
contentStyle={this.styles.dialog}
onRequestClose={this.onRequestCloseAddCircle}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
bodyStyle={{ padding: 0 }}
autoDetectWindowHeight={false}
actionsContainerStyle={{ borderTop: '1px solid rgb(224, 224, 224)' }}
onClose={this.onRequestCloseAddCircle}
>
<div style={{
position: 'relative',
display: 'block',
maxHeight: '220px'
}}>
<div style={{ overflowY: 'auto', height: '100%' }}>
<DialogContent className={classes.dialogContent}>
<List>
{this.circleList()}
</div>
</div>
<div style={{ padding: '10px' }}>
<TextField
hintText='Group name'
onChange={this.handleChangeName}
value={this.state.circleName}
/>
<div className='user-box__add-circle'>
<IconButton onClick={this.onCreateCircle} tooltip='Create circle' disabled={this.state.disabledCreateCircle}>
<SvgAdd />
</IconButton>
</div>
<br />
</div>
<div className={classes.space}></div>
<Divider />
<ListItem key={`'circleName'-${userId}`} dense className={classes.listItem}>
<ListItemText primary={
<TextField
autoFocus
placeholder='Group name'
onChange={this.handleChangeName}
value={this.state.circleName}
/>
} />
<ListItemSecondaryAction>
<IconButton onClick={this.onCreateCircle} disabled={this.state.disabledCreateCircle}>
<Tooltip title='Create circle'>
<SvgAdd />
</Tooltip>
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</DialogContent>
<DialogActions>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
onClick={this.onRequestCloseAddCircle}
style={{ color: grey[800] }}
>
Cancel
</Button>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
disabled={disabledDoneCircles || (addToCircleRequest ? addToCircleRequest!.status === ServerRequestStatusType.Sent : false)}
onClick={this.handleDoneAddCircle}
>
Done
</Button>
</DialogActions>
</Dialog>
</Paper>
)
@@ -407,7 +428,7 @@ const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => {
const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : []
const isFollowed = userBelongCircles.length > 0
const followingCircleId = circles ? Object.keys(circles)
.filter((circleId) => circles[circleId].isSystem && circles[circleId].name === `Following`)[0] : ''
.filter((circleId) => circles[circleId].isSystem && circles[circleId].name === `Following`)[0] : ''
const followRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)] : null
const addToCircleRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, ownProps.userId)] : null
const deleteFollowingUserRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, ownProps.userId)] : null
@@ -430,4 +451,4 @@ const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => {
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(UserBoxComponent as any)
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(UserBoxComponent as any) as any)

View File

@@ -2,13 +2,13 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { List } from 'material-ui/List'
import List from 'material-ui/List'
// - Import app components
import CircleComponent from 'components/circle'
import { IYourCirclesComponentProps } from './IYourCirclesComponentProps'
import { IYourCirclesComponentState } from './IYourCirclesComponentState'
import { Circle } from 'core/domain/circles';
import { Circle } from 'core/domain/circles'
// - Import API
@@ -45,7 +45,7 @@ export class YourCirclesComponent extends Component<IYourCirclesComponentProps,I
if (circles) {
Object.keys(circles).map((key, index) => {
parsedCircles.push(<CircleComponent key={key} circle={circles![key]} id={key} uid={uid!} />)
parsedCircles.push(<CircleComponent key={key} circle={circles![key]} id={key} uid={uid!} />)
})
}
return parsedCircles

View File

@@ -3,6 +3,7 @@ export enum ServerRequestType {
CircleFollowUser = 'CircleFollowUser',
CircleCreateTieUser = 'CircleCreateTieUser',
CircleDeleteFollowingUser = 'CircleDeleteFollowingUser',
CommonSendFeedback = 'CommonSendFeedback'
CommonSendFeedback = 'CommonSendFeedback',
CommentGetComments = 'CommentGetComments'
}

View File

@@ -1,6 +1,6 @@
export enum FeedType {
Awesome = 'Awesome',
Happy = 'Happey',
Happy = 'Happy',
Acceptable = 'Acceptable',
Sad = 'Sad',
Bug = 'Bug'

View File

@@ -121,7 +121,6 @@ export class GraphService implements IGraphService {
graphsRef = graphsRef.where('leftNode', '==', leftNode)
}
if (rightNode && rightNode != null) {
console.trace('getGraphsQuery', {collection, leftNode, edgeType, rightNode})
graphsRef = graphsRef.where('rightNode', '==', rightNode)
}

View File

@@ -3,11 +3,9 @@ import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import injectTapEventPlugin from 'react-tap-event-plugin'
import { cyan500 } from 'material-ui/styles/colors'
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import getMuiTheme from 'material-ui/styles/getMuiTheme'
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles'
import 'reflect-metadata'
import 'typeface-roboto'
import { Provider } from 'react-redux'
import store, { history } from 'store/configureStore'
@@ -21,31 +19,16 @@ import Master from 'components/master'
// tslint:disable-next-line:no-empty
store.subscribe(() => { })
// Needed for onTouchTap
// Needed for onClick
// http://stackoverflow.com/a/34015469/988941
try { injectTapEventPlugin() } catch (e) { }
// This replaces the textColor value on the palette
// and then update the keys for each component that depends on it.
// More on Colors: http://www.material-ui.com/#/customization/colors
const muiTheme = getMuiTheme({
const theme = createMuiTheme({
palette: {
primary1Color: 'rgb(199, 212, 0)'
},
// raisedButton: {
// primaryColor: 'rgb(199, 212, 0)' // Raised button background color
// },
// flatButton: {
// primaryTextColor: 'rgb(199, 212, 0)' // Flat button background color
// },
// tabs: {
// backgroundColor: 'rgb(199, 212, 0)' // Tabs backgound color
// },
toolbar: {
backgroundColor: '#6d9828' // Backgroung color of header in toolbar
primary: { main: '#00b1b3' },
secondary: { main: '#4d545d' }
}
})
// App css
import 'applicationStyles'
@@ -60,7 +43,7 @@ const render = (Component: any) => {
<AppContainer warnings={false}>
<Provider store={store}>
<ConnectedRouter history={history}>
<MuiThemeProvider muiTheme={muiTheme}>
<MuiThemeProvider theme={theme}>
<Component />
</MuiThemeProvider>
</ConnectedRouter>

View File

@@ -1,9 +1,9 @@
// - Import react components
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { grey400 } from 'material-ui/styles/colors'
import SvgClose from 'material-ui/svg-icons/navigation/close'
import FlatButton from 'material-ui/FlatButton'
import { grey } from 'material-ui/colors'
import SvgClose from 'material-ui-icons/close'
import Button from 'material-ui/Button'
import Divider from 'material-ui/Divider'
import { IDialogTitleComponentProps } from './IDialogTitleComponentProps'
import { IDialogTitleComponentState } from './IDialogTitleComponentState'
@@ -76,13 +76,13 @@ export default class DialogTitleComponent extends Component<IDialogTitleComponen
<div className='g__dialog-title'>
<div style={this.styles.contain as any}>
<div style={{ paddingRight: '10px' }}>
<SvgClose onClick={onRequestClose} hoverColor={grey400} style={{ cursor: 'pointer' }} />
<SvgClose onClick={onRequestClose} hoverColor={grey[400]} style={{ cursor: 'pointer' }} />
</div>
<div style={this.styles.title}>
{title || ''}
</div>
{ buttonLabel ? (<div style={{ marginTop: '-9px' }}>
<FlatButton label={buttonLabel || ''} primary={true} disabled={disabledButton ? disabledButton : false} onClick={onClickButton || (x => x)} />
<Button label={buttonLabel || ''} color='primary' disabled={disabledButton ? disabledButton : false} onClick={onClickButton || (x => x)} />
</div>) : ''}
</div>
<Divider />

View File

@@ -1,8 +1,8 @@
// - Import react components
import React, { Component } from 'react'
import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
import { grey200, grey400, grey600 } from 'material-ui/styles/colors'
import MoreVertIcon from 'material-ui-icons/moreVert'
import { grey } from 'material-ui/colors'
/**
* DOM styles
@@ -11,18 +11,9 @@ import { grey200, grey400, grey600 } from 'material-ui/styles/colors'
* @memberof Post
*/
const styles = {
iconButton: {
width: 24,
height: 24
}
}
const IconButtonElementComponent = (
<IconButton style={styles.iconButton} iconStyle={styles.iconButton}
touch={true}
>
<MoreVertIcon color={grey400} viewBox='9 0 24 24' />
</IconButton>
)
// TODO: Delete the component
const IconButtonElementComponent = (<div> </div>)
export default IconButtonElementComponent

View File

@@ -1,7 +1,7 @@
// - Import react components
import React, { Component } from 'react'
import { grey400, grey800, darkBlack, lightBlack,tealA400 } from 'material-ui/styles/colors'
import CircularProgress from 'material-ui/CircularProgress'
import { teal } from 'material-ui/colors'
import { CircularProgress } from 'material-ui/Progress'
/**
* Create component class
@@ -14,7 +14,7 @@ export default class LoadMoreProgressComponent extends Component<{},{}> {
*/
render () {
return (
<div className='g-load-more'><CircularProgress size={30} thickness={5} color={tealA400} /></div>
<div className='g-load-more'><CircularProgress size={30} thickness={5} style={{color: teal['A400'] }} /></div>
)
}
}

View File

@@ -52,4 +52,9 @@ export class CircleState {
* @memberof CircleState
*/
loaded: boolean = false
/**
* Circle stting state
*/
openSetting: {[circleId: string]: boolean }
}

View File

@@ -37,11 +37,14 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
case CircleActionType.UPDATE_CIRCLE:
return {
...state,
openSetting: {
...state.openSetting,
[payload.circle.id]: false
},
circleList: {
...state.circleList,
[payload.circle.id]: {
...payload.circle,
openCircleSettings: false
...payload.circle
}
}
}
@@ -156,24 +159,18 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
case CircleActionType.CLOSE_CIRCLE_SETTINGS:
return {
...state,
circleList: {
...state.circleList,
[payload.circleId]: {
...state.circleList[payload.circleId],
openCircleSettings: false
}
openSetting: {
...state.openSetting,
[payload.circleId]: false
}
}
case CircleActionType.OPEN_CIRCLE_SETTINGS:
return {
...state,
circleList: {
...state.circleList,
[payload.circleId]: {
...state.circleList[payload.circleId],
openCircleSettings: true
}
openSetting: {
...state.openSetting,
[payload.circleId]: true
}
}

View File

@@ -41,6 +41,7 @@ export let commentReducer = (state: CommentState = new CommentState(), action: I
return {
...state,
postComments: {
...state.postComments,
...payload
},
loaded: true

View File

@@ -200,4 +200,13 @@ input[type="file"] {
width: 76px;
margin: 3px auto 15px;
border-radius: 9px;
}
g__input-underline {
&::before {
display: none;
}
&::after {
display: none;
}
}

View File

@@ -1,5 +1,9 @@
.homeHeader__page {
border-left: 1px solid rgba(255, 255, 255, 0.2);
.homeHeader__title-root {
flex: 1;
}
.homeHeader__title {
border-left: 1px solid #fff;
padding-left: 24px;
margin-left: 24px;
line-height: 32px;
@@ -7,7 +11,7 @@
font-family: Roboto, RobotoDraft, Helvetica, Arial, sans-serif;
font-size: 20px;
-webkit-font-smoothing: antialiased;
color: white;
color: #fff;
}
.homeHeader__right {

View File

@@ -11,7 +11,12 @@
text-align: center;
}
.main-box {
padding: 26px 26px 0px 26px;
}
.buttons {
margin-top: 20px;
margin-left: 50px;
position: relative;
}
.close {

View File

@@ -1,3 +1,4 @@
.signup__button-box{
@extend .login__button-box
margin-top: 30px;
@extend .login__button-box;
}