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

View File

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

View File

@@ -14,10 +14,15 @@ import { CommentActionType } from 'constants/commentActionType'
import * as globalActions from 'actions/globalActions' import * as globalActions from 'actions/globalActions'
import * as notifyActions from 'actions/notifyActions' import * as notifyActions from 'actions/notifyActions'
import * as postActions from 'actions/postActions' import * as postActions from 'actions/postActions'
import * as serverActions from 'actions/serverActions'
import { ICommentService } from 'core/services/comments' import { ICommentService } from 'core/services/comments'
import { SocialProviderTypes } from 'core/socialProviderTypes' import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine' 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 * Get service providers
@@ -82,9 +87,16 @@ export const dbGetComments = (ownerUserId: string, postId: string) => {
const state = getState() const state = getState()
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
if (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}}) => { 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 * 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 _____________ */ /* _____________ CRUD State _____________ */
/** /**

View File

@@ -3,21 +3,38 @@ import React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors' import { grey } from 'material-ui/colors'
import { List, ListItem } from 'material-ui/List' import List, {
import SvgGroup from 'material-ui/svg-icons/action/group-work' ListItem,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
ListSubheader
} from 'material-ui/List'
import SvgGroup from 'material-ui-icons/groupWork'
import IconButton from 'material-ui/IconButton' import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert' import MoreVertIcon from 'material-ui-icons/moreVert'
import IconMenu from 'material-ui/IconMenu'
import TextField from 'material-ui/TextField' 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 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 Divider from 'material-ui/Divider'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
import SvgClose from 'material-ui/svg-icons/navigation/close' import SvgClose from 'material-ui-icons/close'
import AppBar from 'material-ui/AppBar' import AppBar from 'material-ui/AppBar'
import Paper from 'material-ui/Paper'
import Collapse from 'material-ui/transitions/Collapse'
// - Import app components // - Import app components
import UserAvatar from 'components/userAvatar' import UserAvatar from 'components/userAvatar'
@@ -32,6 +49,24 @@ import { ICircleComponentState } from './ICircleComponentState'
import { Circle, UserTie } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
import { Profile } from 'core/domain/users/profile' 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 * Create component class
*/ */
@@ -52,6 +87,14 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
}, },
settingContent: { settingContent: {
maxWidth: '400px' 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 * Component constructor
* @param {object} props is an object properties of component * @param {object} props is an object properties of component
*/ */
constructor (props: ICircleComponentProps) { constructor(props: ICircleComponentProps) {
super(props) super(props)
// Defaul state // Defaul state
@@ -75,7 +118,12 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
/** /**
* Save operation will be disable if user doesn't meet requirement * 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` // 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 * Update user's circle
* *
@@ -139,49 +205,74 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
let usersParsed: any = [] let usersParsed: any = []
if (usersOfCircle) { if (usersOfCircle) {
console.trace('usersOfCircle',usersOfCircle)
Object.keys(usersOfCircle).forEach((userId, index) => { Object.keys(usersOfCircle).forEach((userId, index) => {
const { fullName } = usersOfCircle[userId] const { fullName } = usersOfCircle[userId]
let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : '' let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : ''
usersParsed.push(<ListItem usersParsed.push(
key={`${this.props.id}.${userId}`} <ListItem
style={this.styles.userListItem as any} button
value={2} key={`${this.props.id}.${userId}`}
primaryText={fullName} style={this.styles.userListItem as any}
leftAvatar={<UserAvatar fullName={fullName!} fileName={avatar} />} value={2}
onClick={() => this.props.goTo!(`/${userId}`)} onClick={() => this.props.goTo!(`/${userId}`)}
/>) >
<UserAvatar fullName={fullName!} fileName={avatar} />
<ListItemText inset primary={fullName} />
</ListItem>)
}) })
return usersParsed 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 * Reneder component DOM
* @return {react element} return the DOM which rendered by component * @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 = ( const circleTitle = (
<div> <div>
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}> <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<div style={{ paddingRight: '10px' }}> <div style={{ paddingRight: '10px' }}>
<SvgClose onClick={this.props.closeCircleSettings} hoverColor={grey400} style={{ cursor: 'pointer' }} /> <SvgClose onClick={this.props.closeCircleSettings} style={{ cursor: 'pointer' }} />
</div> </div>
<div style={{ <div style={{
color: 'rgba(0,0,0,0.87)', color: 'rgba(0,0,0,0.87)',
@@ -191,45 +282,53 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
Circle settings Circle settings
</div> </div>
<div style={{ marginTop: '-9px' }}> <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>
</div> </div>
<Divider /> <Divider />
</div> </div>
) )
return ( return (
<div> <div>
<ListItem <ListItem
key={this.props.id} className={classes.root}
style={{ backgroundColor: '#fff', borderBottom: '1px solid rgba(0,0,0,0.12)', height: '72px', padding: '12px 0' }} key={this.props.id + '-CircleComponent'}
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}
onClick={this.handleToggleCircle} onClick={this.handleToggleCircle}
open={this.state.open}
nestedItems={this.userList()}
> >
<Dialog <ListItemIcon className={classes.icon}>
key={this.props.id} <SvgGroup />
title={circleTitle} </ListItemIcon>
modal={false} <ListItemText inset primary={<span style={this.styles}>{this.props.circle.name}</span>} />
open={this.props.openSetting!} <ListItemSecondaryAction>
onRequestClose={this.props.closeCircleSettings} { circle.isSystem ? null : rightIconMenu }
overlayStyle={this.styles.settingOverlay as any} </ListItemSecondaryAction>
contentStyle={this.styles.settingContent as any}
>
<div>
<TextField
hintText='Circle name'
floatingLabelText='Circle name'
onChange={this.handleChangeCircleName}
value={this.state.circleName}
/>
</div>
</Dialog>
</ListItem> </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 * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => { const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
const {circle, authorize, server} = state const { circle, authorize, server } = state
const {userTies} = circle const { userTies } = circle
const { uid } = state.authorize const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle
const circleId = ownProps.circle.id!
let usersOfCircle: {[userId: string]: UserTie} = {} let usersOfCircle: { [userId: string]: UserTie } = {}
Object.keys(userTies).forEach((userTieId) => { Object.keys(userTies).forEach((userTieId) => {
const theUserTie = userTies[userTieId] as UserTie const theUserTie = userTies[userTieId] as UserTie
if (theUserTie.circleIdList!.indexOf(ownProps.id) > -1) { if (theUserTie.circleIdList!.indexOf(ownProps.id) > -1) {
@@ -277,11 +376,11 @@ const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
}) })
return { return {
usersOfCircle, 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 userInfo: state.user.info
} }
} }
// - Connect component to redux store // - 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 * @memberof ICircleComponentProps
*/ */
openCircleSettings?: () => any openCircleSettings?: () => any
/**
* Styles
*/
classes?: any
} }

View File

@@ -24,4 +24,9 @@ export interface ICircleComponentState {
* @memberof ICircleComponentState * @memberof ICircleComponentState
*/ */
disabledSave: boolean 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 { Comment } from 'core/domain/comments'
// - Import material UI libraries // - Import material UI libraries
import { List, ListItem } from 'material-ui/List'
import Divider from 'material-ui/Divider' import Divider from 'material-ui/Divider'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors' import { grey } from 'material-ui/colors'
import IconButton from 'material-ui/IconButton' import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert' import MoreVertIcon from 'material-ui-icons/moreVert'
import IconMenu from 'material-ui/IconMenu' import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List'
import MenuItem from 'material-ui/MenuItem' import Menu, { MenuList, MenuItem } from 'material-ui/Menu'
import TextField from 'material-ui/TextField' 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 app components
import UserAvatarComponent from 'components/userAvatar' import UserAvatar from 'components/userAvatar'
// - Import API // - Import API
@@ -35,10 +40,58 @@ import * as userActions from 'actions/userActions'
import { ICommentComponentProps } from './ICommentComponentProps' import { ICommentComponentProps } from './ICommentComponentProps'
import { ICommentComponentState } from './ICommentComponentState' 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 * Create component class
*/ */
export class CommentComponent extends Component<ICommentComponentProps,ICommentComponentState> { export class CommentComponent extends Component<ICommentComponentProps, ICommentComponentState> {
static propTypes = { static propTypes = {
/** /**
@@ -62,16 +115,8 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @memberof CommentComponent * @memberof CommentComponent
*/ */
styles = { styles = {
comment: {
marginBottom: '12px'
},
iconButton: {
width: 16,
height: 16
},
author: { author: {
fontSize: '13px', fontSize: '10px',
paddingRight: '10px', paddingRight: '10px',
fontWeight: 400, fontWeight: 400,
color: 'rgba(0,0,0,0.87)', color: 'rgba(0,0,0,0.87)',
@@ -79,18 +124,6 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
overflow: 'hidden' overflow: 'hidden'
}, },
commentBody: {
fontSize: '13px',
lineHeight: '20px',
color: 'rgba(0,0,0,0.87)',
fontWeight: 300,
height: '',
display: 'block'
},
rightIconMenuItem: {
fontSize: '14px'
},
textarea: { textarea: {
fontWeight: 100, fontWeight: 100,
fontSize: '14px', 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 * 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 * @param {event} evt is an event passed by clicking on edit button
*/ */
handleEditComment = (evt: any) => { handleEditComment = (evt: any) => {
this.inputText.style.height = this.divComment.clientHeight + 'px' this.setState({openMenu: false})
this.props.openEditor() this.props.openEditor!()
} }
/** /**
@@ -177,7 +214,7 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
this.setState({ this.setState({
text: this.state.initialText 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 * @param {event} evt is an event passed by clicking on post button
*/ */
handleUpdateComment = (evt: any) => { handleUpdateComment = (evt: any) => {
const {comment} = this.props const { comment } = this.props
comment.editorStatus = undefined comment.editorStatus = undefined
comment.text = this.state.text comment.text = this.state.text
this.props.update(comment) this.props.update!(comment)
this.setState({ this.setState({
initialText: this.state.text initialText: this.state.text
}) })
@@ -202,7 +239,6 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
*/ */
handleOnChange = (evt: any) => { handleOnChange = (evt: any) => {
const data = evt.target.value const data = evt.target.value
this.inputText.style.height = evt.target.scrollHeight + 'px'
if (data.length === 0 || data.trim() === '' || data.trim() === this.state.initialText) { if (data.length === 0 || data.trim() === '' || data.trim() === this.state.initialText) {
this.setState({ this.setState({
text: data, text: data,
@@ -223,14 +259,28 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @param {string} id comment identifire * @param {string} id comment identifire
* @param {string} postId post identifier which comment belong to * @param {string} postId post identifier which comment belong to
*/ */
handleDelete = (evt: any, id?: string| null, postId?: string) => { handleDelete = (evt: any, id?: string | null, postId?: string) => {
this.props.delete(id, postId) 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 () { componentWillMount () {
const {userId} = this.props.comment const { userId } = this.props.comment
if (!this.props.isCommentOwner && !this.props.info[userId!]) { if (!this.props.isCommentOwner && !this.props.info![userId!]) {
this.props.getUserInfo() this.props.getUserInfo!()
} }
} }
@@ -243,56 +293,94 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
/** /**
* Comment object from props * Comment object from props
*/ */
const {comment} = this.props const { comment, classes, fullName, avatar } = this.props
const iconButtonElement = ( const { openMenu } = this.state
<IconButton style={this.styles.iconButton} iconStyle={this.styles.iconButton}
touch={true}
>
<MoreVertIcon color={grey400} viewBox='9 0 24 24' />
</IconButton>
)
const RightIconMenu = () => ( const RightIconMenu = () => (
<IconMenu iconButtonElement={iconButtonElement} style={{ display: 'block', position: 'absolute', top: '0px', right: '4px' }}> <Manager
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem> className={classes.iconButton}
{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>) : ''} <Target>
</IconMenu> <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 = () => ( const Author = () => (
<div style={{ marginTop: '-11px' }}> <div>
<span style={this.styles.author as any}>{comment.userDisplayName}</span><span style={{ <NavLink to={`/${userId}`}> <span style={this.styles.author as any}>{comment.userDisplayName}</span></NavLink><span style={{
fontWeight: 100, fontWeight: 100,
fontSize: '10px' fontSize: '8px'
}}>{moment.unix(comment.creationDate!).fromNow()}</span> }}>{moment.unix(comment.creationDate!).fromNow()}</span>
</div> </div>
) )
const { userId, editorStatus } = comment
const commentBody = ( 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 ( return (
<div className='animate-top' style={this.styles.comment} key={comment.id!}> <div className='animate-top' key={comment.id!}>
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', padding: '', display: (!this.state.display ? 'block' : 'none') }}> <Paper elevation={0} className='animate2-top10'
<div style={{ marginLeft: '0px', padding: '16px 56px 0px 72px', position: 'relative' }}> style={{ position: 'relative', padding: '', display: (!this.state.display ? 'block' : 'none') }}>
<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> <Card elevation={0}>
<NavLink to={`/${userId}`}> <Author /></NavLink> <CardHeader
{(!this.props.isCommentOwner && !this.props.isPostOwner && this.props.disableComments ) ? '' : (<RightIconMenu />)} className={classes.header}
<div style={{ outline: 'none', marginLeft: '16px', flex: 'auto', flexGrow: 1 }}> title={editorStatus ? '' : <Author />}
<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> subheader={commentBody}
<Linkify properties={{target: '_blank', style: {color: 'blue'}}}> avatar={<NavLink to={`/${userId}`}><UserAvatar fullName={fullName!} fileName={avatar!} size={24} /></NavLink>}
<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> action={(!this.props.isCommentOwner && !this.props.isPostOwner && this.props.disableComments) || editorStatus ? '' : (<RightIconMenu />)}
</Linkify> >
</div> </CardHeader>
</div>
<div style={{ display: (this.props.comment.editorStatus ? 'flex' : 'none'), flexDirection: 'row-reverse' }}> </Card>
<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>
</Paper> </Paper>
</div> </div>
@@ -308,16 +396,13 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
*/ */
const mapDispatchToProps = (dispatch: any, ownProps: ICommentComponentProps) => { const mapDispatchToProps = (dispatch: any, ownProps: ICommentComponentProps) => {
return { 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) => { update: (comment: Comment) => {
console.log('====================================')
console.log(comment)
console.log('====================================')
dispatch(commentActions.dbUpdateComment(comment)) dispatch(commentActions.dbUpdateComment(comment))
}, },
openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })), openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
closeEditor: () => dispatch(commentActions.closeCommentEditor({ 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 * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: any) => { 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 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 || '' : '' const fullName = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].fullName || '' : ''
return { return {
@@ -341,4 +426,4 @@ const mapStateToProps = (state: any, ownProps: any) => {
} }
// - Connect component to redux store // - 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} * @type {Function}
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
openEditor: Function openEditor?: Function
/** /**
* Close comment editor * Close comment editor
@@ -24,7 +24,7 @@ export interface ICommentComponentProps {
* @type {Function} * @type {Function}
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
closeEditor: () => any closeEditor?: () => any
/** /**
* Current user is comment owner {true} or not {false} * Current user is comment owner {true} or not {false}
@@ -32,7 +32,7 @@ export interface ICommentComponentProps {
* @type {boolean} * @type {boolean}
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
isCommentOwner: boolean isCommentOwner?: boolean
/** /**
* Current user is post owner {true} or not {false} * Current user is post owner {true} or not {false}
@@ -47,21 +47,21 @@ export interface ICommentComponentProps {
* *
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
update: (comment: Comment) => any update?: (comment: Comment) => any
/** /**
* Delete comment * Delete comment
* *
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
delete: (id?: string| null, postId?: string) => any delete?: (id?: string| null, postId?: string) => any
/** /**
* Get user profile * Get user profile
* *
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
getUserInfo: () => void getUserInfo?: () => void
/** /**
* User profile * User profile
@@ -69,7 +69,7 @@ export interface ICommentComponentProps {
* @type {{[userId: string]: Profile}} * @type {{[userId: string]: Profile}}
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
info: {[userId: string]: Profile} info?: {[userId: string]: Profile}
/** /**
* User full name * User full name
@@ -77,7 +77,7 @@ export interface ICommentComponentProps {
* @type {string} * @type {string}
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
fullName: string fullName?: string
/** /**
* User avatar address * User avatar address
@@ -85,7 +85,7 @@ export interface ICommentComponentProps {
* @type {string} * @type {string}
* @memberof Comment * @memberof Comment
*/ */
avatar: string avatar?: string
/** /**
* Writing comment on the post is disabled {true} or not false * Writing comment on the post is disabled {true} or not false
@@ -93,6 +93,11 @@ export interface ICommentComponentProps {
* @type {boolean} * @type {boolean}
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
disableComments: boolean disableComments?: boolean
/**
* Styles
*/
classes: any
} }

View File

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

View File

@@ -2,25 +2,73 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import _ from 'lodash' import _ from 'lodash'
import { NavLink } from 'react-router-dom'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import moment from 'moment'
import Paper from 'material-ui/Paper' 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 TextField from 'material-ui/TextField'
import Divider from 'material-ui/Divider' import Divider from 'material-ui/Divider'
import { ListItem } from 'material-ui/List' import { ListItemIcon, ListItemText, ListItem } from 'material-ui/List'
import { grey400, darkBlack, lightBlack, tealA400 } from 'material-ui/styles/colors' import { grey, teal } from 'material-ui/colors'
import LinearProgress from 'material-ui/LinearProgress' 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 actions
import * as commentActions from 'actions/commentActions' import * as commentActions from 'actions/commentActions'
// - Import app components // - Import app components
import CommentListComponent from 'components/CommentList' import CommentListComponent from 'components/CommentList'
import UserAvatarComponent from 'components/userAvatar' import UserAvatar from 'components/userAvatar'
import { ICommentGroupComponentProps } from './ICommentGroupComponentProps' import { ICommentGroupComponentProps } from './ICommentGroupComponentProps'
import { ICommentGroupComponentState } from './ICommentGroupComponentState' import { ICommentGroupComponentState } from './ICommentGroupComponentState'
import { Comment } from 'core/domain/comments/comment' 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 * Create component class
@@ -28,29 +76,29 @@ import { Comment } from 'core/domain/comments/comment'
export class CommentGroupComponent extends Component<ICommentGroupComponentProps, ICommentGroupComponentState> { export class CommentGroupComponent extends Component<ICommentGroupComponentProps, ICommentGroupComponentState> {
static propTypes = { static propTypes = {
/** /**
* If it's true comment box will be open * If it's true comment box will be open
*/ */
open: PropTypes.bool, 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, disableComments: PropTypes.bool,
/** /**
* The post identifier which comment belong to * The post identifier which comment belong to
*/ */
postId: PropTypes.string, 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, 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, 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 ownerPostUserId: PropTypes.string
} }
@@ -66,11 +114,29 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
zIndex: 5 zIndex: 5
}, },
writeCommentTextField: { writeCommentTextField: {
width: '100%' width: '100%',
fontWeight: 100,
fontSize: '14px'
}, },
progressbar: { progressbar: {
height: '1.5px', 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 {event} evt is an event passed by change comment text callback funciton
* @param {string} data is the comment text which user writes * @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 }) this.setState({ commentText: data })
if (data.length === 0 || data.trim() === '') { if (data.length === 0 || data.trim() === '') {
this.setState({ this.setState({
@@ -142,10 +209,11 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
* @return {DOM} list of comments' DOM * @return {DOM} list of comments' DOM
*/ */
commentList = () => { commentList = () => {
const {classes} = this.props
let comments = this.props.commentSlides let comments = this.props.commentSlides
if (comments) { if (comments) {
comments = _.fromPairs(_.toPairs(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[] = [] let parsedComments: Comment[] = []
Object.keys(comments).forEach((commentId) => { Object.keys(comments).forEach((commentId) => {
parsedComments.push({ parsedComments.push({
@@ -160,34 +228,41 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
parsedComments.push(parsedComments[0]) parsedComments.push(parsedComments[0])
} }
return parsedComments.map((comment, index) => { return parsedComments.map((comment, index) => {
const {userInfo} = this.props const { userInfo } = this.props
const commentAvatar = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].avatar || '' : '' const commentAvatar = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].avatar || '' : ''
const commentFullName = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].fullName || '' : '' 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' }} const commentBody = (
leftAvatar={<UserAvatarComponent fullName={commentFullName} fileName={commentAvatar} style={{ top: '8px' }} size={36} />} <div style={{ outline: 'none', flex: 'auto', flexGrow: 1 }}>
secondaryText={<div style={{ height: '' }}> <div className={classNames('animate2-top10', classes.commentBody)} >
<span style={{ {comment.text}
fontSize: '13px', </div>
paddingRight: '10px', </div>
fontWeight: 400, )
color: 'rgba(0,0,0,0.87)',
textOverflow: 'ellipsis', const Author = () => (
overflow: 'hidden' <div>
}}> <NavLink to={`/${comment.userId!}`}> <span className={classes.author}>{comment.userDisplayName}</span></NavLink><span style={{
{comment.userDisplayName}: fontWeight: 100,
</span> fontSize: '8px'
<span style={{ }}>{moment.unix(comment.creationDate!).fromNow()}</span>
fontSize: '13px', </div>
lineHeight: '20px', )
color: 'rgba(0,0,0,0.87)', return (
fontWeight: 300, <Paper key={comment.id! + '-index:' + index} elevation={0} className='animate2-top10'>
whiteSpace: 'pre-wrap' <Card elevation={0}>
}}>{comment.text}</span> <CardHeader
</div>} className={classes.header}
secondaryTextLines={2} 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 * @return {react element} return the DOM which rendered by component
*/ */
render () { render () {
const {comments} = this.props const { comments, classes, postId, fullName, avatar, getCommentsRequest, open, commentSlides } = this.props
/** /**
* Comment list box * Comment list box
*/ */
const commentWriteBox = (<div> const commentWriteBox = (<div>
<Divider /> <Divider />
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', overflowY: 'auto', padding: '12px 16px', display: (this.props.open ? 'block' : 'none') }}> <Paper key={postId! + '-commentwrite'} elevation={0} className='animate2-top10'>
<Card elevation={0}>
<div style={{ display: 'flex' }}> <CardHeader
<UserAvatarComponent fullName={this.props.fullName!} fileName={this.props.avatar!} style={{ flex: 'none', margin: '4px 0px' }} size={36} /> className={classes.header}
<div style={{ outline: 'none', marginLeft: '16px', flex: 'auto', flexGrow: 1 }}> avatar={<UserAvatar fullName={fullName!} fileName={avatar!} size={24} />}
<TextField subheader={<TextField
value={this.state.commentText} autoFocus
onChange={this.handleOnChange} placeholder={'Add a comment...'}
hintText='Add a comment...' multiline
underlineShow={false} rowsMax='4'
multiLine={true} InputProps={{
rows={1} disableUnderline: true,
hintStyle={{ fontWeight: 100, fontSize: '14px' }} autoFocus: true,
rowsMax={4} fullWidth: true
textareaStyle={{ fontWeight: 100, fontSize: '14px' }} }}
style={this.styles.writeCommentTextField} value={this.state.commentText}
/> onChange={this.handleChange}
</div> 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> </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 Elements
*/ */
return ( return (
<div> <div key={postId + '-comments'}>
<div style={this.props.commentSlides && Object.keys(this.props.commentSlides).length > 0 ? { display: 'block' } : { display: 'none' }}> <div style={commentSlides && Object.keys(commentSlides).length > 0 ? { display: 'block' } : { display: 'none' }}>
<Divider /> <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' }} > <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'> <div className='comment__list-show'>
{this.commentList()} {this.commentList()}
@@ -247,20 +337,14 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
</div> </div>
</Paper> </Paper>
{ {
!comments open ? loadComments : ''
? 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>)
: '')
} }
</div> </div>
{ {
!this.props.disableComments (!this.props.disableComments && open )
? commentWriteBox ? commentWriteBox
: '' : ''
} }
</div> </div>
) )
@@ -276,7 +360,7 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps) => { const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps) => {
return { return {
send: (text: string, postId: string, callBack: Function) => { send: (text: string, postId: string, callBack: Function) => {
dispatch(commentActions.dbAddComment(ownProps.ownerPostUserId,{ dispatch(commentActions.dbAddComment(ownProps.ownerPostUserId, {
postId: postId, postId: postId,
text: text text: text
}, callBack)) }, callBack))
@@ -291,11 +375,13 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => { const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => {
const {post, user, authorize} = state const { post, user, authorize, server } = state
const {ownerPostUserId, postId} = ownProps const {request} = server
const { ownerPostUserId, postId } = ownProps
const commentSlides = post.userPosts[ownerPostUserId] && post.userPosts[ownerPostUserId][postId] ? post.userPosts[ownerPostUserId][postId].comments : {} 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 { return {
getCommentsRequest,
commentSlides, commentSlides,
avatar: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].avatar || '' : '', 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 || '' : '', 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 // - 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 { Profile } from 'core/domain/users'
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
import { ServerRequestModel } from 'models/server'
export interface ICommentGroupComponentProps { export interface ICommentGroupComponentProps {
/** /**
@@ -98,4 +99,14 @@ export interface ICommentGroupComponentProps {
*/ */
send?: (commentText: string, postId: string, clearCommentWrite: () => void) => any 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 React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { List, ListItem } from 'material-ui/List' import List, { ListItem, ListItemText } from 'material-ui/List'
// - Import app components // - Import app components
import CommentComponent from 'components/Comment' 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 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 = { const styles: any = {
list: { list: {
width: '100%', width: '100%',
maxHeight: 450, maxHeight: 450
overflowY: 'auto'
} }
} }

View File

@@ -2,25 +2,33 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import PropTypes from 'prop-types' 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 IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert' import MoreVertIcon from 'material-ui-icons/moreVert'
import SvgCamera from 'material-ui/svg-icons/image/photo-camera' import SvgCamera from 'material-ui-icons/photoCamera'
import IconMenu from 'material-ui/IconMenu' import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List'
import MenuItem from 'material-ui/MenuItem' import Menu, { MenuList, MenuItem } from 'material-ui/Menu'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
import EventListener, { withOptions } from 'react-event-listener' 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 Divider from 'material-ui/Divider'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField' 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 app components
import ImgCover from 'components/imgCover' import ImgCover from 'components/imgCover'
import UserAvatarComponent from 'components/userAvatar' import UserAvatarComponent from 'components/userAvatar'
import ImageGallery from 'components/imageGallery' import ImageGallery from 'components/imageGallery'
import DialogTitle from 'layouts/DialogTitle' import AppDialogTitle from 'layouts/dialogTitle'
// - Import API // - Import API
import FileAPI from 'api/FileAPI' import FileAPI from 'api/FileAPI'
@@ -34,6 +42,12 @@ import { IEditProfileComponentProps } from './IEditProfileComponentProps'
import { IEditProfileComponentState } from './IEditProfileComponentState' import { IEditProfileComponentState } from './IEditProfileComponentState'
import { Profile } from 'core/domain/users' import { Profile } from 'core/domain/users'
const styles = (theme: any) => ({
dialogTitle: {
padding: 0
}
})
/** /**
* Create component class * Create component class
*/ */
@@ -277,20 +291,20 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
*/ */
render () { render () {
const {classes} = this.props
const iconButtonElement = ( const iconButtonElement = (
<IconButton style={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton} iconStyle={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton} <IconButton style={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton}>
touch={true} <MoreVertIcon style={{...(this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton),color: grey[400]}} viewBox='10 0 24 24' />
>
<MoreVertIcon color={grey400} viewBox='10 0 24 24' />
</IconButton> </IconButton>
) )
const RightIconMenu = () => ( const RightIconMenu = () => (
<IconMenu iconButtonElement={iconButtonElement}> <div>
{iconButtonElement}
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem> <MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem>
<MenuItem style={{ fontSize: '14px' }}>Edit</MenuItem> <MenuItem style={{ fontSize: '14px' }}>Edit</MenuItem>
<MenuItem style={{ fontSize: '14px' }}>Delete</MenuItem> <MenuItem style={{ fontSize: '14px' }}>Delete</MenuItem>
</IconMenu> </div>
) )
return ( return (
@@ -299,15 +313,10 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
{/* Edit profile dialog */} {/* Edit profile dialog */}
<Dialog <Dialog
key='Edit-Profile' key='Edit-Profile'
modal={false}
open={this.props.open!} open={this.props.open!}
onRequestClose={this.props.onRequestClose} onClose={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%' }}
> >
<DialogContent>
{/* Banner */} {/* Banner */}
<div style={{ position: 'relative' }}> <div style={{ position: 'relative' }}>
<ImgCover width='100%' height='250px' borderRadius='2px' fileName={this.state.banner} /> <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 className='left'>
<div style={{ display: 'flex', justifyContent: 'center' }}> <div style={{ display: 'flex', justifyContent: 'center' }}>
{/* Avatar */} {/* 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)' }} /> <SvgCamera style={{ fill: 'rgba(255, 255, 255, 0.88)', transform: 'translate(6px, 6px)' }} />
</div> </div>
@@ -340,61 +349,59 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
</div> </div>
{/* Edit user information box*/} {/* 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.title as any}>Personal Information</div>
<div style={this.styles.box}> <div style={this.styles.box}>
<TextField <FormControl aria-describedby='fullNameInputError'>
floatingLabelText='Full name' <InputLabel htmlFor='fullNameInput'>Full name</InputLabel>
onChange={this.handleInputChange} <Input id='fullNameInput'
name='fullNameInput' onChange={this.handleInputChange}
errorText={this.state.fullNameInputError} name='fullNameInput'
value={this.state.fullNameInput} value={this.state.fullNameInput} />
/> <FormHelperText id='fullNameInputError'>{this.state.fullNameInputError}</FormHelperText>
</FormControl>
</div> </div>
<br /> <br />
<div style={this.styles.box}> <div style={this.styles.box}>
<TextField <FormControl aria-describedby='tagLineInputError'>
floatingLabelText='Tag Line' <InputLabel htmlFor='tagLineInput'>Tagline</InputLabel>
onChange={this.handleInputChange} <Input id='tagLineInput'
name='tagLineInput' onChange={this.handleInputChange}
value={this.state.tagLineInput} name='tagLineInput'
/> value={this.state.tagLineInput} />
<FormHelperText id='tagLineInputError'>{this.state.fullNameInputError}</FormHelperText>
</FormControl>
</div> </div>
<br /> <br />
<div style={this.styles.actions as any}> <div style={this.styles.actions as any}>
<FlatButton label='CANCEL' onClick={this.props.onRequestClose} /> <Button onClick={this.props.onRequestClose} > CANCEL </Button>
<RaisedButton label='UPDATE' primary={true} onClick={this.handleUpdate} style={this.styles.updateButton} /> <Button raised color='primary' onClick={this.handleUpdate} style={this.styles.updateButton}> UPDATE </Button>
</div> </div>
</Paper> </Paper>
<div style={{ height: '16px' }}></div> <div style={{ height: '16px' }}></div>
</DialogContent>
</Dialog> </Dialog>
{/* Image gallery for banner*/} {/* Image gallery for banner*/}
<Dialog <Dialog
title={<DialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />}
modal={false}
open={this.state.openBanner} open={this.state.openBanner}
contentStyle={this.styles.dialogGallery} onClose={this.handleCloseBannerGallery}
onRequestClose={this.handleCloseBannerGallery}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
autoDetectWindowHeight={false}
> >
<DialogTitle className={classes.dialogTitle}>
<AppDialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />
</DialogTitle>
<ImageGallery set={this.handleRequestSetBanner} close={this.handleCloseBannerGallery} /> <ImageGallery set={this.handleRequestSetBanner} close={this.handleCloseBannerGallery} />
</Dialog> </Dialog>
{/* Image gallery for avatar */} {/* Image gallery for avatar */}
<Dialog <Dialog
title={<DialogTitle title='Choose an avatar image' onRequestClose={this.handleCloseAvatarGallery} />}
modal={false}
open={this.state.openAvatar} open={this.state.openAvatar}
contentStyle={this.styles.dialogGallery} onClose={this.handleCloseAvatarGallery}
onRequestClose={this.handleCloseAvatarGallery}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
autoDetectWindowHeight={false}
> >
<DialogTitle className={classes.dialogTitle}>
<AppDialogTitle title='Choose an avatar image' onRequestClose={this.handleCloseAvatarGallery} />
</DialogTitle>
<ImageGallery set={this.handleRequestSetAvatar} close={this.handleCloseAvatarGallery} /> <ImageGallery set={this.handleRequestSetAvatar} close={this.handleCloseAvatarGallery} />
</Dialog> </Dialog>
@@ -433,4 +440,4 @@ const mapStateToProps = (state: any, ownProps: IEditProfileComponentProps) => {
} }
// - Connect component to redux store // - 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 * @memberof IEditProfileComponentProps
*/ */
onRequestClose?: () => void 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 { push } from 'react-router-redux'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
// - Import actions // - Import actions
import * as authorizeActions from 'actions/authorizeActions' import * as authorizeActions from 'actions/authorizeActions'
@@ -74,7 +73,7 @@ export class EmailVerificationComponent extends Component<IEmailVerificationComp
}}>Green</h1> }}>Green</h1>
<div className='animate-bottom'> <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={{ padding: '48px 40px 36px' }}>
<div style={{ <div style={{
paddingLeft: '40px', 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. 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> </p>
<div style={this.styles.buttons}> <div style={this.styles.buttons}>
<RaisedButton style={this.styles.homeButton} label='Home' primary={true} onClick={() => this.props.homePage()} /> <Button raised style={this.styles.homeButton} label='Home' color='primary' onClick={() => this.props.homePage()} />
<RaisedButton label='Send Email Verification' primary={true} onClick={() => this.props.sendEmailVerification()} /> <Button raised label='Send Email Verification' color='primary' onClick={() => this.props.sendEmailVerification()} />
</div> </div>
<div> <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 { connect } from 'react-redux'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import Menu from 'material-ui/Menu' 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 Divider from 'material-ui/Divider'
import SvgArrowLeft from 'material-ui/svg-icons/hardware/keyboard-arrow-left' import SvgArrowLeft from 'material-ui-icons/keyboardArrowLeft'
import SvgHome from 'material-ui/svg-icons/action/home' import SvgHome from 'material-ui-icons/home'
import SvgFeedback from 'material-ui/svg-icons/action/feedback' import SvgFeedback from 'material-ui-icons/feedback'
import SvgSettings from 'material-ui/svg-icons/action/settings' import SvgSettings from 'material-ui-icons/settings'
import SvgAccountCircle from 'material-ui/svg-icons/action/account-circle' import SvgAccountCircle from 'material-ui-icons/accountCircle'
import SvgPeople from 'material-ui/svg-icons/social/people' import SvgPeople from 'material-ui-icons/people'
// - Import app components // - Import app components
import Sidebar from 'components/sidebar' import Sidebar from 'components/sidebar'
@@ -105,7 +106,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
} }
componentWillMount () { componentWillMount () {
const {global, clearData, loadData, authed, defaultDataEnable, isVerifide, goTo } = this.props const { global, clearData, loadData, authed, defaultDataEnable, isVerifide, goTo } = this.props
if (!authed) { if (!authed) {
goTo!('/login') goTo!('/login')
return return
@@ -129,29 +130,71 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
* @memberof Home * @memberof Home
*/ */
render () { render () {
const {loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback} = this.props const { loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback } = this.props
return ( return (
<div id='home'> <div id='home'>
<HomeHeader sidebar={this.state.sidebarOpen} sidebarStatus={this.state.sidebarStatus} /> <HomeHeader sidebar={this.state.sidebarOpen} sidebarStatus={this.state.sidebarStatus} />
<Sidebar overlay={this.sidebarOverlay} open={this.sidebar} status={this.sidebarStatus}> <Sidebar overlay={this.sidebarOverlay} open={this.sidebar} status={this.sidebarStatus}>
<SidebarContent> <SidebarContent>
<Menu style={{ color: 'rgb(117, 117, 117)', width: '210px' }}> <MenuList style={{ color: 'rgb(117, 117, 117)', width: '210px' }}>
{this.state.sidebarOverlay {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='/'>
<NavLink to={`/${this.props.uid}`}><MenuItem primaryText='Profile' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgAccountCircle />} /></NavLink> <MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
<NavLink to='/people'><MenuItem primaryText='People' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgPeople />} /></NavLink> <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 /> <Divider />
<NavLink to='/settings'><MenuItem primaryText='Settings' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgSettings />} /></NavLink> <NavLink to='/settings'>
<MenuItem primaryText='Send feedback' onClick={() => showSendFeedback()} style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgFeedback />} /> <MenuItem style={{ color: 'rgb(117, 117, 117)' }}>
</Menu> <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> </SidebarContent>
<SidebarMain> <SidebarMain>
<HomeRouter enabled={loaded!} data={{ mergedPosts, loadDataStream, hasMorePosts}} /> <HomeRouter enabled={loaded!} data={{ mergedPosts, loadDataStream, hasMorePosts }} />
</SidebarMain> </SidebarMain>
</Sidebar> </Sidebar>
@@ -166,7 +209,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
return { return {
loadDataStream: loadDataStream:
(page: number, limit: number) => dispatch(postActions.dbGetPosts(page,limit)), (page: number, limit: number) => dispatch(postActions.dbGetPosts(page, limit)),
loadData: () => { loadData: () => {
dispatch(postActions.dbGetPosts()) dispatch(postActions.dbGetPosts())
dispatch(imageGalleryActions.dbGetImageGallery()) dispatch(imageGalleryActions.dbGetImageGallery())
@@ -216,9 +259,9 @@ const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
const hasMorePosts = post.stream.hasMoreData const hasMorePosts = post.stream.hasMoreData
Object.keys(followingUsers).forEach((userId) => { Object.keys(followingUsers).forEach((userId) => {
let newPosts = post.userPosts ? post.userPosts[userId] : {} let newPosts = post.userPosts ? post.userPosts[userId] : {}
_.merge(mergedPosts,newPosts) _.merge(mergedPosts, newPosts)
}) })
_.merge(mergedPosts,posts) _.merge(mergedPosts, posts)
return { return {
authed: authorize.authed, authed: authorize.authed,
isVerifide: authorize.isVerifide, isVerifide: authorize.isVerifide,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,4 +21,9 @@ export interface ILoginComponentProps {
* @memberof ILoginComponentProps * @memberof ILoginComponentProps
*/ */
signupPage?: () => any 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 { push } from 'react-router-redux'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import FontIcon from 'material-ui/FontIcon' import IconButton from 'material-ui/IconButton'
import Divider from 'material-ui/Divider' 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 actions
import * as authorizeActions from 'actions/authorizeActions' import * as authorizeActions from 'actions/authorizeActions'
import { ILoginComponentProps } from './ILoginComponentProps' import { ILoginComponentProps } from './ILoginComponentProps'
import { ILoginComponentState } from './ILoginComponentState' import { ILoginComponentState } from './ILoginComponentState'
import { firebaseAuth } from 'data/firebaseClient'
import { OAuthType } from 'core/domain/authorize' import { OAuthType } from 'core/domain/authorize'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
}
})
// - Create Login component class // - Create Login component class
export class LoginComponent extends Component<ILoginComponentProps,ILoginComponentState> { export class LoginComponent extends Component<ILoginComponentProps,ILoginComponentState> {
styles = { styles = {
singinOptions: { singinOptions: {
paddingBottom: 10 paddingBottom: 10,
justifyContent: 'space-around',
display: 'flex'
}, },
divider: { divider: {
marginBottom: 10, marginBottom: 10,
@@ -124,6 +134,7 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { render () {
const {classes} = this.props
const paperStyle = { const paperStyle = {
minHeight: 370, minHeight: 370,
@@ -148,7 +159,7 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
}}>Green</h1> }}>Green</h1>
<div className='animate-bottom'> <div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} > <Paper style={paperStyle} elevation={1} >
<div style={{ padding: '48px 40px 36px' }}> <div style={{ padding: '48px 40px 36px' }}>
<div style={{ <div style={{
paddingLeft: '40px', paddingLeft: '40px',
@@ -164,38 +175,37 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
margin: 0 margin: 0
}} className='zoomOutLCorner animated'>Sign in</h2> }} className='zoomOutLCorner animated'>Sign in</h2>
</div> </div>
<div style={this.styles.singinOptions}> <div style={this.styles.singinOptions as any}>
<FlatButton <IconButton
icon={<div className='icon-fb icon'></div>} onClick={() => loginWithOAuth!(OAuthType.FACEBOOK)}
onClick={() => loginWithOAuth(OAuthType.FACEBOOK)} ><div className='icon-fb icon'></div></IconButton>
/> <IconButton
<FlatButton onClick={() => loginWithOAuth!(OAuthType.GOOGLE)}
icon={<div className='icon-google icon'></div>} > <div className='icon-google icon'></div> </IconButton>
onClick={() => loginWithOAuth(OAuthType.GOOGLE)} <IconButton
/> onClick={() => loginWithOAuth!(OAuthType.GITHUB)}
<FlatButton > <div className='icon-github icon'></div> </IconButton>
icon={<div className='icon-github icon'></div>}
onClick={() => loginWithOAuth(OAuthType.GITHUB)}
/>
</div> </div>
<Divider style={this.styles.divider} /> <Divider style={this.styles.divider} />
<TextField <TextField
className={classes.textField}
autoFocus
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.emailInputError} helperText={this.state.emailInputError}
error={this.state.emailInputError.trim() !== ''}
name='emailInput' name='emailInput'
floatingLabelStyle={{ fontSize: '15px' }} label='Email'
floatingLabelText='Email'
type='email' type='email'
tabIndex={1} tabIndex={1}
/><br /> /><br />
<TextField <TextField
className={classes.textField}
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.passwordInputError} helperText={this.state.passwordInputError}
error={this.state.passwordInputError.trim() !== ''}
name='passwordInput' name='passwordInput'
floatingLabelStyle={{ fontSize: '15px' }} label='Password'
floatingLabelText='Password'
type='password' type='password'
tabIndex={2} tabIndex={2}
/><br /> /><br />
@@ -203,10 +213,10 @@ export class LoginComponent extends Component<ILoginComponentProps,ILoginCompone
<br /> <br />
<div className='login__button-box'> <div className='login__button-box'>
<div> <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>
<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>
</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> <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 // - 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 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 components
import React, { Component } from 'react' import React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { Route, Switch, NavLink, withRouter, Redirect } from 'react-router-dom' import { Route, Switch, NavLink, withRouter, Redirect } from 'react-router-dom'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import Snackbar from 'material-ui/Snackbar' import Snackbar from 'material-ui/Snackbar'
import LinearProgress from 'material-ui/LinearProgress' import { LinearProgress } from 'material-ui/Progress'
// - Import components // - Import components
@@ -125,12 +125,12 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
*/ */
public render () { public render () {
const { progress, global, loaded, guest, uid } = this.props const { progress, global, loaded, guest, uid, sendFeedbackStatus } = this.props
const { loading, isVerifide } = this.state const { loading, isVerifide } = this.state
return ( return (
<div id='master'> <div id='master'>
<SendFeedback /> {sendFeedbackStatus ? <SendFeedback /> : ''}
<div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}> <div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}>
<LinearProgress mode='determinate' value={progress.percent} /> <LinearProgress mode='determinate' value={progress.percent} />
</div> </div>
@@ -196,7 +196,9 @@ const mapDispatchToProps = (dispatch: any, ownProps: IMasterComponentProps) => {
*/ */
const mapStateToProps = (state: any) => { const mapStateToProps = (state: any) => {
const { authorize, global, user, post, comment, imageGallery, vote, notify, circle } = state const { authorize, global, user, post, comment, imageGallery, vote, notify, circle } = state
const { sendFeedbackStatus } = global
return { return {
sendFeedbackStatus,
guest: authorize.guest, guest: authorize.guest,
uid: authorize.uid, uid: authorize.uid,
authed: authorize.authed, authed: authorize.authed,

View File

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

View File

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

View File

@@ -2,7 +2,14 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { connect } from 'react-redux' 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 app components
import NotifyItem from 'components/notifyItem' import NotifyItem from 'components/notifyItem'
@@ -15,10 +22,30 @@ import { INotifyComponentProps } from './INotifyComponentProps'
import { INotifyComponentState } from './INotifyComponentState' import { INotifyComponentState } from './INotifyComponentState'
import { Notification } from 'core/domain/notifications' 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 * Create component class
*/ */
export class NotifyComponent extends Component<INotifyComponentProps,INotifyComponentState> { export class NotifyComponent extends Component<INotifyComponentProps, INotifyComponentState> {
static propTypes = { static propTypes = {
/** /**
@@ -60,7 +87,7 @@ export class NotifyComponent extends Component<INotifyComponentProps,INotifyComp
let parsedDOM: any[] = [] let parsedDOM: any[] = []
if (notifications) { if (notifications) {
Object.keys(notifications).forEach((key) => { Object.keys(notifications).forEach((key) => {
const {notifierUserId} = notifications![key] const { notifierUserId } = notifications![key]
parsedDOM.push( parsedDOM.push(
<NotifyItem <NotifyItem
key={key} key={key}
@@ -68,7 +95,7 @@ export class NotifyComponent extends Component<INotifyComponentProps,INotifyComp
fullName={(info![notifierUserId] ? info![notifierUserId].fullName || '' : '')} fullName={(info![notifierUserId] ? info![notifierUserId].fullName || '' : '')}
avatar={(info![notifierUserId] ? info![notifierUserId].avatar || '' : '')} avatar={(info![notifierUserId] ? info![notifierUserId].avatar || '' : '')}
id={key} id={key}
isSeen={(notifications![key] ? notifications![key].isSeen || false : false )} isSeen={(notifications![key] ? notifications![key].isSeen || false : false)}
url={(notifications![key] ? notifications![key].url || '' : '')} url={(notifications![key] ? notifications![key].url || '' : '')}
notifierUserId={notifierUserId} notifierUserId={notifierUserId}
closeNotify={onRequestClose} closeNotify={onRequestClose}
@@ -84,25 +111,27 @@ export class NotifyComponent extends Component<INotifyComponentProps,INotifyComp
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { 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 ( return (
<Popover <Popper
className='homeHeader__notify-menu' placement='bottom-start'
open={open} eventsEnabled={open}
anchorEl={anchorEl} className={classNames({ [classes.popperClose]: !open })}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
onRequestClose={onRequestClose}
> >
<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 // - 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 * @memberof INotifyItemComponentProps
*/ */
seenNotify?: (notificationId: string) => any 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 { connect } from 'react-redux'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import SvgClose from 'material-ui/svg-icons/navigation/close' import SvgClose from 'material-ui-icons/close'
import { grey400 } from 'material-ui/styles/colors' 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 app components
import UserAvatar from 'components/userAvatar' import UserAvatar from 'components/userAvatar'
@@ -18,6 +20,20 @@ import * as notifyActions from 'actions/notifyActions'
import { INotifyItemComponentProps } from './INotifyItemComponentProps' import { INotifyItemComponentProps } from './INotifyItemComponentProps'
import { INotifyItemComponentState } from './INotifyItemComponentState' 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 * Create component class
*/ */
@@ -90,13 +106,12 @@ export class NotifyItemComponent extends Component<INotifyItemComponentProps,INo
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { 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 ( return (
<div className='item' style={isSeen ? { opacity: 0.6 } : {}} key={id}> <ListItem key={notifierUserId} dense button className={classes.listItem} style={isSeen ? { opacity: 0.6 } : {}}>
<div className='avatar'> <NavLink
<NavLink
to={`/${notifierUserId}`} to={`/${notifierUserId}`}
onClick={(evt) => { onClick={(evt) => {
evt.preventDefault() evt.preventDefault()
@@ -106,21 +121,20 @@ export class NotifyItemComponent extends Component<INotifyItemComponentProps,INo
> >
<UserAvatar fullName={fullName} fileName={avatar} /> <UserAvatar fullName={fullName} fileName={avatar} />
</NavLink> </NavLink>
</div> <ListItemText primary={<NavLink to={url} onClick={this.handleSeenNotify}>
<div className='info'>
<NavLink to={url} onClick={this.handleSeenNotify}>
<div className='user-name'> <div className='user-name'>
{fullName} {fullName}
</div> </div>
<div className='description'> <div className='description'>
{description} {description}
</div> </div>
</NavLink> </NavLink>} />
<ListItemSecondaryAction className={classes.closeButton}>
<div onClick={() => deleteNotiy!(id)}>
<SvgClose className={classes.closeIcon} style={{ cursor: 'pointer' }} />
</div> </div>
<div className='close' onClick={() => deleteNotiy!(id)}> </ListItemSecondaryAction>
<SvgClose hoverColor={grey400} style={{ cursor: 'pointer' }} /> </ListItem>
</div>
</div>
) )
} }
@@ -153,4 +167,4 @@ const mapStateToProps = (state: any, ownProps: INotifyItemComponentProps) => {
} }
// - Connect component to redux store // - 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 { export interface IPeopleComponentState {
/**
* Tab index
*/
tabIndex: number
} }

View File

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

View File

@@ -38,5 +38,18 @@ export interface IPostComponentState {
*/ */
openPostWrite: boolean openPostWrite: boolean
/**
* Open the comment group
*/
openCommentGroup?: () => void 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 PropTypes from 'prop-types'
import moment from 'moment' import moment from 'moment'
import Linkify from 'react-linkify' import Linkify from 'react-linkify'
import copy from 'copy-to-clipboard'
// - Material UI // - Material UI
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card' import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import FloatingActionButton from 'material-ui/FloatingActionButton' import Typography from 'material-ui/Typography'
import SvgShare from 'material-ui/svg-icons/social/share' import SvgShare from 'material-ui-icons/share'
import SvgLink from 'material-ui/svg-icons/content/link' import SvgLink from 'material-ui-icons/link'
import SvgComment from 'material-ui/svg-icons/communication/comment' import SvgComment from 'material-ui-icons/comment'
import SvgFavorite from 'material-ui/svg-icons/action/favorite' import SvgFavorite from 'material-ui-icons/favorite'
import SvgFavoriteBorder from 'material-ui/svg-icons/action/favorite-border' import SvgFavoriteBorder from 'material-ui-icons/favoriteBorder'
import Checkbox from 'material-ui/Checkbox' 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 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 Paper from 'material-ui/Paper'
import Menu from 'material-ui/Menu' 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 TextField from 'material-ui/TextField'
import Dialog from 'material-ui/Dialog' import Dialog from 'material-ui/Dialog'
import IconButton from 'material-ui/IconButton' import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert' import MoreVertIcon from 'material-ui-icons/moreVert'
import IconMenu from 'material-ui/IconMenu' 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' import reactStringReplace from 'react-string-replace'
@@ -45,33 +51,68 @@ import * as globalActions from 'actions/globalActions'
import { IPostComponentProps } from './IPostComponentProps' import { IPostComponentProps } from './IPostComponentProps'
import { IPostComponentState } from './IPostComponentState' 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 // - Create component class
export class PostComponent extends Component<IPostComponentProps,IPostComponentState> { export class PostComponent extends Component<IPostComponentProps, IPostComponentState> {
styles = { styles = {
counter: {
lineHeight: '36px',
color: '#777',
fontSize: '12px',
marginRight: '6px'
},
postBody: {
wordWrap: 'break-word'
},
dialog: { dialog: {
width: '', width: '',
maxWidth: '530px', maxWidth: '530px',
borderRadius: '4px' 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) { constructor (props: IPostComponentProps) {
super(props) super(props)
const {post} = props const { post } = props
this.state = { this.state = {
/** /**
* Post text * Post text
@@ -119,7 +160,15 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
/** /**
* If it's true, post write will be open * 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 // 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 * @param {event} evt passed by clicking on comment slide show
*/ */
handleOpenComments = () => { handleOpenComments = () => {
const { getPostComments, commentList, post} = this.props const { getPostComments, commentList, post } = this.props
const {id, ownerUserId} = post const { id, ownerUserId } = post
if (!commentList) { if (!commentList) {
getPostComments(ownerUserId!, id!) getPostComments!(ownerUserId!, id!)
} }
this.setState({ this.setState({
openComments: !this.state.openComments openComments: !this.state.openComments
@@ -181,10 +230,31 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @memberof Post * @memberof Post
*/ */
handleDelete = () => { handleDelete = () => {
const {post} = this.props const { post } = this.props
this.props.delete!(post.id!) 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 * Show copy link
* *
@@ -205,6 +275,8 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @memberof Post * @memberof Post
*/ */
handleOpenShare = () => { handleOpenShare = () => {
const {post} = this.props
copy(`${location.origin}/${post.ownerUserId}/posts/${post.id}`)
this.setState({ this.setState({
shareOpen: true shareOpen: true
}) })
@@ -264,77 +336,114 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { 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 = () => ( const RightIconMenu = () => (
<IconMenu iconButtonElement={IconButtonElement} style={{ display: 'block', position: 'absolute', top: '0px', right: '4px' }}> <Manager>
<MenuItem primaryText='Edit' onClick={this.handleOpenPostWrite} /> <Target>
<MenuItem primaryText='Delete' onClick={this.handleDelete} /> <IconButton
<MenuItem primaryText={post.disableComments ? 'Enable comments' : 'Disable comments'} onClick={() => this.props.toggleDisableComments!(!post.disableComments)} /> aria-owns={isPostMenuOpen! ? 'post-menu' : null}
<MenuItem primaryText={post.disableSharing ? 'Enable sharing' : 'Disable sharing'} onClick={() => this.props.toggleSharingComments!(!post.disableSharing)} /> aria-haspopup='true'
</IconMenu> 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 // Define variables
return ( return (
<Card> <Card>
<CardHeader <CardHeader
title={<NavLink to={`/${ownerUserId}`}>{ownerDisplayName}</NavLink>} 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>} 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> </CardHeader>
{image ? ( {image ? (
<CardMedia> <CardMedia image={image}>
<Img fileName={image} /> <Img fileName={image} />
</CardMedia>) : ''} </CardMedia>) : ''}
<CardText style={this.styles.postBody}> <CardContent className={classes.postBody}>
<Linkify properties={{target: '_blank', style: {color: 'blue'}}}> <Linkify properties={{ target: '_blank', style: { color: 'blue' } }}>
{reactStringReplace(body,/#(\w+)/g, (match: string, i: string) => ( {reactStringReplace(body, /#(\w+)/g, (match: string, i: string) => (
<NavLink <NavLink
style={{color: 'green'}} style={{ color: 'green' }}
key={match + i} key={match + i}
to={`/tag/${match}`} to={`/tag/${match}`}
onClick ={evt => { onClick={evt => {
evt.preventDefault() evt.preventDefault()
goTo!(`/tag/${match}`) goTo!(`/tag/${match}`)
setHomeTitle!(`#${match}`) setHomeTitle!(`#${match}`)
}} }}
> >
#{match} #{match}
</NavLink> </NavLink>
))} ))}
</Linkify> </Linkify>
</CardText> </CardContent>
<CardActions> <CardActions>
<div style={{ margin: '16px 8px', display: 'flex', justifyContent: 'space-between' }}> <div className={classes.vote}>
<div style={{ display: 'flex' }}> <IconButton
{/*<FloatingActionButton style={{ margin: "0 8px" }} zDepth={1} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: "36px", width: "36px" }} zDepth={1} secondary={false}>*/} className={classes.iconButton}
<div className='g__circle' onClick={this.handleVote}> onClick={this.handleVote}
aria-label='Love'>
<Checkbox <Checkbox
checkedIcon={<SvgFavorite style={{fill: '#4CAF50'}}/>} className={classes.iconButton}
uncheckedIcon={<SvgFavoriteBorder style={{fill: '#757575'}} />} checkedIcon={<SvgFavorite style={{ fill: '#4CAF50' }} />}
checked={this.props.currentUserVote} icon={<SvgFavoriteBorder style={{ fill: '#757575' }} />}
style={{transform: 'translate(6px, 6px)'}} checked={this.props.currentUserVote}
/> />
</div> <div className={classes.voteCounter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div>
<div style={this.styles.counter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div> </IconButton>
</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>
</div> </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> </CardActions>
<CommentGroup open={this.state.openComments} comments={commentList} ownerPostUserId={post.ownerUserId!} onToggleRequest={this.handleOpenComments} isPostOwner={this.props.isPostOwner!} disableComments={post.disableComments!} postId={post.id!} /> <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*/} {/* Copy link dialog*/}
<Dialog <Dialog
title='Share On' title='Share On'
modal={false}
open={this.state.shareOpen} open={this.state.shareOpen}
onRequestClose={this.handleCloseShare} onClose={this.handleCloseShare}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
contentStyle={this.styles.dialog}
autoDetectWindowHeight={false}
actionsContainerStyle={{ borderTop: '1px solid rgb(224, 224, 224)' }}
> >
{!this.state.openCopyLink <Paper className={classes.shareLinkPaper}>
? (<Paper > {!this.state.openCopyLink
<Menu> ? (<MenuList>
<MenuItem primaryText='Copy Link' leftIcon={<SvgLink />} onClick={this.handleCopyLink} /> <MenuItem onClick={this.handleCopyLink} >
</Menu> <ListItemIcon>
</Paper>) <SvgLink />
: <TextField fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} /> </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> </Dialog>
<PostWrite <PostWrite
open={this.state.openPostWrite} open={this.state.openPostWrite}
onRequestClose={this.handleClosePostWrite} onRequestClose={this.handleClosePostWrite}
edit={true} edit={true}
postModel= {post} postModel={post}
/> />
</Card> </Card>
@@ -380,10 +493,10 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
* @return {object} props of component * @return {object} props of component
*/ */
const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => { const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
const {post} = ownProps const { post } = ownProps
return { return {
vote: () => dispatch(voteActions.dbAddVote(post.id!,post.ownerUserId!)), vote: () => dispatch(voteActions.dbAddVote(post.id!, post.ownerUserId!)),
unvote: () => dispatch(voteActions.dbDeleteVote(post.id!, post.ownerUserId!)) , unvote: () => dispatch(voteActions.dbDeleteVote(post.id!, post.ownerUserId!)),
delete: (id: string) => dispatch(postActions.dbDeletePost(id)), delete: (id: string) => dispatch(postActions.dbDeletePost(id)),
toggleDisableComments: (status: boolean) => { toggleDisableComments: (status: boolean) => {
post.disableComments = status post.disableComments = status
@@ -391,11 +504,11 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
}, },
toggleSharingComments: (status: boolean) => { toggleSharingComments: (status: boolean) => {
post.disableSharing = status 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)), goTo: (url: string) => dispatch(push(url)),
setHomeTitle: (title: string) => dispatch(globalActions.setHeaderTitle(title || '')), 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 * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IPostComponentProps) => { const mapStateToProps = (state: any, ownProps: IPostComponentProps) => {
const {post, vote, authorize, comment} = state const { post, vote, authorize, comment } = state
const {uid} = authorize const { uid } = authorize
let currentUserVote = ownProps.post.votes ? ownProps.post.votes[uid] : false let currentUserVote = ownProps.post.votes ? ownProps.post.votes[uid] : false
const postModel = post.userPosts[ownProps.post.ownerUserId!][ownProps.post.id!] 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 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!] const commentList: { [commentId: string]: Comment } = comment.postComments[ownProps.post.id!]
return { return {
commentList, commentList,
avatar: state.user.info && state.user.info[ownProps.post.ownerUserId!] ? state.user.info[ownProps.post.ownerUserId!].avatar || '' : '', 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 // - 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 * @memberof IPostWriteComponentProps
*/ */
update?: (post: Post, callback: Function) => any 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 * 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 React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import PropTypes from 'prop-types' 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 Paper from 'material-ui/Paper'
import Dialog from 'material-ui/Dialog' import Dialog, {
import FlatButton from 'material-ui/FlatButton' DialogActions,
import RaisedButton from 'material-ui/RaisedButton' DialogContent,
import { grey400, grey800, darkBlack, lightBlack } from 'material-ui/styles/colors' 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 IconButton from 'material-ui/IconButton'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import MenuItem from 'material-ui/MenuItem' import Tooltip from 'material-ui/Tooltip'
import SvgRemoveImage from 'material-ui/svg-icons/content/remove-circle' import { MenuList, MenuItem } from 'material-ui/Menu'
import SvgCamera from 'material-ui/svg-icons/image/photo-camera' import SvgRemoveImage from 'material-ui-icons/removeCircle'
import IconMenu from 'material-ui/IconMenu' import SvgCamera from 'material-ui-icons/photoCamera'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert' 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 app components
import ImageGallery from 'components/imageGallery' import ImageGallery from 'components/imageGallery'
@@ -31,37 +49,72 @@ import { IPostWriteComponentProps } from './IPostWriteComponentProps'
import { IPostWriteComponentState } from './IPostWriteComponentState' import { IPostWriteComponentState } from './IPostWriteComponentState'
import { Post } from 'core/domain/posts' 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 // - Create PostWrite component class
export class PostWriteComponent extends Component<IPostWriteComponentProps,IPostWriteComponentState> { export class PostWriteComponent extends Component<IPostWriteComponentProps, IPostWriteComponentState> {
/** /**
* Component constructor * Component constructor
* @param {object} props is an object properties of component * @param {object} props is an object properties of component
*/ */
constructor (props: IPostWriteComponentProps) { constructor(props: IPostWriteComponentProps) {
super(props) super(props)
const {postModel} = props const { postModel } = props
// Default state // Default state
this.state = { this.state = {
/** /**
* Post text * 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 * 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 * 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 * If it's true gallery will be open
*/ */
galleryOpen: false, galleryOpen: false,
/**
* Whether menu is open
*/
menuOpen: false,
/** /**
* If it's true post button will be disabled * If it's true post button will be disabled
*/ */
@@ -143,13 +196,13 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
const { const {
id, id,
ownerAvatar, ownerAvatar,
ownerDisplayName, ownerDisplayName,
edit, edit,
onRequestClose, onRequestClose,
post, post,
update, update,
postModel postModel
} = this.props } = this.props
if (image === '' && postText.trim() === '') { if (image === '' && postText.trim() === '') {
this.setState({ 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 {event} evt is an event passed by change post text callback funciton
* @param {string} data is the post content which user writes * @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 }) this.setState({ postText: data })
if (data.length === 0 || data.trim() === '' || (this.props.edit && data.trim() === this.props.text)) { if (data.length === 0 || data.trim() === '' || (this.props.edit && data.trim() === this.props.text)) {
this.setState({ 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) { if (!nextProps.open) {
const {postModel} = this.props const { postModel } = this.props
this.setState({ this.setState({
/** /**
* Post text * 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 * 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 * 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 * If it's true gallery will be open
*/ */
galleryOpen: false, galleryOpen: false,
/** /**
* If it's true post button will be disabled * If it's true post button will be disabled
*/ */
disabledPost: true, 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, 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 disableSharing: this.props.edit && postModel ? postModel.disableSharing! : false
}) })
@@ -292,28 +364,44 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
* Reneder component DOM * Reneder component DOM
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { render() {
const iconButtonElement = ( const { classes } = this.props
<IconButton const { menuOpen } = this.state
touch={true}
tooltip='more'
tooltipPosition='bottom-left'
>
<MoreVertIcon color={grey400} />
</IconButton>
)
const rightIconMenu = ( const rightIconMenu = (
<IconMenu iconButtonElement={iconButtonElement}> <Manager>
<MenuItem onClick={this.handleToggleComments} style={{ fontSize: '14px' }}>{!this.state.disableComments ? 'Disable comments' : 'Enable comments'} </MenuItem> <Target>
<MenuItem onClick={this.handleToggleSharing} style={{ fontSize: '14px' }}>{!this.state.disableSharing ? 'Disable sharing' : 'Enable sharing'}</MenuItem> <Tooltip id='tooltip-icon' title='more' placement='bottom-start'>
</IconMenu> <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 = ( let author = (
<div> <div className={classes.author}>
<span style={{ <span style={{
fontSize: '14px', fontSize: '14px',
paddingRight: '10px', paddingRight: '10px',
@@ -321,7 +409,6 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
color: 'rgba(0,0,0,0.87)', color: 'rgba(0,0,0,0.87)',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
overflow: 'hidden', overflow: 'hidden',
paddingLeft: '50px',
lineHeight: '25px' lineHeight: '25px'
}}>{this.props.ownerDisplayName}</span><span style={{ }}>{this.props.ownerDisplayName}</span><span style={{
fontWeight: 100, fontWeight: 100,
@@ -330,32 +417,32 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
</div> </div>
) )
const writeActions = [ /**
<FlatButton * Provide post image
label='Cancel' */
primary={true} const loadImage = (this.state.image && this.state.image !== '')
keyboardFocused={false} ? (<div>
onTouchTap={this.props.onRequestClose} <div style={{ position: 'relative', overflowY: 'hidden', overflowX: 'auto' }}>
style={{ color: grey800 }} <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' }}>
<FlatButton <span onClick={this.handleRemoveImage} style={{
label={this.props.edit ? 'UPDATE' : 'POST'} position: 'absolute', width: '28px', backgroundColor: 'rgba(255, 255, 255, 0.22)',
primary={true} height: '28px', right: 12, top: 4, cursor: 'pointer', borderRadius: '50%',
keyboardFocused={false} display: 'flex', alignItems: 'center', justifyContent: 'center'
onTouchTap={this.handlePost} }}>
disabled={this.state.disabledPost} <SvgRemoveImage style={{ color: 'rgba(0, 0, 0, 0.53)' }} />
/> </span>
]
const galleryActions = [ <div style={{ display: 'inline-block', width: '100%', marginRight: '8px', transition: 'transform .25s' }}>
<FlatButton <li style={{ width: '100%', margin: 0, verticalAlign: 'bottom', position: 'static' }}>
label='Cancel' <Img fileName={this.state.image} style={{ width: '100%', height: 'auto' }} />
primary={true} </li>
keyboardFocused={false} </div>
onTouchTap={this.handleCloseGallery} </div>
style={{ color: grey800 }}
/> </ul>
] </div>
</div>) : ''
const styles = { const styles = {
dialog: { dialog: {
@@ -369,88 +456,94 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
<div style={this.props.style}> <div style={this.props.style}>
{this.props.children} {this.props.children}
<Dialog <Dialog
BackdropProps={{className: classes.backdrop} as any}
key={this.props.id || 0} key={this.props.id || 0}
actions={writeActions}
modal={false}
open={this.props.open} open={this.props.open}
contentStyle={styles.dialog} onClose={this.props.onRequestClose}
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)' }}
> >
<DialogContent
className={classes.root}
style={{paddingTop: 0}}
<ListItem >
disabled={true}
leftAvatar={postAvatar} <Card elevation={0}>
rightIconButton={rightIconMenu} <CardHeader
primaryText={author} title={author}
style={{ padding: '16px 4px 30px 16px' }} avatar={postAvatar}
/> action={rightIconMenu}
<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' }}> </CardHeader>
<TextField <CardContent>
value={this.state.postText} <div style={{ display: 'flex', flexDirection: 'column', flexGrow: 1, overflow: 'hidden' }}>
onChange={this.handleOnChange} <div style={{ position: 'relative', flexDirection: 'column', display: 'flex', flexGrow: 1, overflow: 'hidden', overflowY: 'auto', maxHeight: '300px' }}>
hintText='What is new with you?' <TextField
underlineShow={false} autoFocus
multiLine={true} value={this.state.postText}
rows={2} onChange={this.handleOnChange}
hintStyle={{ fontWeight: 200, fontSize: '14px' }} placeholder='What is new with you?'
textareaStyle={{ fontWeight: 200, fontSize: '14px' }} multiline
style={{ margin: '0 16px', flexShrink: 0, width: 'initial', flexGrow: 1 }} rows={2}
rowsMax={4}
style={{ fontWeight: 200, fontSize: '14px', margin: '0 16px', flexShrink: 0, width: 'initial', flexGrow: 1 }}
/> />
{(this.state.image && this.state.image !== '') {loadImage}
? (<div> </div>
<div style={{ position: 'relative', overflowY: 'hidden', overflowX: 'auto' }}> <div style={{ flexShrink: 0, boxFlex: 0, flexGrow: 0, maxHeight: '48px', width: '100%' }}>
<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={{ flexDirection: 'row', display: 'flex' }}>
<div style={{ display: 'flex', position: 'relative' }}> <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 onClick={this.handleRemoveImage} style={{ <span style={{ top: '15px', display: 'block', position: 'relative', cursor: 'pointer' }}>
position: 'absolute', width: '28px', backgroundColor: 'rgba(255, 255, 255, 0.22)', <SvgCamera style={{ color: 'grey' }} />
height: '28px', right: 12, top: 4, cursor: 'pointer', borderRadius: '50%', </span>
display: 'flex', alignItems: 'center', justifyContent: 'center' </div>
}}> </div>
<SvgRemoveImage hoverColor='rgba(0, 0, 0, 0.65)' style={{ color: 'rgba(0, 0, 0, 0.53)' }} />
</span>
<div style={{ display: 'inline-block', width: '100%', marginRight: '8px', transition: 'transform .25s' }}>
<li style={{ width: '100%', margin: 0, verticalAlign: 'bottom', position: 'static' }}>
<Img fileName={this.state.image} style={{ width: '100%', height: 'auto' }} />
</li>
</div>
</div>
</ul>
</div> </div>
</div>) : ''}
</div>
<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> </CardContent>
</div> </Card>
</div> </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>
<Dialog <Dialog
actions={galleryActions}
modal={false}
open={this.state.galleryOpen} open={this.state.galleryOpen}
contentStyle={styles.dialog} onClose={this.handleCloseGallery}
onRequestClose={this.handleCloseGallery}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
autoDetectWindowHeight={false}
> >
<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> </Dialog>
</div> </div>
@@ -486,4 +579,4 @@ const mapStateToProps = (state: any, ownProps: IPostWriteComponentProps) => {
} }
// - Connect component to redux store // - 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 { connect } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Dialog from 'material-ui/Dialog' import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
// - Import app components // - Import app components
import ProfileHeader from 'components/profileHeader' import ProfileHeader from 'components/profileHeader'

View File

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

View File

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

View File

@@ -13,4 +13,9 @@ export interface IResetPasswordComponentProps {
* @memberof IResetPasswordComponentProps * @memberof IResetPasswordComponentProps
*/ */
loginPage?: () => void 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 { push } from 'react-router-redux'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import { firebaseRef, firebaseAuth } from 'data/firebaseClient' import { withStyles } from 'material-ui/styles'
import Typography from 'material-ui/Typography'
// - Import actions // - Import actions
import * as authorizeActions from 'actions/authorizeActions' import * as authorizeActions from 'actions/authorizeActions'
import { IResetPasswordComponentProps } from './IResetPasswordComponentProps' import { IResetPasswordComponentProps } from './IResetPasswordComponentProps'
import { IResetPasswordComponentState } from './IResetPasswordComponentState' import { IResetPasswordComponentState } from './IResetPasswordComponentState'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
},
caption: {
marginTop: 30
}
})
/** /**
* Create component class * Create component class
* *
@@ -77,6 +89,8 @@ export class ResetPasswordComponent extends Component<IResetPasswordComponentPro
*/ */
render () { render () {
const {classes} = this.props
const paperStyle = { const paperStyle = {
minHeight: 370, minHeight: 370,
width: 450, width: 450,
@@ -98,7 +112,7 @@ export class ResetPasswordComponent extends Component<IResetPasswordComponentPro
}}>Green</h1> }}>Green</h1>
<div className='animate-bottom'> <div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} > <Paper style={paperStyle} elevation={1}>
<div style={{ padding: '48px 40px 36px' }}> <div style={{ padding: '48px 40px 36px' }}>
<div style={{ <div style={{
paddingLeft: '40px', paddingLeft: '40px',
@@ -116,24 +130,29 @@ export class ResetPasswordComponent extends Component<IResetPasswordComponentPro
</div> </div>
<TextField <TextField
className={classes.textField}
autoFocus
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.emailInputError} helperText={this.state.emailInputError}
error={this.state.emailInputError.trim() !== ''}
name='emailInput' name='emailInput'
floatingLabelStyle={{ fontSize: '15px' }} label='Email'
floatingLabelText='Email'
type='email' type='email'
/><br /> /><br />
<br /> <br />
<br /> <br />
<div className='settings__button-box'> <div className='settings__button-box'>
<div> <div>
<FlatButton label='Back' onClick={this.props.loginPage} /> <Button onClick={this.props.loginPage}> Back </Button>
</div> </div>
<div> <div>
<RaisedButton label='Reset password' primary={true} onClick={this.handleForm} /> <Button raised color='primary' onClick={this.handleForm}> Reset password </Button>
</div> </div>
</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> </div>
</Paper> </Paper>
</div> </div>
@@ -170,4 +189,4 @@ const mapStateToProps = (state: any, ownProps: IResetPasswordComponentProps) =>
} }
// - Connect component to redux store // - 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 * Hide feedback form
*/ */
hideFeedback: () => any hideFeedback?: () => any
/** /**
* The server request of send feedback * The server request of send feedback
*/ */
sendFeedbackRequest: ServerRequestModel sendFeedbackRequest?: ServerRequestModel
/** /**
* Current user profile * 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 Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import IconButton from 'material-ui/IconButton' import IconButton from 'material-ui/IconButton'
import SvgHappy from 'material-ui/svg-icons/image/tag-faces' import SvgHappy from 'material-ui-icons/tagFaces'
import SvgSad from 'material-ui/svg-icons/action/face' import SvgSad from 'material-ui-icons/face'
import SvgClose from 'material-ui/svg-icons/content/clear' import SvgClose from 'material-ui-icons/clear'
import RefreshIndicator from 'material-ui/RefreshIndicator' import { CircularProgress } from 'material-ui/Progress'
import Tooltip from 'material-ui/Tooltip'
// - Import app components // - Import app components
@@ -37,7 +38,7 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
* Component constructor * Component constructor
* @param {object} props is an object properties of component * @param {object} props is an object properties of component
*/ */
constructor(props: ISendFeedbackComponentProps) { constructor (props: ISendFeedbackComponentProps) {
super(props) super(props)
// Defaul state // Defaul state
@@ -75,44 +76,47 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state const { feedText } = this.state
return ( return (
<div> <div className='main-box'>
<TextField <TextField
hintText='What do you feel?' placeholder='What do you feel?'
multiLine={true} multiline
onChange={this.handleFeedText} onChange={this.handleFeedText}
rows={2} rows={2}
rowsMax={4} rowsMax={4}
/><br /> autoFocus
fullWidth
/>
<br />
<div className='buttons'> <div className='buttons'>
<Tooltip title='Sad' placement='bottom-start'>
<IconButton <IconButton
tooltip='sad' className='flaticon-sad-2 icon__svg'
iconClassName='flaticon-sad-2 icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Sad)} onClick={() => this.handleSendFeed(FeedType.Sad)}
> >
</IconButton> </IconButton>
</Tooltip>
<Tooltip title='Acceptable' placement='bottom-start'>
<IconButton <IconButton
tooltip='acceptable' className='flaticon-neutral icon__svg'
iconClassName='flaticon-neutral icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Acceptable)} onClick={() => this.handleSendFeed(FeedType.Acceptable)}
> >
</IconButton> </IconButton>
</Tooltip>
<Tooltip title='Happy' placement='bottom-start'>
<IconButton <IconButton
tooltip='happy' className='flaticon-happy-2 icon__svg'
iconClassName='flaticon-happy-2 icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Happy)} onClick={() => this.handleSendFeed(FeedType.Happy)}
> >
</IconButton> </IconButton>
</Tooltip>
<Tooltip title='Awesome' placement='bottom-start'>
<IconButton <IconButton
tooltip='awesome' className='flaticon-happy icon__svg'
iconClassName='flaticon-happy icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Awesome)} onClick={() => this.handleSendFeed(FeedType.Awesome)}
> >
</IconButton> </IconButton>
</Tooltip>
</div> </div>
</div >) </div >)
} }
@@ -123,13 +127,14 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
Your feedback is sending! Your feedback is sending!
</p> </p>
<div className='icon'> <div className='icon'>
<RefreshIndicator <CircularProgress
size={50} color='secondary'
left={70} size={50}
top={0} mode='determinate'
status='loading' value={25}
/> min={0}
max={50}
/>
</div> </div>
</div>) </div>)
} }
@@ -169,21 +174,21 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
* Reneder component DOM * Reneder component DOM
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render() { render () {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state const { feedText } = this.state
return ( return (
<div className='sendFeedback__content animate__up' style={{ display: (sendFeedbackStatus ? 'block' : 'none') }}> <div className='sendFeedback__content animate__up'>
<Paper className='paper' > <Paper className='paper' >
<div className='close'> <div className='close'>
<Tooltip title='Cancel' placement='bottom-start'>
<IconButton <IconButton
tooltip='cancel' onClick={() => hideFeedback!()}
tooltipPosition='bottom-left'
onClick={() => hideFeedback()}
> >
<SvgClose /> <SvgClose />
</IconButton> </IconButton>
</Tooltip >
</div> </div>
{this.getFeedbackForm()} {this.getFeedbackForm()}

View File

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

View File

@@ -30,7 +30,7 @@ export default class SidebarMainComponent extends Component<ISidebarMainComponen
render () { render () {
return ( return (
<div className='home__main' style={this.props.cstyle} > <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} {this.props.children}
</div> </div>
) )

View File

@@ -5,19 +5,24 @@ export interface ISignupComponentProps {
* *
* @memberof ISignupComponentState * @memberof ISignupComponentState
*/ */
showError: (message: string) => any showError?: (message: string) => any
/** /**
* Register user * Register user
* *
* @memberof ISignupComponentState * @memberof ISignupComponentState
*/ */
register: (data: any) => any register?: (data: any) => any
/** /**
* Login * Login
* *
* @memberof ISignupComponentState * @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 { NavLink, withRouter } from 'react-router-dom'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton' import RaisedButton from 'material-ui/Button'
import FlatButton from 'material-ui/FlatButton' import Button from 'material-ui/Button'
import { withStyles } from 'material-ui/styles'
// - Import actions // - Import actions
import * as authorizeActions from 'actions/authorizeActions' import * as authorizeActions from 'actions/authorizeActions'
@@ -19,6 +20,14 @@ import { ISignupComponentProps } from './ISignupComponentProps'
import { ISignupComponentState } from './ISignupComponentState' import { ISignupComponentState } from './ISignupComponentState'
import { UserRegisterModel } from 'models/users/userRegisterModel' import { UserRegisterModel } from 'models/users/userRegisterModel'
const styles = (theme: any) => ({
textField: {
minWidth: 280,
marginTop: 20
}
})
// - Create Signup component class // - Create Signup component class
export class SignupComponent extends Component<ISignupComponentProps,ISignupComponentState> { export class SignupComponent extends Component<ISignupComponentProps,ISignupComponentState> {
@@ -38,7 +47,6 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
passwordInputError: '', passwordInputError: '',
confirmInput: '', confirmInput: '',
confirmInputError: '' confirmInputError: ''
} }
// Binding function to `this` // Binding function to `this`
this.handleForm = this.handleForm.bind(this) this.handleForm = this.handleForm.bind(this)
@@ -145,7 +153,7 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
} }
if (!error) { if (!error) {
register({ register!({
email: emailInput, email: emailInput,
password: passwordInput, password: passwordInput,
fullName: fullNameInput fullName: fullNameInput
@@ -159,6 +167,8 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { render () {
const {classes} = this.props
const paperStyle = { const paperStyle = {
minHeight: 500, minHeight: 500,
width: 450, width: 450,
@@ -182,7 +192,7 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
}}>Green</h1> }}>Green</h1>
<div className='animate-bottom'> <div className='animate-bottom'>
<Paper style={paperStyle} zDepth={1} rounded={false} > <Paper style={paperStyle} elevation={1} >
<div style={{padding: '48px 40px 36px'}}> <div style={{padding: '48px 40px 36px'}}>
<div style={{paddingLeft: '40px', <div style={{paddingLeft: '40px',
paddingRight: '40px'}}> paddingRight: '40px'}}>
@@ -197,44 +207,49 @@ export class SignupComponent extends Component<ISignupComponentProps,ISignupComp
</div> </div>
<TextField <TextField
className={classes.textField}
autoFocus
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.fullNameInputError} helperText={this.state.fullNameInputError}
error={this.state.fullNameInputError.trim() !== ''}
name='fullNameInput' name='fullNameInput'
floatingLabelStyle={{fontSize: '15px'}} label='Full Name'
floatingLabelText='Full Name'
type='text' type='text'
/><br /> /><br />
<TextField <TextField
className={classes.textField}
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.emailInputError} helperText={this.state.emailInputError}
error={this.state.emailInputError.trim() !== ''}
name='emailInput' name='emailInput'
floatingLabelStyle={{fontSize: '15px'}} label='Email'
floatingLabelText='Email'
type='email' type='email'
/><br /> /><br />
<TextField <TextField
className={classes.textField}
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.passwordInputError } helperText={this.state.passwordInputError }
error={this.state.passwordInputError.trim() !== ''}
name='passwordInput' name='passwordInput'
floatingLabelStyle={{fontSize: '15px'}} label='Password'
floatingLabelText='Password'
type='password' type='password'
/><br /> /><br />
<TextField <TextField
className={classes.textField}
onChange={this.handleInputChange} onChange={this.handleInputChange}
errorText={this.state.confirmInputError } helperText={this.state.confirmInputError }
error={this.state.confirmInputError.trim() !== ''}
name='confirmInput' name='confirmInput'
floatingLabelStyle={{fontSize: '15px'}} label='Confirm Password'
floatingLabelText='Confirm Password'
type='password' type='password'
/><br /> /><br />
<br /> <br />
<div className='signup__button-box'> <div className='signup__button-box'>
<div> <div>
<FlatButton label='Login' onClick={this.props.loginPage} /> <Button onClick={this.props.loginPage}> Login </Button>
</div> </div>
<div> <div>
<RaisedButton label='Create' primary={true} onClick={this.handleForm}/> <Button raised color='primary' onClick={this.handleForm}> Create </Button>
</div> </div>
</div> </div>
@@ -281,4 +296,4 @@ const mapStateToProps = (state: any,ownProps: ISignupComponentProps) => {
} }
// - Connect component to redux store // - 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 { withRouter } from 'react-router-dom'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card' import Button from 'material-ui/Button'
import FlatButton from 'material-ui/FlatButton' import { grey,teal } from 'material-ui/colors'
import { grey400, grey800, darkBlack, lightBlack,tealA400 } from 'material-ui/styles/colors' import SvgCamera from 'material-ui-icons/photoCamera'
import CircularProgress from 'material-ui/CircularProgress'
import SvgCamera from 'material-ui/svg-icons/image/photo-camera'
import Paper from 'material-ui/Paper' 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 InfiniteScroll from 'react-infinite-scroller'
// - Import app components // - Import app components
@@ -50,11 +48,10 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
styles = { styles = {
postWritePrimaryText : { postWritePrimaryText : {
color: grey400, color: grey[400],
cursor: 'text' cursor: 'text'
}, },
postWtireItem : { postWtireItem : {
padding: '7px 0px',
fontWeight: '200' fontWeight: '200'
} }
} }
@@ -220,15 +217,18 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
<div className='grid-cell animate-top' style= {{maxWidth: '530px', minWidth: '280px'}}> <div className='grid-cell animate-top' style= {{maxWidth: '530px', minWidth: '280px'}}>
{displayWriting && !tag {displayWriting && !tag
? (<PostWriteComponent open={this.state.openPostWrite} onRequestClose={this.handleClosePostWrite} edit={false} > ? (<PostWriteComponent open={this.state.openPostWrite} onRequestClose={this.handleClosePostWrite} edit={false} >
<Paper zDepth={2} style={{ height: '68px', width: '100%' }}> <Paper elevation={2}>
<ListItem <ListItem button
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 />}
style={this.styles.postWtireItem as any} 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> </Paper>
<div style={{ height: '16px' }}></div> <div style={{ height: '16px' }}></div>

View File

@@ -19,5 +19,5 @@ export interface IUserAvatarComponentProps {
/** /**
* Trigger on touch tap * 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 * 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 * @return {react element} return the DOM which rendered by component
*/ */
render () { render () {
let { fileName, fullName, style, size, onTouchTap } = this.props let { fileName, fullName, style, size, onClick } = this.props
return ( return (
<div style={{display: 'inherit'}}> <div style={{display: 'inherit'}}>
{(fileName && fileName !== '' && fileName !== 'noImage' ) {(fileName && fileName !== '' && fileName !== 'noImage' )
? ( <Avatar backgroundColor='#ffffff' src={fileName} size={size || 36} style={style} onTouchTap={onTouchTap} />) ? ( <Avatar src={fileName ? fileName : ' '} style={{...style, backgroundColor: '#ffffff', width: size || 36, height: size || 36}} onClick={onClick} />)
: (<Avatar backgroundColor='#00bcd4' size={size || 36} style={style} onTouchTap={onTouchTap}>{fullName ? fullName.slice(0, 1) : ''}</Avatar>) } : (<Avatar style={{...style, backgroundColor: '#00bcd4', width: size || 36, height: size || 36}} onClick={onClick}>{fullName ? fullName.slice(0, 1) : ''}</Avatar>) }
</div> </div>
) )
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,11 +3,9 @@ import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader' import { AppContainer } from 'react-hot-loader'
import injectTapEventPlugin from 'react-tap-event-plugin' import injectTapEventPlugin from 'react-tap-event-plugin'
import { cyan500 } from 'material-ui/styles/colors' import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles'
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import getMuiTheme from 'material-ui/styles/getMuiTheme'
import 'reflect-metadata' import 'reflect-metadata'
import 'typeface-roboto'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import store, { history } from 'store/configureStore' import store, { history } from 'store/configureStore'
@@ -21,31 +19,16 @@ import Master from 'components/master'
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
store.subscribe(() => { }) store.subscribe(() => { })
// Needed for onTouchTap // Needed for onClick
// http://stackoverflow.com/a/34015469/988941 // http://stackoverflow.com/a/34015469/988941
try { injectTapEventPlugin() } catch (e) { } try { injectTapEventPlugin() } catch (e) { }
// This replaces the textColor value on the palette const theme = createMuiTheme({
// 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({
palette: { palette: {
primary1Color: 'rgb(199, 212, 0)' primary: { main: '#00b1b3' },
}, secondary: { main: '#4d545d' }
// 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
} }
}) })
// App css // App css
import 'applicationStyles' import 'applicationStyles'
@@ -60,7 +43,7 @@ const render = (Component: any) => {
<AppContainer warnings={false}> <AppContainer warnings={false}>
<Provider store={store}> <Provider store={store}>
<ConnectedRouter history={history}> <ConnectedRouter history={history}>
<MuiThemeProvider muiTheme={muiTheme}> <MuiThemeProvider theme={theme}>
<Component /> <Component />
</MuiThemeProvider> </MuiThemeProvider>
</ConnectedRouter> </ConnectedRouter>

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
// - Import react components // - Import react components
import React, { Component } from 'react' import React, { Component } from 'react'
import { grey400, grey800, darkBlack, lightBlack,tealA400 } from 'material-ui/styles/colors' import { teal } from 'material-ui/colors'
import CircularProgress from 'material-ui/CircularProgress' import { CircularProgress } from 'material-ui/Progress'
/** /**
* Create component class * Create component class
@@ -14,7 +14,7 @@ export default class LoadMoreProgressComponent extends Component<{},{}> {
*/ */
render () { render () {
return ( 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 * @memberof CircleState
*/ */
loaded: boolean = false 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: case CircleActionType.UPDATE_CIRCLE:
return { return {
...state, ...state,
openSetting: {
...state.openSetting,
[payload.circle.id]: false
},
circleList: { circleList: {
...state.circleList, ...state.circleList,
[payload.circle.id]: { [payload.circle.id]: {
...payload.circle, ...payload.circle
openCircleSettings: false
} }
} }
} }
@@ -156,24 +159,18 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
case CircleActionType.CLOSE_CIRCLE_SETTINGS: case CircleActionType.CLOSE_CIRCLE_SETTINGS:
return { return {
...state, ...state,
circleList: { openSetting: {
...state.circleList, ...state.openSetting,
[payload.circleId]: { [payload.circleId]: false
...state.circleList[payload.circleId],
openCircleSettings: false
}
} }
} }
case CircleActionType.OPEN_CIRCLE_SETTINGS: case CircleActionType.OPEN_CIRCLE_SETTINGS:
return { return {
...state, ...state,
circleList: { openSetting: {
...state.circleList, ...state.openSetting,
[payload.circleId]: { [payload.circleId]: true
...state.circleList[payload.circleId],
openCircleSettings: true
}
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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