This repository has been archived on 2025-09-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
resolver/src/components/userBox/UserBoxComponent.tsx

455 lines
15 KiB
TypeScript

// - Import react components
import React, { Component } from 'react'
import moment from 'moment/moment'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { push } from 'react-router-redux'
import Paper from 'material-ui/Paper'
import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button'
import Menu, { MenuItem } from 'material-ui/Menu'
import Checkbox from 'material-ui/Checkbox'
import TextField from 'material-ui/TextField'
import Tooltip from 'material-ui/Tooltip'
import { withStyles } from 'material-ui/styles'
import List, { ListItem, ListItemSecondaryAction, ListItemText } from 'material-ui/List'
import Divider from 'material-ui/Divider'
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
withMobileDialog
} from 'material-ui/Dialog'
import SvgAdd from 'material-ui-icons/Add'
import IconButton from 'material-ui/IconButton'
import { grey } from 'material-ui/colors'
// - Import app components
import UserAvatar from 'components/userAvatar'
// - Import API
import StringAPI from 'api/StringAPI'
// - Import actions
import * as circleActions from 'actions/circleActions'
import { IUserBoxComponentProps } from './IUserBoxComponentProps'
import { IUserBoxComponentState } from './IUserBoxComponentState'
import { User } from 'core/domain/users'
import { UserTie, Circle } from 'core/domain/circles'
import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { ServerRequestModel } from 'models/server'
const styles = (theme: any) => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper
},
dialogContent: {
paddingTop: '5px',
padding: '0px 5px 5px 5px'
},
circleName: {
fontSize: '1rem'
},
space: {
height: 20
}
})
/**
* Create component class
*/
export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBoxComponentState> {
/**
* Fields
*/
static propTypes = {
/**
* User identifier
*/
userId: PropTypes.string,
/**
* User information
*/
user: PropTypes.object
}
styles = {
paper: {
height: 254,
width: 243,
margin: 10,
textAlign: 'center',
maxWidth: '257px'
},
followButton: {
position: 'absolute',
bottom: '30px',
left: 0,
right: 0
},
dialog: {
width: '',
maxWidth: '280px',
borderRadius: '4px'
}
}
selectedCircles: string[]
/**
* Component constructor
* @param {object} props is an object properties of component
*/
constructor (props: IUserBoxComponentProps) {
super(props)
const { userBelongCircles, circles, userId } = this.props
// Defaul state
this.state = {
/**
* The value of circle input
*/
circleName: ``,
/**
* It will be true if the text field for adding group is empty
*/
disabledCreateCircle: true,
/**
* The button of add user in a circle is disabled {true} or not {false}
*/
disabledAddToCircle: true,
/**
* Whether current user changed the selected circles for referer user
*/
disabledDoneCircles: true
}
this.selectedCircles = userBelongCircles!.slice()
// Binding functions to `this`
this.handleChangeName = this.handleChangeName.bind(this)
this.onCreateCircle = this.onCreateCircle.bind(this)
this.handleDoneAddCircle = this.handleDoneAddCircle.bind(this)
this.circleList = this.circleList.bind(this)
}
/**
* Handle follow user
*/
handleDoneAddCircle = () => {
const { userId, user, addUserToCircle, selectedCircles, deleteFollowingUser } = this.props
const { avatar, fullName } = user
const { disabledDoneCircles } = this.state
if (!disabledDoneCircles) {
if (selectedCircles!.length > 0) {
addUserToCircle!(selectedCircles!, { avatar, userId, fullName })
} else {
deleteFollowingUser!(userId)
}
}
}
/**
* Handle follow user
*/
onFollowUser = (event: any) => {
// This prevents ghost click
event.preventDefault()
const { isFollowed, followUser, followingCircleId, userId, user, followRequest } = this.props
if (followRequest && followRequest.status === ServerRequestStatusType.Sent) {
return
}
const { avatar, fullName } = user
if (!isFollowed) {
followUser!(followingCircleId!, { avatar, userId, fullName })
} else {
this.onRequestOpenAddCircle()
}
}
/**
* Handle request close for add circle box
*/
onRequestCloseAddCircle = () => {
const { setSelectedCircles, userId, userBelongCircles, closeSelectCircles } = this.props
setSelectedCircles!(userId, userBelongCircles!)
closeSelectCircles!(userId)
this.setState({
circleName: ``,
disabledCreateCircle: true,
disabledAddToCircle: true,
disabledDoneCircles: true
})
}
/**
* Handle request open for add circle box
*/
onRequestOpenAddCircle = () => {
const { openSelectCircles, userId } = this.props
openSelectCircles!(userId)
}
/**
* Create a new circle
*/
onCreateCircle = () => {
const { circleName } = this.state
if (circleName && circleName.trim() !== '') {
this.props.createCircle!(this.state.circleName)
this.setState({
circleName: '',
disabledCreateCircle: true
})
}
}
/**
* Handle change group name input to the state
*/
handleChangeName = (event: any) => {
this.setState({
circleName: event.target.value,
disabledCreateCircle: (event.target.value === undefined || event.target.value.trim() === '')
})
}
handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => {
const { userBelongCircles, circles, setSelectedCircles, selectedCircles, userId } = this.props
let newSelectedCircles = selectedCircles!.slice()
if (isInputChecked) {
newSelectedCircles = [
...selectedCircles!,
circleId
]
} else {
const circleIndex = selectedCircles!.indexOf(circleId)
newSelectedCircles.splice(circleIndex, 1)
}
setSelectedCircles!(userId, newSelectedCircles)
this.setState({
disabledDoneCircles: !this.selectedCircleChange(newSelectedCircles)
})
}
/**
* Create a circle list of user which belong to
*/
circleList = () => {
let { circles, userId, userBelongCircles, selectedCircles, classes } = this.props
if (circles) {
const parsedDate = Object.keys(circles).map((circleId, index) => {
let isBelong = selectedCircles ? selectedCircles!.indexOf(circleId) > -1 : false
// Create checkbox for selected/unselected circle
return (
<ListItem key={`${circleId}-${userId}`} dense className={classes.listItem}>
<ListItemText className={classes.circleName} primary={circles![circleId].name} />
<ListItemSecondaryAction>
<Checkbox
onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId)}
checked={isBelong}
/>
</ListItemSecondaryAction>
</ListItem>)
})
return parsedDate
}
}
/**
* Check if the the selected circles changed
*/
selectedCircleChange = (selectedCircles: string[]) => {
let isChanged = false
const { userBelongCircles, circles } = this.props
if (selectedCircles.length === userBelongCircles!.length) {
for (let circleIndex: number = 0; circleIndex < selectedCircles.length; circleIndex++) {
const selectedCircleId = selectedCircles[circleIndex]
if (!(userBelongCircles!.indexOf(selectedCircleId) > -1)) {
isChanged = true
break
}
}
} else {
isChanged = true
}
return isChanged
}
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render () {
const { disabledDoneCircles } = this.state
const { isFollowed, followRequest, userId, isSelecteCirclesOpen, addToCircleRequest, deleteFollowingUserRequest, classes } = this.props
return (
<Paper key={userId} style={this.styles.paper} elevation={1} className='grid-cell'>
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-start',
height: '100%',
position: 'relative',
paddingTop: 20
}}>
<div onClick={() => this.props.goTo!(`/${this.props.userId}`)} style={{ cursor: 'pointer' }}>
<UserAvatar
fullName={this.props.fullName!}
fileName={this.props.avatar!}
size={90}
/>
</div>
<div onClick={() => this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{ cursor: 'pointer' }}>
<div>
{this.props.user.fullName}
</div>
</div>
<div style={this.styles.followButton as any}>
<Button
color='primary'
onClick={this.onFollowUser}
disabled={
(followRequest ? followRequest.status === ServerRequestStatusType.Sent : false) ||
(deleteFollowingUserRequest ? deleteFollowingUserRequest.status === ServerRequestStatusType.Sent : false)
}
>
{!isFollowed ? 'Follow'
: (this.props.belongCirclesCount! > 1 ? `${this.props.belongCirclesCount} Circles` : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : 'Follow'))}
</Button>
</div>
</div>
<Dialog
key={this.props.userId || 0}
open={isSelecteCirclesOpen === true}
onClose={this.onRequestCloseAddCircle}
>
<DialogContent className={classes.dialogContent}>
<List>
{this.circleList()}
<div className={classes.space}></div>
<Divider />
<ListItem key={`'circleName'-${userId}`} dense className={classes.listItem}>
<ListItemText primary={
<TextField
autoFocus
placeholder='Group name'
onChange={this.handleChangeName}
value={this.state.circleName}
/>
} />
<ListItemSecondaryAction>
<IconButton onClick={this.onCreateCircle} disabled={this.state.disabledCreateCircle}>
<Tooltip title='Create circle'>
<SvgAdd />
</Tooltip>
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</DialogContent>
<DialogActions>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
onClick={this.onRequestCloseAddCircle}
style={{ color: grey[800] }}
>
Cancel
</Button>
<Button
color='primary'
disableFocusRipple={true}
disableRipple={true}
disabled={disabledDoneCircles || (addToCircleRequest ? addToCircleRequest!.status === ServerRequestStatusType.Sent : false)}
onClick={this.handleDoneAddCircle}
>
Done
</Button>
</DialogActions>
</Dialog>
</Paper>
)
}
}
/**
* Map dispatch to props
* @param {func} dispatch is the function to dispatch action to reducers
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps) => {
return {
createCircle: (name: string) => dispatch(circleActions.dbAddCircle(name)),
addUserToCircle: (circleIds: string[], user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)),
followUser: (circleId: string, userFollowing: UserTie) => dispatch(circleActions.dbFollowUser(circleId, userFollowing)),
deleteFollowingUser: (followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)),
setSelectedCircles: (userId: string, circleList: string[]) => dispatch(circleActions.setSelectedCircles(userId, circleList)),
removeSelectedCircles: (userId: string, circleList: string[]) => dispatch(circleActions.removeSelectedCircles(userId)),
openSelectCircles: (userId: string) => dispatch(circleActions.openSelectCircleBox(userId)),
closeSelectCircles: (userId: string) => dispatch(circleActions.closeSelectCircleBox(userId)),
goTo: (url: string) => dispatch(push(url))
}
}
/**
* Map state to props
* @param {object} state is the obeject from redux store
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => {
const { circle, authorize, server } = state
const { uid } = authorize
const { request } = server
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : []
const isFollowed = userBelongCircles.length > 0
const followingCircleId = circles ? Object.keys(circles)
.filter((circleId) => circles[circleId].isSystem && circles[circleId].name === `Following`)[0] : ''
const followRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)] : null
const addToCircleRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, ownProps.userId)] : null
const deleteFollowingUserRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, ownProps.userId)] : null
const selectedCircles = circle.selectedCircles ? circle.selectedCircles[ownProps.userId] : []
const isSelecteCirclesOpen = circle.openSelecteCircles ? circle.openSelecteCircles[ownProps.userId] : []
return {
isSelecteCirclesOpen,
isFollowed,
selectedCircles,
circles,
followingCircleId,
userBelongCircles,
followRequest,
belongCirclesCount: userBelongCircles.length || 0,
firstBelongCircle: userBelongCircles ? (circles ? circles[userBelongCircles[0]] : {}) : {},
avatar: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].avatar || '' : '',
fullName: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].fullName || '' : ''
}
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(UserBoxComponent as any) as any)