[Enhancement] Apply immutable js. (#49)

This commit is contained in:
Qolzam
2018-04-04 10:33:15 +07:00
parent b94d2a0124
commit 9cd2672395
98 changed files with 1294 additions and 1633 deletions

View File

@@ -18,9 +18,11 @@
"author": "Amir Movahedi", "author": "Amir Movahedi",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/react-helmet": "^5.0.5",
"@types/redux-devtools": "^3.0.43", "@types/redux-devtools": "^3.0.43",
"@types/redux-devtools-dock-monitor": "^1.1.32", "@types/redux-devtools-dock-monitor": "^1.1.32",
"@types/redux-devtools-log-monitor": "^1.0.33", "@types/redux-devtools-log-monitor": "^1.0.33",
"@types/redux-immutable": "^3.0.38",
"amazon-cognito-identity-js": "^1.21.0", "amazon-cognito-identity-js": "^1.21.0",
"aws-sdk": "^2.132.0", "aws-sdk": "^2.132.0",
"axios": "^0.16.2", "axios": "^0.16.2",
@@ -29,6 +31,7 @@
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"faker": "^4.1.0", "faker": "^4.1.0",
"firebase": "^4.11.0", "firebase": "^4.11.0",
"immutable": "^3.8.2",
"install": "^0.10.2", "install": "^0.10.2",
"inversify": "^4.6.0", "inversify": "^4.6.0",
"jss-rtl": "^0.2.1", "jss-rtl": "^0.2.1",
@@ -48,9 +51,11 @@
"react-day-picker": "^7.0.7", "react-day-picker": "^7.0.7",
"react-dom": "^16.2.0", "react-dom": "^16.2.0",
"react-event-listener": "^0.5.1", "react-event-listener": "^0.5.1",
"react-helmet": "^5.2.0",
"react-infinite-scroller": "^1.1.2", "react-infinite-scroller": "^1.1.2",
"react-linkify": "^0.2.1", "react-linkify": "^0.2.1",
"react-localize-redux": "^2.15.1", "react-loadable": "^5.3.1",
"react-localize-redux": "^2.16.0",
"react-parallax": "^1.6.1", "react-parallax": "^1.6.1",
"react-redux": "^5.0.6", "react-redux": "^5.0.6",
"react-router": "^4.1.1 ", "react-router": "^4.1.1 ",
@@ -62,10 +67,12 @@
"react-tap-event-plugin": "^3.0.2", "react-tap-event-plugin": "^3.0.2",
"redux": "^3.7.2", "redux": "^3.7.2",
"redux-actions": "^2.0.3", "redux-actions": "^2.0.3",
"redux-saga": "^0.16.0",
"redux-devtools": "^3.4.1", "redux-devtools": "^3.4.1",
"redux-devtools-dock-monitor": "^1.1.3", "redux-devtools-dock-monitor": "^1.1.3",
"redux-devtools-extension": "^2.13.2",
"redux-devtools-log-monitor": "^1.4.0", "redux-devtools-log-monitor": "^1.4.0",
"redux-immutable": "^4.0.0",
"redux-saga": "^0.16.0",
"redux-thunk": "^2.2.0", "redux-thunk": "^2.2.0",
"reflect-metadata": "^0.1.10", "reflect-metadata": "^0.1.10",
"save": "^2.3.0", "save": "^2.3.0",

View File

@@ -24,7 +24,8 @@ const sortCommentsByDate = (sortedObjects: comments) => {
return _.fromPairs(_.toPairs(sortedObjects) return _.fromPairs(_.toPairs(sortedObjects)
.sort((a: any, b: any) => parseInt(b[1].creationDate, 10) - parseInt(a[1].creationDate, 10)).slice(0, 3)) .sort((a: any, b: any) => parseInt(b[1].creationDate, 10) - parseInt(a[1].creationDate, 10)).slice(0, 3))
} }
return sortedObjects
} }
export default { export default {

View File

@@ -4,11 +4,14 @@ import * as moment from 'moment/moment'
* @param title log title * @param title log title
* @param data log data object * @param data log data object
*/ */
const logger = (title: string, data: any) => { const logger = (title: string, ...data: any[]) => {
const randomColor = getRandomColor() const randomColor = getRandomColor()
console.log(`\n\n%c ======= ${title} ======= %c${moment().format('HH:mm:ss SSS')} \n`, `color:${randomColor};font-size:15` window['console']['log'](`\n\n%c ======= ${title} ======= %c${moment().format('HH:mm:ss SSS')} \n`, `color:${randomColor};font-size:15`
, `color:${getRandomColor()};font-size:15`, data,`\n\n =========================================`) , `color:${getRandomColor()};font-size:15`)
window['console']['log'](``)
window['console']['log'](` `, data)
window['console']['log'](`\n =========================================`)
} }
@@ -31,8 +34,11 @@ const updateObject = (oldObject: any, updatedProperties: any) => {
} }
} }
const getStateSlice = (state: any) => state.toJS()['locale']
export default { export default {
logger, logger,
getRandomColor, getRandomColor,
updateObject updateObject,
getStateSlice
} }

View File

@@ -1,3 +1,4 @@
import {List} from 'immutable'
// Get tags from post content // Get tags from post content
export const detectTags: (content: string, character: string) => string[] = (content: string, character: string) => { export const detectTags: (content: string, character: string) => string[] = (content: string, character: string) => {
@@ -27,3 +28,15 @@ export const sortObjectsDate = (objects: any) => {
return sortedObjects return sortedObjects
} }
export const sortImuObjectsDate = (objects: List<Map<string, any>>) => {
let sortedObjects = objects
// Sort posts with creation date
return sortedObjects.sort((a: any, b: any) => {
return parseInt(b.get('creationDate'),10) - parseInt(a.get('creationDate'),10)
})
// return sortedObjects
}

View File

@@ -2,6 +2,9 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import {Map, List as ImuList} from 'immutable'
// - Material UI
import List, { import List, {
ListItem, ListItem,
ListItemIcon, ListItemIcon,
@@ -119,7 +122,7 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
/** /**
* Circle name on change * Circle name on change
*/ */
circleName: this.props.circle.name, circleName: this.props.circle.get('name', ''),
/** /**
* Save operation will be disable if user doesn't meet requirement * Save operation will be disable if user doesn't meet requirement
*/ */
@@ -210,9 +213,9 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
let usersParsed: any = [] let usersParsed: any = []
if (usersOfCircle) { if (usersOfCircle) {
Object.keys(usersOfCircle).forEach((userId, index) => { usersOfCircle.forEach((user: Map<string, any>, userId) => {
const { fullName } = usersOfCircle[userId] const fullName = user.get('fullName')
let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : '' let avatar = user.get('avatar', '')
usersParsed.push( usersParsed.push(
<ListItem <ListItem
button button
@@ -303,9 +306,9 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
<ListItemIcon className={classes.icon}> <ListItemIcon className={classes.icon}>
<SvgGroup /> <SvgGroup />
</ListItemIcon> </ListItemIcon>
<ListItemText inset primary={<span style={this.styles}>{this.props.circle.name}</span>} /> <ListItemText inset primary={<span style={this.styles}>{this.props.circle.get('name')}</span>} />
<ListItemSecondaryAction> <ListItemSecondaryAction>
{circle.isSystem ? null : rightIconMenu} {circle.get('isSystem') ? null : rightIconMenu}
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
<Collapse component='li' in={this.state.open} timeout='auto' unmountOnExit> <Collapse component='li' in={this.state.open} timeout='auto' unmountOnExit>
@@ -363,27 +366,24 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICircleComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: ICircleComponentProps) => {
const { circle, authorize, server } = state const userTies: Map<string, any> = state.getIn(['circle', 'userTies'])
const { userTies } = circle const uid = state.getIn(['authorize', 'uid'])
const { uid } = state.authorize const circles: Map<string, Map<string, any>> = state.getIn(['circle', 'circleList'], {})
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const currentCircle: Map<string, any> = circles.get(ownProps.id, Map({}))
const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle const circleId = ownProps.circle.get('id')
const circleId = ownProps.circle.id! let usersOfCircle: Map<string, any> = Map({})
let usersOfCircle: { [userId: string]: UserTie } = {} userTies.forEach((userTie , userTieId) => {
Object.keys(userTies).forEach((userTieId) => { const theUserTie: Map<string, any> = userTie
const theUserTie = userTies[userTieId] as UserTie const circleList: ImuList<string> = theUserTie.getIn(['circleIdList'])
if (theUserTie.circleIdList!.indexOf(ownProps.id) > -1) { if ( circleList.indexOf(ownProps.id) > -1) {
usersOfCircle = { usersOfCircle = usersOfCircle.set(userTieId!, theUserTie)
...usersOfCircle,
[userTieId]: theUserTie
}
} }
}) })
return { return {
usersOfCircle, usersOfCircle,
openSetting: (state.circle && state.circle.openSetting && state.circle.openSetting[circleId]) ? state.circle.openSetting[circleId] : false, openSetting: state.getIn(['circle', 'openSetting', circleId], false),
userInfo: state.user.info userInfo: state.getIn(['user', 'info'])
} }
} }

View File

@@ -1,81 +1,57 @@
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
import { Profile } from 'core/domain/users' import { Profile } from 'core/domain/users'
import { Circle, UserTie } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
import {Map} from 'immutable'
export interface ICircleComponentProps { export interface ICircleComponentProps {
/** /**
* * Circle
*
* @type {Circle}
* @memberof ICircleComponentProps
*/ */
circle: Circle circle: Map<string, any>
/** /**
* Circle identifier * Circle identifier
*
* @type {string}
* @memberof ICircleComponentProps
*/ */
id: string id: string
/** /**
* User identifier * User identifier
*
* @type {string}
* @memberof ICircleComponentProps
*/ */
uid: string uid: string
/** /**
* * Update circle
*
* @type {Function}
* @memberof ICircleComponentProps
*/ */
updateCircle?: Function updateCircle?: Function
/** /**
* * Delete circle
*
* @type {Function}
* @memberof ICircleComponentProps
*/ */
deleteCircle?: Function deleteCircle?: Function
/** /**
* Users of current circle * Users of current circle
*/ */
usersOfCircle?: {[userId: string]: UserTie} usersOfCircle?: Map<string, any>
/** /**
* Close setting box of circle * Close setting box of circle
*
* @type {Function}
* @memberof ICircleComponentProps
*/ */
closeCircleSettings?: any closeCircleSettings?: any
/** /**
* Circle setting dialog is open {true} or not {false} * Circle setting dialog is open {true} or not {false}
*
* @type {Function}
* @memberof ICircleComponentProps
*/ */
openSetting?: boolean openSetting?: boolean
/** /**
* Change route location * Change route location
*
* @memberof ICircleComponentProps
*/ */
goTo?: (url: string) => void goTo?: (url: string) => void
/** /**
* Open setting box for a circle * Open setting box for a circle
*
* @memberof ICircleComponentProps
*/ */
openCircleSettings?: () => any openCircleSettings?: () => any

View File

@@ -8,6 +8,7 @@ import moment from 'moment/moment'
import Linkify from 'react-linkify' import Linkify from 'react-linkify'
import Popover from 'material-ui/Popover' import Popover from 'material-ui/Popover'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
@@ -285,8 +286,8 @@ export class CommentComponent extends Component<ICommentComponentProps, IComment
} }
componentWillMount () { componentWillMount () {
const { userId } = this.props.comment const { commentOwner } = this.props
if (!this.props.isCommentOwner && !this.props.info![userId!]) { if (!this.props.isCommentOwner && !commentOwner) {
this.props.getUserInfo!() this.props.getUserInfo!()
} }
} }
@@ -432,15 +433,16 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentComponentProps) =>
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: any) => { const mapStateToProps = (state: any, ownProps: ICommentComponentProps) => {
const { uid } = state.authorize const commentOwnerId = ownProps.comment.userId
const avatar = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].avatar || '' : '' const uid = state.getIn(['authorize', 'uid'])
const fullName = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].fullName || '' : '' const avatar = ownProps.comment.userAvatar
const fullName = ownProps.comment.userDisplayName
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
uid: uid, uid: uid,
isCommentOwner: (uid === ownProps.comment.userId), isCommentOwner: (uid === commentOwnerId),
info: state.user.info, commentOwner: state.getIn(['user', 'info', commentOwnerId]),
avatar, avatar,
fullName fullName
} }

View File

@@ -10,6 +10,11 @@ export interface ICommentComponentProps {
*/ */
comment: Comment comment: Comment
/**
* Comment owner
*/
commentOwner?: Profile
/** /**
* Open profile editor * Open profile editor
* *
@@ -63,14 +68,6 @@ export interface ICommentComponentProps {
*/ */
getUserInfo?: () => void getUserInfo?: () => void
/**
* User profile
*
* @type {{[userId: string]: Profile}}
* @memberof ICommentComponentProps
*/
info?: {[userId: string]: Profile}
/** /**
* User full name * User full name
* *

View File

@@ -6,6 +6,7 @@ import { NavLink } from 'react-router-dom'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import moment from 'moment/moment' import moment from 'moment/moment'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map } from 'immutable'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import Button from 'material-ui/Button' import Button from 'material-ui/Button'
@@ -30,11 +31,12 @@ 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'
import { ServerRequestModel } from 'models/server' import { ServerRequestModel } from 'models/server'
import StringAPI from 'api/StringAPI' import StringAPI from 'api/StringAPI'
import { ServerRequestType } from 'constants/serverRequestType' import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType' import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
import { Profile } from 'core/domain/users'
const styles = (theme: any) => ({ const styles = (theme: any) => ({
textField: { textField: {
@@ -210,8 +212,8 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
* @return {DOM} list of comments' DOM * @return {DOM} list of comments' DOM
*/ */
commentList = () => { commentList = () => {
const {classes} = this.props const {classes, postId} = this.props
let comments = this.props.commentSlides let comments = Map(this.props.commentSlides!).toJS()
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)))
@@ -229,10 +231,8 @@ 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 commentAvatar = comment.userAvatar
const commentFullName = comment.userDisplayName
const commentAvatar = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].avatar || '' : ''
const commentFullName = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].fullName || '' : ''
const commentBody = ( const commentBody = (
<div style={{ outline: 'none', flex: 'auto', flexGrow: 1 }}> <div style={{ outline: 'none', flex: 'auto', flexGrow: 1 }}>
@@ -274,8 +274,8 @@ 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, classes, postId, fullName, avatar, getCommentsRequest, open, commentSlides, translate } = this.props const { classes, postId, fullName, avatar, commentsRequestStatus, open, commentSlides, translate } = this.props
const comments: Map<string, Comment> = this.props.comments || Map({})
/** /**
* Comment list box * Comment list box
*/ */
@@ -314,20 +314,20 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
</div> </div>
) )
const showComments = ( comments && Object.keys(comments).length > 0 const showComments = ( !comments.isEmpty()
? ( ? (
<Paper elevation={0} style={open ? { display: 'block', padding: '0px 0px' } : { display: 'none', padding: '12px 16px' }}> <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} postId={postId}/> <CommentListComponent comments={comments!} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments} postId={postId}/>
</Paper>) </Paper>)
: '') : '')
const loadComments = (( getCommentsRequest === undefined || (getCommentsRequest && getCommentsRequest!.status !== ServerRequestStatusType.OK)) ? <LinearProgress style={this.styles.progressbar} variant='indeterminate' /> : showComments) const loadComments = ((commentsRequestStatus === ServerRequestStatusType.OK) || !comments.isEmpty() ? showComments : <LinearProgress style={this.styles.progressbar} variant='indeterminate' />)
/** /**
* Return Elements * Return Elements
*/ */
return ( return (
<div key={postId + '-comments'}> <div key={postId + '-comments-group'}>
<div style={commentSlides && Object.keys(commentSlides).length > 0 ? { display: 'block' } : { display: 'none' }}>
<Divider /> <Divider />
<div style={commentSlides && !commentSlides.isEmpty() ? { display: 'block' } : { display: 'none' }}>
<Paper elevation={0} className='animate-top' style={!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' }} >
@@ -339,11 +339,11 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
</div> </div>
</div> </div>
</Paper> </Paper>
</div>
{ {
open ? loadComments : '' open ? loadComments : ''
} }
</div>
{ {
(!this.props.disableComments && open ) (!this.props.disableComments && open )
? commentWriteBox ? commentWriteBox
@@ -377,19 +377,20 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: ICommentGroupComponentProps) => {
const { post, user, authorize, server } = state
const {request} = server
const { ownerPostUserId, postId } = ownProps const { ownerPostUserId, postId } = ownProps
const commentSlides = post.userPosts[ownerPostUserId] && post.userPosts[ownerPostUserId][postId] ? post.userPosts[ownerPostUserId][postId].comments : {} const uid = state.getIn(['authorize', 'uid'], 0)
const getCommentsRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CommentGetComments, postId)] : null const requestId = StringAPI.createServerRequestId(ServerRequestType.CommentGetComments, postId)
const commentsRequestStatus = state.getIn(['server', 'request', requestId])
const commentSlides = state.getIn(['post', 'userPosts', ownerPostUserId, postId, 'comments'])
const user = state.getIn(['user', 'info', uid])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
getCommentsRequest, commentsRequestStatus : commentsRequestStatus ? commentsRequestStatus.status : ServerRequestStatusType.NoAction,
commentSlides, commentSlides,
avatar: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].avatar || '' : '', avatar: user.avatar || '',
fullName: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].fullName || '' : '', fullName: user.fullName || '',
userInfo: user.info userInfo: state.getIn(['user', 'info'])
} }
} }

View File

@@ -1,6 +1,8 @@
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' import { ServerRequestModel } from 'models/server'
import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
import {Map} from 'immutable'
export interface ICommentGroupComponentProps { export interface ICommentGroupComponentProps {
/** /**
@@ -9,15 +11,12 @@ export interface ICommentGroupComponentProps {
* @type {{[commentId: string]: Comment}} * @type {{[commentId: string]: Comment}}
* @memberof ICommentGroupComponentProps * @memberof ICommentGroupComponentProps
*/ */
comments?: {[commentId: string]: Comment} comments?: Map<string, Comment>
/** /**
* Commnets show on slide preview * Commnets show on slide preview
*
* @type {{[commentId: string]: Comment}}
* @memberof ICommentGroupComponentProps
*/ */
commentSlides?: {[commentId: string]: Comment} commentSlides?: Map<string, Comment>
/** /**
* The post identifier which comment belong to * The post identifier which comment belong to
@@ -33,7 +32,7 @@ export interface ICommentGroupComponentProps {
* @type {{[userId: string]: Profile}} * @type {{[userId: string]: Profile}}
* @memberof ICommentGroupComponentProps * @memberof ICommentGroupComponentProps
*/ */
userInfo?: {[userId: string]: Profile} userInfo?: Map<string, Profile>
/** /**
* Comment group is open {true} or not {false} * Comment group is open {true} or not {false}
@@ -102,7 +101,7 @@ export interface ICommentGroupComponentProps {
/** /**
* Get post comments request payload * Get post comments request payload
*/ */
getCommentsRequest?: ServerRequestModel commentsRequestStatus?: ServerRequestStatusType
/** /**
* Styles * Styles

View File

@@ -2,6 +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 {Map} from 'immutable'
// - Material UI // - Material UI
import { withStyles } from 'material-ui/styles' import { withStyles } from 'material-ui/styles'
@@ -66,14 +67,15 @@ export class CommentListComponent extends Component<ICommentListComponentProps,
* @return {DOM} list of comments' DOM * @return {DOM} list of comments' DOM
*/ */
commentList = () => { commentList = () => {
let {comments, commentsEditorStatus} = this.props let comments = Map<string, Comment>(this.props.comments)
if (comments) { let commentsEditorStatus = Map<string, boolean>(this.props.commentsEditorStatus!)
if (!comments.isEmpty()) {
let parsedComments: Comment[] = [] let parsedComments: Comment[] = []
Object.keys(comments).forEach((commentId) => { comments.forEach((comment, commentId) => {
parsedComments.push({ parsedComments.push({
id: commentId, id: commentId,
...comments[commentId] ...Map(comment!).toJS()
}) })
}) })
let sortedComments = PostAPI.sortObjectsDate(parsedComments) let sortedComments = PostAPI.sortObjectsDate(parsedComments)
@@ -86,7 +88,7 @@ export class CommentListComponent extends Component<ICommentListComponentProps,
comment={comment} comment={comment}
isPostOwner={this.props.isPostOwner} isPostOwner={this.props.isPostOwner}
disableComments={this.props.disableComments} disableComments={this.props.disableComments}
editorStatus={(commentsEditorStatus![comment.id!]) || false} editorStatus={(commentsEditorStatus.get(comment.id!, false))}
/> />
) )
@@ -100,11 +102,11 @@ export class CommentListComponent extends Component<ICommentListComponentProps,
* @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 {classes, postId} = this.props
return ( return (
<List className={classes.list}> <List key={`comment-list-${postId}`} className={classes.list}>
{this.commentList()} {this.commentList()}
</List> </List>
@@ -130,9 +132,8 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentListComponentProps)
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ICommentListComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: ICommentListComponentProps) => {
const { comment } = state const commentsEditorStatus = state.getIn(['comment', 'editorStatus', ownProps.postId ], {})
const commentsEditorStatus: { [commentId: string]: Comment } = comment.editorStatus[ownProps.postId] || {}
return { return {
commentsEditorStatus commentsEditorStatus
} }

View File

@@ -1,4 +1,5 @@
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
import {Map} from 'immutable'
export interface ICommentListComponentProps { export interface ICommentListComponentProps {
@@ -8,7 +9,7 @@ export interface ICommentListComponentProps {
* @type {{[commentId: string]: Comment}} * @type {{[commentId: string]: Comment}}
* @memberof ICommentListComponentProps * @memberof ICommentListComponentProps
*/ */
comments: {[commentId: string]: Comment} comments: Map<string, Comment>
/** /**
* Comments editor status * Comments editor status

View File

@@ -10,6 +10,7 @@ import MomentLocaleUtils, {
formatDate, formatDate,
parseDate, parseDate,
} from 'react-day-picker/moment' } from 'react-day-picker/moment'
import {Map} from 'immutable'
import { grey } from 'material-ui/colors' import { grey } from 'material-ui/colors'
import IconButton from 'material-ui/IconButton' import IconButton from 'material-ui/IconButton'
@@ -294,9 +295,9 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,
*/ */
handleUpdate = () => { handleUpdate = () => {
const { fullNameInput, tagLineInput, avatar, banner, selectedBirthday, companyName, webUrl, twitterId } = this.state const { fullNameInput, tagLineInput, avatar, banner, selectedBirthday, companyName, webUrl, twitterId } = this.state
const { info } = this.props const { info, update } = this.props
if (this.state.fullNameInput.trim() === '') { if (fullNameInput.trim() === '') {
this.setState({ this.setState({
fullNameInputError: 'This field is required' fullNameInputError: 'This field is required'
}) })
@@ -305,7 +306,7 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,
fullNameInputError: '' fullNameInputError: ''
}) })
this.props.update!({ update!({
fullName: fullNameInput, fullName: fullNameInput,
tagLine: tagLineInput, tagLine: tagLineInput,
avatar: avatar, avatar: avatar,
@@ -562,13 +563,14 @@ const mapDispatchToProps = (dispatch: any, ownProps: IEditProfileComponentProps)
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IEditProfileComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IEditProfileComponentProps) => {
const uid = state.getIn(['authorize', 'uid'])
return { return {
currentLanguage: getActiveLanguage(state.locale).code, currentLanguage: getActiveLanguage(state.get('locale')).code,
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
open: state.user.openEditProfile, open: state.getIn(['user', 'openEditProfile'], false),
info: state.user.info[state.authorize.uid], info: state.getIn(['user', 'info', uid]),
avatarURL: state.imageGallery.imageURLList avatarURL: state.getIn(['imageGallery', 'imageURLList'])
} }
} }

View File

@@ -5,6 +5,7 @@ import PropTypes from 'prop-types'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import InfiniteScroll from 'react-infinite-scroller' import InfiniteScroll from 'react-infinite-scroller'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components // - Import app components
import UserBoxList from 'components/userBoxList' import UserBoxList from 'components/userBoxList'
@@ -16,6 +17,7 @@ import LoadMoreProgressComponent from 'layouts/loadMoreProgress'
import * as userActions from 'store/actions/userActions' import * as userActions from 'store/actions/userActions'
import { IFindPeopleComponentProps } from './IFindPeopleComponentProps' import { IFindPeopleComponentProps } from './IFindPeopleComponentProps'
import { IFindPeopleComponentState } from './IFindPeopleComponentState' import { IFindPeopleComponentState } from './IFindPeopleComponentState'
import { UserTie } from 'core/domain/circles/userTie'
/** /**
* Create component class * Create component class
@@ -50,7 +52,7 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
*/ */
render () { render () {
const {hasMorePeople, translate} = this.props const {hasMorePeople, translate} = this.props
const peopleInfo = Map<string, UserTie>(this.props.peopleInfo!)
return ( return (
<div> <div>
<InfiniteScroll <InfiniteScroll
@@ -63,11 +65,11 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
<div className='tracks'> <div className='tracks'>
{this.props.peopleInfo && Object.keys(this.props.peopleInfo).length !== 0 ? (<div> {peopleInfo && peopleInfo.count() > 0 ? (<div>
<div className='profile__title'> <div className='profile__title'>
{translate!('people.suggestionsForYouLabel')} {translate!('people.suggestionsForYouLabel')}
</div> </div>
<UserBoxList users={this.props.peopleInfo}/> <UserBoxList users={peopleInfo}/>
<div style={{ height: '24px' }}></div> <div style={{ height: '24px' }}></div>
</div>) : (<div className='g__title-center'> </div>) : (<div className='g__title-center'>
{translate!('people.nothingToShowLabel')} {translate!('people.nothingToShowLabel')}
@@ -98,11 +100,13 @@ const mapDispatchToProps = (dispatch: any, ownProps: IFindPeopleComponentProps)
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IFindPeopleComponentProps) => { const mapStateToProps = (state: any, ownProps: IFindPeopleComponentProps) => {
const {people, info} = state.user const people = state.getIn(['user', 'people'])
const hasMorePeople = state.getIn(['user', 'people', 'hasMoreData' ], true)
const info: Map<string, UserTie> = state.getIn(['user', 'info'])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
peopleInfo: info, peopleInfo: info,
hasMorePeople: people.hasMoreData hasMorePeople
} }
} }

View File

@@ -1,4 +1,5 @@
import { Profile } from 'core/domain/users/profile' import { Profile } from 'core/domain/users/profile'
import { UserTie } from 'core/domain/circles'
export interface IFindPeopleComponentProps { export interface IFindPeopleComponentProps {
@@ -11,11 +12,8 @@ export interface IFindPeopleComponentProps {
/** /**
* Users' profile * Users' profile
*
* @type {{[userId: string]: Profile}}
* @memberof IFindPeopleComponentProps
*/ */
peopleInfo?: {[userId: string]: Profile} peopleInfo?: Map<string, UserTie>
/** /**
* If there are more people {true} or not {false} * If there are more people {true} or not {false}

View File

@@ -3,6 +3,7 @@ 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 { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components // - Import app components
import UserBoxList from 'components/userBoxList' import UserBoxList from 'components/userBoxList'
@@ -46,13 +47,14 @@ export class FollowersComponent extends Component<IFollowersComponentProps,IFoll
*/ */
render () { render () {
const {translate} = this.props const {translate} = this.props
const followers = this.props.followers!
return ( return (
<div> <div>
{(this.props.followers && Object.keys(this.props.followers).length !== 0) ? (<div> {(followers && followers.keySeq().count() !== 0) ? (<div>
<div className='profile__title'> <div className='profile__title'>
{translate!('people.followersLabel')} {translate!('people.followersLabel')}
</div> </div>
<UserBoxList users={this.props.followers} /> <UserBoxList users={followers} />
<div style={{ height: '24px' }}></div> <div style={{ height: '24px' }}></div>
</div>) </div>)
: (<div className='g__title-center'> : (<div className='g__title-center'>
@@ -81,13 +83,13 @@ const mapDispatchToProps = (dispatch: any,ownProps: IFollowersComponentProps) =>
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any,ownProps: IFollowersComponentProps) => { const mapStateToProps = (state: Map<string, any>,ownProps: IFollowersComponentProps) => {
const {circle, authorize, server} = state
const { uid } = state.authorize const uid = state.getIn(['authorize', 'uid'], 0)
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: { [circleId: string]: Circle } = state.getIn(['circle', 'circleList'], {})
const followers = circle ? circle.userTieds : {} const followers = state.getIn(['circle', 'userTieds'], {})
return{ return{
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
followers followers
} }
} }

View File

@@ -1,14 +1,12 @@
import { UserTie } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
import {Map} from 'immutable'
export interface IFollowersComponentProps { export interface IFollowersComponentProps {
/** /**
* User followers info * User followers info
*
* @type {{[userId: string]: UserTie}}
* @memberof IFindPeopleComponentProps
*/ */
followers?: {[userId: string]: UserTie} followers?: Map<string, UserTie>
/** /**
* Translate to locale string * Translate to locale string

View File

@@ -3,6 +3,7 @@ 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 { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components // - Import app components
import UserBoxList from 'components/userBoxList' import UserBoxList from 'components/userBoxList'
@@ -46,13 +47,14 @@ export class FollowingComponent extends Component<IFollowingComponentProps,IFoll
*/ */
render () { render () {
const {translate} = this.props const {translate} = this.props
const followingUsers = Map(this.props.followingUsers!)
return ( return (
<div> <div>
{(this.props.followingUsers && Object.keys(this.props.followingUsers).length !== 0 ) ? (<div> {(followingUsers && followingUsers.keySeq().count() !== 0 ) ? (<div>
<div className='profile__title'> <div className='profile__title'>
{translate!('people.followingLabel')} {translate!('people.followingLabel')}
</div> </div>
<UserBoxList users={this.props.followingUsers} /> <UserBoxList users={followingUsers} />
<div style={{ height: '24px' }}></div> <div style={{ height: '24px' }}></div>
</div>) : (<div className='g__title-center'> </div>) : (<div className='g__title-center'>
@@ -81,13 +83,13 @@ const mapDispatchToProps = (dispatch: any,ownProp: IFollowingComponentProps) =>
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any,ownProps: IFollowingComponentProps) => { const mapStateToProps = (state: Map<string, any>,ownProps: IFollowingComponentProps) => {
const {circle, authorize, server} = state
const { uid } = state.authorize const uid = state.getIn(['authorize', 'uid'], 0)
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: { [circleId: string]: Circle } = state.getIn(['circle', 'circleList'], {})
const followingUsers = circle ? circle.userTies : {} const followingUsers = state.getIn(['circle', 'userTies'], {})
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
uid, uid,
circles, circles,
followingUsers followingUsers

View File

@@ -1,8 +1,9 @@
import { UserTie } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
import {Map} from 'immutable'
export interface IFollowingComponentProps { export interface IFollowingComponentProps {
followingUsers?: {[userId: string]: UserTie} followingUsers?: Map<string, UserTie>
/** /**
* Translate to locale string * Translate to locale string

View File

@@ -3,6 +3,7 @@ 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 classNames from 'classnames' import classNames from 'classnames'
import { Map } from 'immutable'
// - Material UI // - Material UI
import SvgDehaze from 'material-ui-icons/Dehaze' import SvgDehaze from 'material-ui-icons/Dehaze'
@@ -256,18 +257,20 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IHomeHeaderComponentPr
} }
// - Map state to props // - Map state to props
const mapStateToProps = (state: any, ownProps: IHomeHeaderComponentProps) => { const mapStateToProps = (state: Map<string,any>, ownProps: IHomeHeaderComponentProps) => {
let notifyCount = state.notify.userNotifies const uid = state.getIn(['authorize', 'uid'], 0)
? Object const userNotifies: Map<string, any> = state.getIn(['notify','userNotifies'])
.keys(state.notify.userNotifies) let notifyCount = userNotifies
.filter((key) => !state.notify.userNotifies[key].isSeen).length ? userNotifies
.filter((notification) => !notification.get('isSeen', false)).count()
: 0 : 0
const user = state.getIn(['user', 'info', uid], {})
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '', avatar: user.avatar || '',
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : '', fullName: user.fullName || '',
title: state.global.headerTitle, title: state.getIn(['global', 'headerTitle'], ''),
notifyCount notifyCount
} }
} }

View File

@@ -1,4 +1,5 @@
import { Image } from 'core/domain/imageGallery' import { Image } from 'core/domain/imageGallery'
import {Map, Collection, List} from 'immutable'
export interface IImageGalleryComponentProps { export interface IImageGalleryComponentProps {
@@ -33,11 +34,8 @@ export interface IImageGalleryComponentProps {
/** /**
* List of image in image gallery * List of image in image gallery
*
* @type {Image[]}
* @memberof IImageGalleryComponentProps
*/ */
images?: Image[] images?: List<Image>
/** /**
* Styles * Styles

View File

@@ -13,6 +13,7 @@ import { grey } from 'material-ui/colors'
import { withStyles } from 'material-ui/styles' import { withStyles } from 'material-ui/styles'
import uuid from 'uuid' import uuid from 'uuid'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import actions // - Import actions
import * as imageGalleryActions from 'store/actions/imageGalleryActions' import * as imageGalleryActions from 'store/actions/imageGalleryActions'
@@ -161,7 +162,6 @@ export class ImageGalleryComponent extends Component<IImageGalleryComponentProps
} }
imageList = () => { imageList = () => {
return this.props.images!.map((image: Image, index) => { return this.props.images!.map((image: Image, index) => {
return ( return (
@@ -254,11 +254,13 @@ const mapDispatchToProps = (dispatch: any, ownProps: IImageGalleryComponentProps
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any) => { const mapStateToProps = (state: Map<string, any>) => {
const uid = state.getIn(['authorize', 'uid'])
const currentUser = state.getIn(['user', 'info', uid])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
images: state.imageGallery.images, images: state.getIn(['imageGallery', 'images']),
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '' avatar: currentUser ? currentUser.avatar : ''
} }
} }

View File

@@ -5,6 +5,7 @@ import { connect } from 'react-redux'
import SvgImage from 'material-ui-icons/Image' import SvgImage from 'material-ui-icons/Image'
import { withStyles } from 'material-ui/styles' import { withStyles } from 'material-ui/styles'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map } from 'immutable'
// - Import app components // - Import app components
@@ -121,11 +122,11 @@ const mapDispatchToProps = (dispatch: any, ownProps: IImgComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IImgComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IImgComponentProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
avatarURL: state.imageGallery.imageURLList, avatarURL: state.getIn(['imageGallery', 'imageURLList']),
imageRequests: state.imageGallery.imageRequests imageRequests: state.getIn(['imageGallery', 'imageRequests'])
} }
} }

View File

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import SvgImage from 'material-ui-icons/Image' import SvgImage from 'material-ui-icons/Image'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components // - Import app components
@@ -153,9 +154,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IImgCoverComponentProps) =>
*/ */
const mapStateToProps = (state: any, ownProps: IImgCoverComponentProps) => { const mapStateToProps = (state: any, ownProps: IImgCoverComponentProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale'))
avatarURL: state.imageGallery.imageURLList,
imageRequests: state.imageGallery.imageRequests
} }
} }

View File

@@ -1,12 +1,5 @@
export interface IMasterLoadingComponentProps { export interface IMasterLoadingComponentProps {
error?: boolean
/** timedOut?: boolean
* Loading is active {true} or not {false} pastDelay?: boolean
*
* @type {boolean}
* @memberof IMasterLoadingComponentProps
*/
activeLoading: boolean
handleLoading: (status: boolean) => void
} }

View File

@@ -2,8 +2,11 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { CircularProgress } from 'material-ui/Progress' import { CircularProgress } from 'material-ui/Progress'
import Dialog from 'material-ui/Dialog' import Dialog from 'material-ui/Dialog'
import red from 'material-ui/colors/red'
import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps' import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps'
import { IMasterLoadingComponentState } from './IMasterLoadingComponentState' import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
import Grid from 'material-ui/Grid/Grid'
import { Typography } from 'material-ui'
// - Import app components // - Import app components
@@ -11,26 +14,78 @@ import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
export default class MasterLoadingComponent extends Component<IMasterLoadingComponentProps, IMasterLoadingComponentState> { export default class MasterLoadingComponent extends Component<IMasterLoadingComponentProps, IMasterLoadingComponentState> {
// Constructor // Constructor
constructor (props: IMasterLoadingComponentProps) { constructor(props: IMasterLoadingComponentProps) {
super(props) super(props)
// Binding functions to `this` // Binding functions to `this`
} }
loadProgress() {
const { error, timedOut, pastDelay } = this.props
if (error) {
return (
<Grid container>
<Grid item>
<CircularProgress style={{ color: red[500] }} size={50} />
</Grid>
<Grid item>
<Typography variant='title' color='primary' style={{ marginLeft: '15px' }} >
Unexpected Error Happened ...
</Typography>
</Grid>
</Grid>
)
} else if (timedOut) {
return (
<Grid container>
<Grid item>
<CircularProgress style={{ color: red[500] }} size={50} />
</Grid>
<Grid item>
<Typography variant='title' color='primary' style={{ marginLeft: '15px' }} >
It takes long time ...
</Typography>
</Grid>
</Grid>
)
} else if (pastDelay) {
return (
<Grid container>
<Grid item>
<CircularProgress size={50} />
</Grid>
<Grid item>
<Typography variant='title' color='primary' style={{ marginLeft: '15px' }} >
Loading...
</Typography>
</Grid>
</Grid>
)
} else {
return (
<Grid container>
<Grid item>
<CircularProgress size={50} />
</Grid>
<Grid item>
<Typography variant='title' color='primary' style={{ marginLeft: '15px' }} >
Loading...
</Typography>
</Grid>
</Grid>
)
}
}
// Render app DOM component // Render app DOM component
render () { render() {
const {activeLoading} = this.props
return ( return (
<div className='mLoading__loading' style={{ display: (activeLoading ? 'flex' : 'none') }}> <div className='mLoading__loading'>
<CircularProgress {
color='secondary' this.loadProgress()
size={50} }
variant='determinate'
value={25}
min={0}
max={50}
/>
</div> </div>
) )

View File

@@ -1,44 +1,30 @@
import { Profile } from 'core/domain/users' import { Profile } from 'core/domain/users'
import { Notification } from 'core/domain/notifications' import { Notification } from 'core/domain/notifications'
import {Map} from 'immutable'
export interface INotifyComponentProps { export interface INotifyComponentProps {
/** /**
* Notifications * Notifications
*
* @type {{[notificationId: string]: Notification}}
* @memberof INotifyComponentProps
*/ */
notifications?: {[notificationId: string]: Notification} notifications?: Map<string, any>
/** /**
* Users' profile * Users' profile
*
* @type {{[userId: string]: Profile}}
* @memberof INotifyComponentProps
*/ */
info?: {[userId: string]: Profile} info?: Map<string, Profile>
/** /**
* Close notification * Close notification
*
* @memberof INotifyComponentProps
*/ */
onRequestClose: () => void onRequestClose: () => void
/** /**
* User notifications popover is opem {true} or not {false} * User notifications popover is opem {true} or not {false}
*
* @type {boolean}
* @memberof INotifyComponentProps
*/ */
open: boolean open: boolean
/** /**
* Keep element * Keep element
*
* @type {*}
* @memberof INotifyComponentProps
*/ */
anchorEl?: any anchorEl?: any

View File

@@ -10,6 +10,7 @@ import { withStyles } from 'material-ui/styles'
import Typography from 'material-ui/Typography' import Typography from 'material-ui/Typography'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import List, { ListItem, ListItemSecondaryAction, ListItemText } from 'material-ui/List' import List, { ListItem, ListItemSecondaryAction, ListItemText } from 'material-ui/List'
import { Map } from 'immutable'
// - Import app components // - Import app components
import NotifyItem from 'components/notifyItem' import NotifyItem from 'components/notifyItem'
@@ -52,7 +53,8 @@ const styles = (theme: any) => ({
}, },
list: { list: {
maxHeight: 380, maxHeight: 380,
overflowY: 'auto' overflowY: 'auto',
width: '98%'
}, },
fullPageXs: { fullPageXs: {
@@ -106,20 +108,22 @@ export class NotifyComponent extends Component<INotifyComponentProps, INotifyCom
} }
notifyItemList = () => { notifyItemList = () => {
let { notifications, info, onRequestClose } = this.props let { info, onRequestClose } = this.props
let notifications: Map<string, Map<string, any>> = this.props.notifications!
let parsedDOM: any[] = [] let parsedDOM: any[] = []
if (notifications) { if (notifications) {
Object.keys(notifications).forEach((key) => { notifications.forEach((notification, key) => {
const { notifierUserId } = notifications![key] const notifierUserId = notification!.get('notifierUserId')
const userInfo = info!.get(notifierUserId)
parsedDOM.push( parsedDOM.push(
<NotifyItem <NotifyItem
key={key} key={key}
description={(notifications![key] ? notifications![key].description || '' : '')} description={notification!.get('description', '')}
fullName={(info![notifierUserId] ? info![notifierUserId].fullName || '' : '')} fullName={(userInfo ? userInfo.fullName || '' : '')}
avatar={(info![notifierUserId] ? info![notifierUserId].avatar || '' : '')} avatar={(userInfo ? userInfo.avatar || '' : '')}
id={key} id={key!}
isSeen={(notifications![key] ? notifications![key].isSeen || false : false)} isSeen={notification!.get('isSeen', false)}
url={(notifications![key] ? notifications![key].url || '' : '')} url={notification!.get('url')}
notifierUserId={notifierUserId} notifierUserId={notifierUserId}
closeNotify={onRequestClose} closeNotify={onRequestClose}
/> />
@@ -179,10 +183,10 @@ const mapDispatchToProps = (dispatch: any, ownProps: INotifyComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: INotifyComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: INotifyComponentProps) => {
return { return {
notifications: state.notify.userNotifies, notifications: state.getIn(['notify', 'userNotifies']),
info: state.user.info info: state.getIn(['user', 'info'])
} }
} }

View File

@@ -1,15 +1,12 @@
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
import { Post } from 'core/domain/posts/post' import { Post } from 'core/domain/posts/post'
import {Map} from 'immutable'
export interface IPostComponentProps { export interface IPostComponentProps {
/** /**
* Post object * Post object
*
* @type {Post}
* @memberof IPostComponentProps
*/ */
post: Post post: Map<string, any>
/** /**
* Owner's post avatar * Owner's post avatar
@@ -113,7 +110,7 @@ export interface IPostComponentProps {
* @type {{[commentId: string]: Comment}} * @type {{[commentId: string]: Comment}}
* @memberof ICommentGroupComponentProps * @memberof ICommentGroupComponentProps
*/ */
commentList?: {[commentId: string]: Comment} commentList?: Map<string, Comment>
/** /**
* Styles * Styles

View File

@@ -8,9 +8,11 @@ import moment from 'moment/moment'
import Linkify from 'react-linkify' import Linkify from 'react-linkify'
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map } from 'immutable'
// - Material UI // - Material UI
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui' import Card, { CardActions, CardHeader, CardMedia, CardContent } from 'material-ui/Card'
import { LinearProgress } from 'material-ui/Progress'
import Typography from 'material-ui/Typography' import Typography from 'material-ui/Typography'
import SvgShare from 'material-ui-icons/Share' import SvgShare from 'material-ui-icons/Share'
import SvgComment from 'material-ui-icons/Comment' import SvgComment from 'material-ui-icons/Comment'
@@ -65,7 +67,8 @@ const styles = (theme: any) => ({
color: 'rgb(134, 129, 129)', color: 'rgb(134, 129, 129)',
fontSize: 10, fontSize: 10,
fontWeight: 400, fontWeight: 400,
padding: 2 padding: 2,
zIndex: 1
}, },
commentCounter: { commentCounter: {
color: 'rgb(134, 129, 129)', color: 'rgb(134, 129, 129)',
@@ -125,7 +128,7 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
/** /**
* Post text * Post text
*/ */
text: post.body ? post.body : '', text: post.get('body', ''),
/** /**
* It's true if whole the text post is visible * It's true if whole the text post is visible
*/ */
@@ -141,11 +144,11 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
/** /**
* If it's true comment will be disabled on post * If it's true comment will be disabled on post
*/ */
disableComments: post.disableComments!, disableComments: post.get('disableComments', false),
/** /**
* If it's true share will be disabled on post * If it's true share will be disabled on post
*/ */
disableSharing: post.disableSharing!, disableSharing: post.get('disableSharing', false),
/** /**
* Title of share post * Title of share post
*/ */
@@ -187,7 +190,8 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
*/ */
handleOpenComments = () => { handleOpenComments = () => {
const { getPostComments, commentList, post } = this.props const { getPostComments, commentList, post } = this.props
const { id, ownerUserId } = post const id = post.get('id')
const ownerUserId = post.get('ownerUserId')
if (!commentList) { if (!commentList) {
getPostComments!(ownerUserId!, id!) getPostComments!(ownerUserId!, id!)
} }
@@ -228,14 +232,13 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
*/ */
handleDelete = () => { handleDelete = () => {
const { post } = this.props const { post } = this.props
this.props.delete!(post.id!) this.props.delete!(post.get('id'))
} }
/** /**
* Open post menu * Open post menu
*/ */
openPostMenu = (event: any) => { openPostMenu = (event: any) => {
console.log(event.currentTarget)
this.setState({ this.setState({
postMenuAnchorEl: event.currentTarget, postMenuAnchorEl: event.currentTarget,
isPostMenuOpen: true isPostMenuOpen: true
@@ -274,7 +277,7 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
*/ */
handleOpenShare = () => { handleOpenShare = () => {
const {post} = this.props const {post} = this.props
copy(`${location.origin}/${post.ownerUserId}/posts/${post.id}`) copy(`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`)
this.setState({ this.setState({
shareOpen: true shareOpen: true
}) })
@@ -360,12 +363,12 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
<MenuItem onClick={this.handleOpenPostWrite} > {translate!('post.edit')} </MenuItem> <MenuItem onClick={this.handleOpenPostWrite} > {translate!('post.edit')} </MenuItem>
<MenuItem onClick={this.handleDelete} > {translate!('post.delete')} </MenuItem> <MenuItem onClick={this.handleDelete} > {translate!('post.delete')} </MenuItem>
<MenuItem <MenuItem
onClick={() => this.props.toggleDisableComments!(!post.disableComments)} > onClick={() => this.props.toggleDisableComments!(!post.get('disableComments'))} >
{post.disableComments ? translate!('post.enableComments') : translate!('post.disableComments')} {post.get('disableComments') ? translate!('post.enableComments') : translate!('post.disableComments')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => this.props.toggleSharingComments!(!post.disableSharing)} > onClick={() => this.props.toggleSharingComments!(!post.get('disableSharing'))} >
{post.disableSharing ? translate!('post.enableSharing') : translate!('post.disableSharing')} {post.get('disableSharing') ? translate!('post.enableSharing') : translate!('post.disableSharing')}
</MenuItem> </MenuItem>
</MenuList> </MenuList>
</Paper> </Paper>
@@ -375,13 +378,23 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
</Manager> </Manager>
) )
const { ownerUserId, ownerDisplayName, creationDate, image, body } = post const {
ownerUserId,
ownerDisplayName,
creationDate,
image,
body,
id,
disableComments,
commentCounter,
disableSharing ,
} = post.toJS()
// Define variables // Define variables
return ( return (
<Card> <Card key={`post-component-${id}`}>
<CardHeader <CardHeader
title={<NavLink to={`/${ownerUserId}`}>{ownerDisplayName}</NavLink>} title={<NavLink to={`/${ownerUserId}`}>{ownerDisplayName}</NavLink>}
subheader={moment.unix(creationDate!).fromNow() + ' | ' + translate!('post.public')} subheader={creationDate ? moment.unix(creationDate!).fromNow() + ' | ' + translate!('post.public') : <LinearProgress color='primary' />}
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 : ''} action={isPostOwner ? rightIconMenu : ''}
> >
@@ -426,16 +439,16 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
<div className={classes.voteCounter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div> <div className={classes.voteCounter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div>
</IconButton> </IconButton>
</div> </div>
{!post.disableComments ? {!disableComments ?
(<div style={{ display: 'inherit' }}><IconButton (<div style={{ display: 'inherit' }}><IconButton
className={classes.iconButton} className={classes.iconButton}
onClick={this.handleOpenComments} onClick={this.handleOpenComments}
aria-label='Comment'> aria-label='Comment'>
<SvgComment /> <SvgComment />
<div className={classes.commentCounter}>{post.commentCounter! > 0 ? post.commentCounter : ''} </div> <div className={classes.commentCounter}>{commentCounter! > 0 ? commentCounter : ''} </div>
</IconButton> </IconButton>
</div>) : ''} </div>) : ''}
{!post.disableSharing ? (<IconButton {!disableSharing ? (<IconButton
className={classes.iconButton} className={classes.iconButton}
onClick={this.handleOpenShare} onClick={this.handleOpenShare}
aria-label='Comment'> aria-label='Comment'>
@@ -444,7 +457,7 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
</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={ownerUserId!} onToggleRequest={this.handleOpenComments} isPostOwner={this.props.isPostOwner!} disableComments={disableComments!} postId={id} />
<ShareDialog <ShareDialog
onClose={this.handleCloseShare} onClose={this.handleCloseShare}
@@ -477,16 +490,14 @@ export class PostComponent extends Component<IPostComponentProps, IPostComponent
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.get('id'), post.get('ownerUserId'))),
unvote: () => dispatch(voteActions.dbDeleteVote(post.id!, post.ownerUserId!)), unvote: () => dispatch(voteActions.dbDeleteVote(post.get('id'), post.get('ownerUserId'))),
delete: (id: string) => dispatch(postActions.dbDeletePost(id)), delete: (id: string) => dispatch(postActions.dbDeletePost(id)),
toggleDisableComments: (status: boolean) => { toggleDisableComments: (status: boolean) => {
post.disableComments = status dispatch(postActions.dbUpdatePost(post.set('disableComments', status), (x: any) => x))
dispatch(postActions.dbUpdatePost(post, (x: any) => x))
}, },
toggleSharingComments: (status: boolean) => { toggleSharingComments: (status: boolean) => {
post.disableSharing = status dispatch(postActions.dbUpdatePost(post.set('disableSharing', status), (x: any) => x))
dispatch(postActions.dbUpdatePost(post, (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 || '')),
@@ -501,21 +512,21 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IPostComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IPostComponentProps) => {
const { post, vote, authorize, comment } = state
const { uid } = authorize const uid = state.getIn(['authorize', 'uid'])
let currentUserVote = ownProps.post.votes ? ownProps.post.votes[uid] : false let currentUserVote = ownProps.post.getIn(['votes', uid], false)
const postModel = post.userPosts[ownProps.post.ownerUserId!][ownProps.post.id!] const voteCount = state.getIn(['post', 'userPosts', ownProps.post.get('ownerUserId'), ownProps.post.get('id'), 'score'], 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 } = state.getIn(['comment', 'postComments', ownProps.post.get('id')])
const commentList: { [commentId: string]: Comment } = comment.postComments[ownProps.post.id!] const user = state.getIn(['user', 'info', ownProps.post.get('ownerUserId')])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
commentList, commentList,
avatar: state.user.info && state.user.info[ownProps.post.ownerUserId!] ? state.user.info[ownProps.post.ownerUserId!].avatar || '' : '', avatar: user ? user.avatar : '',
fullName: state.user.info && state.user.info[ownProps.post.ownerUserId!] ? state.user.info[ownProps.post.ownerUserId!].fullName || '' : '', fullName: user ? user.fullName : '',
voteCount: postModel.score, voteCount,
currentUserVote, currentUserVote,
isPostOwner: postOwner > 0 isPostOwner: uid === ownProps.post.get('ownerUserId')
} }
} }

View File

@@ -1,5 +1,5 @@
import { Post } from 'core/domain/posts' import { Post } from 'core/domain/posts'
import {Map} from 'immutable'
export interface IPostWriteComponentProps { export interface IPostWriteComponentProps {
/** /**
@@ -55,7 +55,7 @@ export interface IPostWriteComponentProps {
/** /**
* Post model * Post model
*/ */
postModel?: Post postModel?: Map<string, any>
/** /**
* Save a post * Save a post
@@ -69,7 +69,7 @@ export interface IPostWriteComponentProps {
* *
* @memberof IPostWriteComponentProps * @memberof IPostWriteComponentProps
*/ */
update?: (post: Post, callback: Function) => any update?: (post: Map<string, any>, callback: Function) => any
/** /**
* Styles * Styles

View File

@@ -3,6 +3,7 @@ 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 { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map } from 'immutable'
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui' import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import List, { import List, {
@@ -108,15 +109,15 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
/** /**
* Post text * Post text
*/ */
postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '') : '', postText: this.props.edit && postModel ? postModel.get('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.get('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.get('imageFullPath', '') : '',
/** /**
* If it's true gallery will be open * If it's true gallery will be open
*/ */
@@ -132,11 +133,11 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
/** /**
* 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.get('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.get('disableSharing') : false
} }
@@ -253,14 +254,14 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
}, onRequestClose) }, onRequestClose)
} }
} else { // In edit status we pass post to update functions } else { // In edit status we pass post to update functions
postModel!.body = postText const updatedPost = postModel!.set('body', postText)
postModel!.tags = tags .set('tags', tags)
postModel!.image = image .set('image', image)
postModel!.imageFullPath = imageFullPath .set('imageFullPath', imageFullPath)
postModel!.disableComments = disableComments .set('disableComments', disableComments)
postModel!.disableSharing = disableSharing .set('disableSharing', disableSharing)
update!(postModel!, onRequestClose) update!(updatedPost, onRequestClose)
} }
} }
@@ -337,34 +338,38 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
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.get('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.get('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.get('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 * Whether menu is open
*/ */
disabledPost: true, menuOpen: false,
/** /**
* If it's true comment will be disabled on post * If it's true post button will be disabled
*/ */
disableComments: this.props.edit && postModel ? postModel.disableComments! : false, disabledPost: true,
/** /**
* If it's true share will be disabled on post * If it's true comment will be disabled on post
*/ */
disableSharing: this.props.edit && postModel ? postModel.disableSharing! : false disableComments: this.props.edit && postModel ? postModel.get('disableComments') : false,
/**
* If it's true share will be disabled on post
*/
disableSharing: this.props.edit && postModel ? postModel.get('disableSharing') : false
}) })
} }
@@ -576,7 +581,7 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
const mapDispatchToProps = (dispatch: any, ownProps: IPostWriteComponentProps) => { const mapDispatchToProps = (dispatch: any, ownProps: IPostWriteComponentProps) => {
return { return {
post: (post: Post, callBack: Function) => dispatch(postActions.dbAddImagePost(post, callBack)), post: (post: Post, callBack: Function) => dispatch(postActions.dbAddImagePost(post, callBack)),
update: (post: Post, callBack: Function) => dispatch(postActions.dbUpdatePost(post, callBack)) update: (post: Map<string, any>, callBack: Function) => dispatch(postActions.dbUpdatePost(post, callBack))
} }
} }
@@ -586,12 +591,14 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostWriteComponentProps) =
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IPostWriteComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IPostWriteComponentProps) => {
const uid = state.getIn(['authorize', 'uid'])
const user = state.getIn(['user', 'info', uid], {})
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
postImageState: state.imageGallery.status, postImageState: state.getIn(['imageGallery', 'status']),
ownerAvatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '', ownerAvatar: user.avatar || '',
ownerDisplayName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : '' ownerDisplayName: user.fullName || ''
} }
} }

View File

@@ -1,8 +1,8 @@
// - 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 PropTypes from 'prop-types'
import config from 'src/config' import config from 'src/config'
import {Map} from 'immutable'
// - Material UI // - Material UI
import { grey } from 'material-ui/colors' import { grey } from 'material-ui/colors'
@@ -33,39 +33,6 @@ import { IProfileHeaderComponentState } from './IProfileHeaderComponentState'
*/ */
export class ProfileHeaderComponent extends Component<IProfileHeaderComponentProps, IProfileHeaderComponentState> { export class ProfileHeaderComponent extends Component<IProfileHeaderComponentProps, IProfileHeaderComponentState> {
static propTypes = {
/**
* User avatar address
*/
avatar: PropTypes.string,
/**
* User banner address
*/
banner: PropTypes.string,
/**
* User tagline
*/
tagLine: PropTypes.string,
/**
* User full name
*/
fullName: PropTypes.string.isRequired,
/**
* The number of followers
*/
followerCount: PropTypes.number,
/**
* User identifier
*/
userId: PropTypes.string,
/**
* If the user profile identifier of param is equal to the user authed identifier
*/
isAuthedUser: PropTypes.bool
}
/** /**
* Component constructor * Component constructor
* @param {object} props is an object properties of component * @param {object} props is an object properties of component
@@ -186,7 +153,7 @@ export class ProfileHeaderComponent extends Component<IProfileHeaderComponentPro
/> />
<div className='left'> <div className='left'>
{/* User avatar*/} {/* User avatar*/}
<div style={{ display: 'flex', justifyContent: 'center' }}><UserAvatar fullName={this.props.fullName} fileName={this.props.avatar} size={60} style={styles.avatar} /></div> <div style={{ display: 'flex', justifyContent: 'center' }}><UserAvatar fullName={this.props.fullName || ' '} fileName={this.props.avatar} size={60} style={styles.avatar} /></div>
<div className='info'> <div className='info'>
<div className='fullName'> <div className='fullName'>
{this.props.fullName} {this.props.fullName}
@@ -235,11 +202,11 @@ const mapDispatchToProps = (dispatch: any, ownProps: IProfileHeaderComponentProp
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IProfileHeaderComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IProfileHeaderComponentProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
editProfileOpen: state.user.openEditProfile editProfileOpen: state.getIn(['user', 'openEditProfile'])
} }
} }

View File

@@ -1,6 +1,7 @@
import { Feed } from 'core/domain/common/feed' import { Feed } from 'core/domain/common/feed'
import { ServerRequestModel } from 'models/server/serverRequestModel' import { ServerRequestModel } from 'models/server/serverRequestModel'
import { Profile } from 'core/domain/users' import { Profile } from 'core/domain/users'
import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
export interface ISendFeedbackComponentProps { export interface ISendFeedbackComponentProps {
/** /**
@@ -21,7 +22,7 @@ export interface ISendFeedbackComponentProps {
/** /**
* The server request of send feedback * The server request of send feedback
*/ */
sendFeedbackRequest?: ServerRequestModel sendFeedbackRequestType?: ServerRequestStatusType
/** /**
* Current user profile * Current user profile

View File

@@ -3,6 +3,7 @@ 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 classNames from 'classnames' import classNames from 'classnames'
import {Map} from 'immutable'
// - Material UI // - Material UI
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
@@ -89,7 +90,7 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
} }
mainForm = () => { mainForm = () => {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest, translate } = this.props const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequestType, translate } = this.props
const { feedText } = this.state const { feedText } = this.state
return ( return (
<div className='main-box'> <div className='main-box'>
@@ -168,11 +169,11 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
} }
getFeedbackForm = () => { getFeedbackForm = () => {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequestType } = this.props
const { feedText } = this.state const { feedText } = this.state
if (sendFeedbackRequest) { if (sendFeedbackRequestType) {
switch (sendFeedbackRequest.status) { switch (sendFeedbackRequestType) {
case ServerRequestStatusType.Sent: case ServerRequestStatusType.Sent:
return this.loadingForm() return this.loadingForm()
@@ -195,7 +196,7 @@ export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps
* @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, classes } = this.props const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequestType, classes } = this.props
const { feedText } = this.state const { feedText } = this.state
return ( return (
@@ -237,19 +238,19 @@ const mapDispatchToProps = (dispatch: Function, ownProps: ISendFeedbackComponent
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ISendFeedbackComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: ISendFeedbackComponentProps) => {
const { server, global, authorize, user } = state const request = state.getIn(['server', 'request'])
const { request } = server const uid = state.getIn(['authorize', 'uid'])
const { uid } = authorize const requestId = StringAPI.createServerRequestId(ServerRequestType.CommonSendFeedback, uid)
const currentUser: User = user.info && user.info[uid] ? { ...user.info[uid], userId: uid } : {} const currentUser: User = { ...state.getIn(['user', 'info', uid], {}), userId: uid }
const { sendFeedbackStatus } = global const sendFeedbackStatus = state.getIn(['global', 'sendFeedbackStatus'])
const sendFeedbackRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CommonSendFeedback, uid)] : null const sendFeedbackRequestType = state.getIn(['server', 'request', requestId])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
sendFeedbackStatus, sendFeedbackStatus,
sendFeedbackRequest, sendFeedbackRequestType: sendFeedbackRequestType ? sendFeedbackRequestType.status : ServerRequestStatusType.NoAction,
currentUser currentUser
} }
} }

View File

@@ -1,4 +1,5 @@
import { Post } from 'core/domain/posts' import { Post } from 'core/domain/posts'
import {Map} from 'immutable'
export interface IShareDialogComponentProps { export interface IShareDialogComponentProps {
@@ -25,7 +26,7 @@ export interface IShareDialogComponentProps {
/** /**
* The post object for sharing * The post object for sharing
*/ */
post: Post post: Map<string, any>
/** /**
* Styles * Styles

View File

@@ -105,9 +105,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div> <div>
<FacebookShareButton <FacebookShareButton
onShareWindowClose={onClose} onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.body} quote={post.get('body')}
hashtag={`#${post.tags![0]}`}> hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem > <MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}> <ListItemIcon classes={{ root: classes.networkShare }}>
<FacebookIcon <FacebookIcon
@@ -121,9 +121,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div> <div>
<TwitterShareButton <TwitterShareButton
onShareWindowClose={onClose} onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.body} quote={post.get('body')}
hashtag={`#${post.tags![0]}`}> hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem > <MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}> <ListItemIcon classes={{ root: classes.networkShare }}>
<TwitterIcon <TwitterIcon
@@ -137,9 +137,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div> <div>
<LinkedinShareButton <LinkedinShareButton
onShareWindowClose={onClose} onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.body} quote={post.get('body')}
hashtag={`#${post.tags![0]}`}> hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem > <MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}> <ListItemIcon classes={{ root: classes.networkShare }}>
<LinkedinIcon <LinkedinIcon
@@ -153,9 +153,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div> <div>
<GooglePlusShareButton <GooglePlusShareButton
onShareWindowClose={onClose} onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.body} quote={post.get('body')}
hashtag={`#${post.tags![0]}`}> hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem > <MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}> <ListItemIcon classes={{ root: classes.networkShare }}>
<GooglePlusIcon <GooglePlusIcon
@@ -174,7 +174,7 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
</MenuItem> </MenuItem>
</MenuList>) </MenuList>)
: <div> : <div>
<TextField autoFocus fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} /> <TextField autoFocus fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`} />
<Typography className={classNames('animate-top', classes.clipboard)} variant='headline' component='h2'> <Typography className={classNames('animate-top', classes.clipboard)} variant='headline' component='h2'>
Link has been copied to clipboard ... Link has been copied to clipboard ...
</Typography> </Typography>
@@ -205,7 +205,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IShareDialogComponentProps)
*/ */
const mapStateToProps = (state: any, ownProps: IShareDialogComponentProps) => { const mapStateToProps = (state: any, ownProps: IShareDialogComponentProps) => {
return { return {
translate: getTranslate(state.locale) translate: getTranslate(state.get('locale'))
} }
} }

View File

@@ -3,6 +3,7 @@ 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 Avatar from 'material-ui/Avatar' import Avatar from 'material-ui/Avatar'
import { Map } from 'immutable'
// - Import app components // - Import app components
@@ -95,8 +96,8 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IUserAvatarComponentPr
*/ */
const mapStateToProps = (state: any, ownProps: IUserAvatarComponentProps) => { const mapStateToProps = (state: any, ownProps: IUserAvatarComponentProps) => {
return { return {
avatarURL: state.imageGallery.imageURLList, avatarURL: state.getIn(['imageGallery', 'imageURLList']),
imageRequests: state.imageGallery.imageRequests imageRequests: state.getIn(['imageGallery', 'imageRequests'])
} }
} }

View File

@@ -3,40 +3,28 @@ import { Circle } from 'core/domain/circles/circle'
import { UserTie } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType' import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
import { ServerRequestModel } from 'models/server/serverRequestModel' import { ServerRequestModel } from 'models/server/serverRequestModel'
import {Map, List} from 'immutable'
export interface IUserBoxComponentProps { export interface IUserBoxComponentProps {
/** /**
* User identifier * User identifier
*
* @type {string}
* @memberof IUserBoxComponentProps
*/ */
userId: string userId: string
/** /**
* User * User
*
* @type {User}
* @memberof IUserBoxComponentProps
*/ */
user: UserTie user: UserTie
/** /**
* Circles * Circles
*
* @type {{[circleId: string]: Circle}}
* @memberof IUserBoxComponentProps
*/ */
circles?: {[circleId: string]: Circle} circles?: Map<string, Map<string, any>>
/** /**
* List of circles' id * List of circles' id
*
* @type {string[]}
* @memberof IUserBoxComponentProps
*/ */
userBelongCircles?: string[] userBelongCircles?: List<string>
/** /**
* Whether current user followed this user * Whether current user followed this user
@@ -45,54 +33,38 @@ export interface IUserBoxComponentProps {
/** /**
* The number of circles * The number of circles
*
* @type {number}
* @memberof IUserBoxComponentProps
*/ */
belongCirclesCount?: number belongCirclesCount?: number
/** /**
* The first circle * The first circle
*
* @type {User}
* @memberof IUserBoxComponentProps
*/ */
firstBelongCircle?: Circle firstBelongCircle?: Map<string, any>
/** /**
* Avatar address * Avatar address
*
* @type {string}
* @memberof IUserBoxComponentProps
*/ */
avatar?: string avatar?: string
/** /**
* User full name * User full name
*
* @type {string}
* @memberof IUserBoxComponentProps
*/ */
fullName?: string fullName?: string
/** /**
* The `Following` circle identifier of current user * The `Following` circle identifier of current user
*/ */
followingCircleId?: string followingCircle?: Map<string, any>
/** /**
* Create a circle * Create a circle
*
* @memberof IUserBoxComponentProps
*/ */
createCircle?: (name: string) => any createCircle?: (name: string) => any
/** /**
* Add a user in a circle * Add a user in a circle
*
* @memberof IUserBoxComponentProps
*/ */
addUserToCircle?: (circleIds: string[],user: UserTie) => any addUserToCircle?: (circleIds: List<string>,user: UserTie) => any
/** /**
* Add referer user to the `Following` circle of current user * Add referer user to the `Following` circle of current user
@@ -107,12 +79,12 @@ export interface IUserBoxComponentProps {
/** /**
* Set current user selected circles for referer user * Set current user selected circles for referer user
*/ */
setSelectedCircles?: (userId: string, circleList: string[]) => any setSelectedCircles?: (userId: string, circleList: List<string>) => any
/** /**
* Remove current user selected circles for referer user * Remove current user selected circles for referer user
*/ */
removeSelectedCircles?: (userId: string, circleList: string[]) => any removeSelectedCircles?: (userId: string, circleList: List<string>) => any
/** /**
* Open select circle box * Open select circle box
@@ -126,8 +98,6 @@ export interface IUserBoxComponentProps {
/** /**
* Redirect page to [url] * Redirect page to [url]
*
* @memberof IUserBoxComponentProps
*/ */
goTo?: (url: string) => any goTo?: (url: string) => any
@@ -149,7 +119,7 @@ export interface IUserBoxComponentProps {
/** /**
* Keep selected circles for refere user * Keep selected circles for refere user
*/ */
selectedCircles?: string[] selectedCircles?: List<string>
/** /**
* Whether the select circles box for referer user is open * Whether the select circles box for referer user is open

View File

@@ -6,6 +6,7 @@ import PropTypes from 'prop-types'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import classNames from 'classnames' import classNames from 'classnames'
import {Map, List as ImuList} from 'immutable'
// - Material UI // - Material UI
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
@@ -112,7 +113,6 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
borderRadius: '4px' borderRadius: '4px'
} }
} }
selectedCircles: string[]
/** /**
* Component constructor * Component constructor
@@ -140,7 +140,6 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/ */
disabledDoneCircles: true disabledDoneCircles: true
} }
this.selectedCircles = userBelongCircles!.slice()
// Binding functions to `this` // Binding functions to `this`
this.handleChangeName = this.handleChangeName.bind(this) this.handleChangeName = this.handleChangeName.bind(this)
this.onCreateCircle = this.onCreateCircle.bind(this) this.onCreateCircle = this.onCreateCircle.bind(this)
@@ -153,11 +152,10 @@ 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, avatar, fullName } = this.props
const { avatar, fullName } = user
const { disabledDoneCircles } = this.state const { disabledDoneCircles } = this.state
if (!disabledDoneCircles) { if (!disabledDoneCircles) {
if (selectedCircles!.length > 0) { if (selectedCircles!.count() > 0) {
addUserToCircle!(selectedCircles!, { avatar, userId, fullName }) addUserToCircle!(selectedCircles!, { avatar, userId, fullName })
} else { } else {
deleteFollowingUser!(userId) deleteFollowingUser!(userId)
@@ -171,14 +169,13 @@ 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, followingCircle, userId, user, followRequest, avatar, fullName } = this.props
if (followRequest && followRequest.status === ServerRequestStatusType.Sent) { if (followRequest && followRequest.status === ServerRequestStatusType.Sent) {
return return
} }
const { avatar, fullName } = user
if (!isFollowed) { if (!isFollowed) {
followUser!(followingCircleId!, { avatar, userId, fullName }) followUser!(followingCircle!.get('id'), { avatar, userId, fullName })
} else { } else {
this.onRequestOpenAddCircle() this.onRequestOpenAddCircle()
} }
@@ -234,17 +231,13 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => { handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => {
const { userBelongCircles, circles, setSelectedCircles, selectedCircles, userId } = this.props const { userBelongCircles, circles, setSelectedCircles, selectedCircles, userId } = this.props
let newSelectedCircles = selectedCircles!.slice() let newSelectedCircles = selectedCircles!
if (isInputChecked) { if (isInputChecked) {
newSelectedCircles = selectedCircles!.push(circleId)
newSelectedCircles = [
...selectedCircles!,
circleId
]
} else { } else {
const circleIndex = selectedCircles!.indexOf(circleId) const circleIndex = selectedCircles!.indexOf(circleId)
newSelectedCircles.splice(circleIndex, 1) newSelectedCircles = newSelectedCircles.remove(circleIndex)
} }
setSelectedCircles!(userId, newSelectedCircles) setSelectedCircles!(userId, newSelectedCircles)
@@ -258,39 +251,39 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/ */
circleList = () => { circleList = () => {
let { circles, userId, userBelongCircles, selectedCircles, classes } = this.props let { circles, userId, userBelongCircles, selectedCircles, classes } = this.props
const circleDomList: any[] = []
if (circles) { if (circles) {
const parsedDate = Object.keys(circles).map((circleId, index) => { circles.forEach((circle, circleId) => {
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 ( circleDomList.push(
<ListItem key={`${circleId}-${userId}`} dense className={classes.listItem}> <ListItem key={`${circleId}-${userId}`} dense className={classes.listItem}>
<ListItemText className={classes.circleName} primary={circles![circleId].name} /> <ListItemText className={classes.circleName} primary={circle!.get('name')} />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<Checkbox <Checkbox
onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId)} onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId!)}
checked={isBelong} checked={isBelong}
/> />
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem>) </ListItem>)
}) })
return parsedDate return circleDomList
} }
} }
/** /**
* Check if the the selected circles changed * Check if the the selected circles changed
*/ */
selectedCircleChange = (selectedCircles: string[]) => { selectedCircleChange = (selectedCircles: ImuList<string>) => {
let isChanged = false let isChanged = false
const { userBelongCircles, circles } = this.props const { userBelongCircles, circles } = this.props
if (selectedCircles.length === userBelongCircles!.length) { if (selectedCircles.count() === userBelongCircles!.count()) {
for (let circleIndex: number = 0; circleIndex < selectedCircles.length; circleIndex++) { for (let circleIndex: number = 0; circleIndex < selectedCircles.count(); circleIndex++) {
const selectedCircleId = selectedCircles[circleIndex] const selectedCircleId = selectedCircles.get(circleIndex)
if (!(userBelongCircles!.indexOf(selectedCircleId) > -1)) { if (!(userBelongCircles!.indexOf(selectedCircleId) > -1)) {
isChanged = true isChanged = true
break break
@@ -308,7 +301,18 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/ */
render () { render () {
const { disabledDoneCircles } = this.state const { disabledDoneCircles } = this.state
const { isFollowed, followRequest, userId, isSelecteCirclesOpen, addToCircleRequest, deleteFollowingUserRequest, classes, translate } = this.props const {
isFollowed,
firstBelongCircle,
belongCirclesCount,
followRequest,
userId,
isSelecteCirclesOpen,
addToCircleRequest,
deleteFollowingUserRequest,
classes,
translate
} = this.props
return ( return (
<Paper key={userId} elevation={1} className={classNames('grid-cell', classes.paper)}> <Paper key={userId} elevation={1} className={classNames('grid-cell', classes.paper)}>
@@ -331,7 +335,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
</div> </div>
<div onClick={() => this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{ cursor: 'pointer' }}> <div onClick={() => this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{ cursor: 'pointer' }}>
<div> <div>
{this.props.user.fullName} {this.props.fullName}
</div> </div>
</div> </div>
<div style={this.styles.followButton as any}> <div style={this.styles.followButton as any}>
@@ -344,7 +348,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
} }
> >
{!isFollowed ? translate!('userBox.followButton') {!isFollowed ? translate!('userBox.followButton')
: (this.props.belongCirclesCount! > 1 ? translate!('userBox.numberOfCircleButton', {circlesCount: this.props.belongCirclesCount}) : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : translate!('userBox.followButton')))} : (belongCirclesCount! > 1 ? translate!('userBox.numberOfCircleButton', {circlesCount: belongCirclesCount}) : ((firstBelongCircle) ? firstBelongCircle.get('name', 'Followed') : translate!('userBox.followButton')))}
</Button> </Button>
</div> </div>
</div> </div>
@@ -414,7 +418,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps) => { const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps) => {
return { return {
createCircle: (name: string) => dispatch(circleActions.dbAddCircle(name)), createCircle: (name: string) => dispatch(circleActions.dbAddCircle(name)),
addUserToCircle: (circleIds: string[], user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)), addUserToCircle: (circleIds: ImuList<string>, user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)),
followUser: (circleId: string, userFollowing: UserTie) => dispatch(circleActions.dbFollowUser(circleId, userFollowing)), followUser: (circleId: string, userFollowing: UserTie) => dispatch(circleActions.dbFollowUser(circleId, userFollowing)),
deleteFollowingUser: (followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)), deleteFollowingUser: (followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)),
setSelectedCircles: (userId: string, circleList: string[]) => dispatch(circleActions.setSelectedCircles(userId, circleList)), setSelectedCircles: (userId: string, circleList: string[]) => dispatch(circleActions.setSelectedCircles(userId, circleList)),
@@ -432,36 +436,41 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IUserBoxComponentProps) => {
const { circle, authorize, server } = state const uid = state.getIn(['authorize', 'uid'])
const { uid } = authorize const request = state.getIn(['server', 'request'])
const { request } = server
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: Map<string, Map<string, any>> = state.getIn(['circle', 'circleList'], {})
const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : [] const userBelongCircles: ImuList<any> = state.getIn(['circle', 'userTies', ownProps.userId, 'circleIdList'], ImuList())
const isFollowed = userBelongCircles.length > 0 const isFollowed = userBelongCircles.count() > 0
const followingCircleId = circles ? Object.keys(circles) const followingCircle = circles
.filter((circleId) => circles[circleId].isSystem && circles[circleId].name === `Following`)[0] : '' .filter((followingCircle) => followingCircle!.get('isSystem', false) && followingCircle!.get('name') === `Following`)
const followRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)] : null .toArray()[0]
const addToCircleRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, ownProps.userId)] : null const followRequestId = StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)
const deleteFollowingUserRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, ownProps.userId)] : null const followRequest = state.getIn(['server', 'request', followRequestId])
const selectedCircles = circle.selectedCircles ? circle.selectedCircles[ownProps.userId] : [] const addToCircleRequestId = StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, ownProps.userId)
const isSelecteCirclesOpen = circle.openSelecteCircles ? circle.openSelecteCircles[ownProps.userId] : [] const addToCircleRequest = state.getIn(['server', 'request', addToCircleRequestId])
const deleteFollowingUserRequestId = StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, ownProps.userId)
const deleteFollowingUserRequest = state.getIn(['server', 'request', deleteFollowingUserRequestId])
const selectedCircles = state.getIn(['circle', 'selectedCircles', ownProps.userId], [])
const isSelecteCirclesOpen = state.getIn(['circle', 'openSelecteCircles', ownProps.userId], [])
const userBox = state.getIn(['user', 'info', ownProps.userId])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
isSelecteCirclesOpen, isSelecteCirclesOpen,
isFollowed, isFollowed,
selectedCircles, selectedCircles,
circles, circles,
followingCircleId, followingCircle,
userBelongCircles, userBelongCircles,
followRequest, followRequest,
belongCirclesCount: userBelongCircles.length || 0, belongCirclesCount: userBelongCircles.count() || 0,
firstBelongCircle: userBelongCircles ? (circles ? circles[userBelongCircles[0]] : {}) : {}, firstBelongCircle: userBelongCircles ? circles.get(userBelongCircles.get(0), Map({})) : Map({}),
avatar: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].avatar || '' : '', avatar: userBox.avatar || '' ,
fullName: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].fullName || '' : '' fullName: userBox.fullName || ''
} }
} }

View File

@@ -1,5 +1,6 @@
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { UserTie } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
import {Map} from 'immutable'
export interface IUserBoxListComponentProps { export interface IUserBoxListComponentProps {
@@ -9,7 +10,7 @@ export interface IUserBoxListComponentProps {
* @type {{[userId: string]: User}} * @type {{[userId: string]: User}}
* @memberof IUserBoxListComponentProps * @memberof IUserBoxListComponentProps
*/ */
users: {[userId: string]: UserTie} users: Map<string, UserTie>
/** /**
* User identifier * User identifier

View File

@@ -2,12 +2,14 @@
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 {Map} from 'immutable'
// - Import app components // - Import app components
import UserBox from 'components/userBox' import UserBox from 'components/userBox'
import { IUserBoxListComponentProps } from './IUserBoxListComponentProps' import { IUserBoxListComponentProps } from './IUserBoxListComponentProps'
import { IUserBoxListComponentState } from './IUserBoxListComponentState' import { IUserBoxListComponentState } from './IUserBoxListComponentState'
import { UserTie } from 'core/domain/circles/userTie'
// - Import API // - Import API
@@ -42,15 +44,17 @@ export class UserBoxListComponent extends Component<IUserBoxListComponentProps,I
} }
userList = () => { userList = () => {
let { users, uid } = this.props let { uid } = this.props
const users = this.props.users
const userBoxList: any[] = []
if (users) { if (users) {
return Object.keys(users).map((key, index) => { users.forEach((user: UserTie, key: string) => {
if (uid !== key) { if (uid !== key) {
return <UserBox key={key} userId={key} user={users[key]}/> userBoxList.push(<UserBox key={key} userId={key} user={user}/>)
} }
}) })
} }
return userBoxList
} }
/** /**
@@ -92,7 +96,7 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxListComponentP
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IUserBoxListComponentProps) => { const mapStateToProps = (state: any, ownProps: IUserBoxListComponentProps) => {
const {uid} = state.authorize const uid = state.getIn(['authorize', 'uid'], 0)
return { return {
uid uid
} }

View File

@@ -1,20 +1,14 @@
import { Circle } from 'core/domain/circles' import { Circle } from 'core/domain/circles'
import {Map} from 'immutable'
export interface IYourCirclesComponentProps { export interface IYourCirclesComponentProps {
/** /**
* Circles * Circles
*
* @type {{[circleId: string]: Circle}}
* @memberof IYourCirclesComponentProps
*/ */
circles?: {[circleId: string]: Circle} circles?: Map<string, Map<string, any>>
/** /**
* User identifier * User identifier
*
* @type {string}
* @memberof IYourCirclesComponentProps
*/ */
uid?: string uid?: string
} }

View File

@@ -2,6 +2,8 @@
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 {Map} from 'immutable'
import List from 'material-ui/List' import List from 'material-ui/List'
// - Import app components // - Import app components
@@ -44,8 +46,8 @@ export class YourCirclesComponent extends Component<IYourCirclesComponentProps,I
let parsedCircles: any[] = [] let parsedCircles: any[] = []
if (circles) { if (circles) {
Object.keys(circles).map((key, index) => { circles.map((circle, key) => {
parsedCircles.push(<CircleComponent key={key} circle={circles![key]} id={key} uid={uid!} />) parsedCircles.push(<CircleComponent key={key} circle={circle!} id={key!} uid={uid!} />)
}) })
} }
return parsedCircles return parsedCircles
@@ -95,10 +97,9 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IYourCirclesComponentP
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IYourCirclesComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IYourCirclesComponentProps) => {
const {circle, authorize, server} = state const uid = state.getIn(['authorize', 'uid'])
const { uid } = state.authorize const circles: Map<string, Map<string, any>> = state.getIn(['circle', 'circleList'], {})
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
return { return {
uid, uid,
circles circles

View File

@@ -12,6 +12,8 @@
ADD_VIDEO_POST = 'ADD_VIDEO_POST', ADD_VIDEO_POST = 'ADD_VIDEO_POST',
ADD_POST = 'ADD_POST', ADD_POST = 'ADD_POST',
UPDATE_POST = 'UPDATE_POST', UPDATE_POST = 'UPDATE_POST',
UPDATE_POST_COMMENTS = 'UPDATE_POST_COMMENTS',
UPDATE_POST_VOTES = 'UPDATE_POST_VOTES',
DELETE_POST = 'DELETE_POST', DELETE_POST = 'DELETE_POST',
ADD_LIST_POST = 'ADD_LIST_POST', ADD_LIST_POST = 'ADD_LIST_POST',
CLEAR_ALL_DATA_POST = 'CLEAR_ALL_DATA_POST' CLEAR_ALL_DATA_POST = 'CLEAR_ALL_DATA_POST'

View File

@@ -144,7 +144,7 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IEmailVerificationComp
*/ */
const mapStateToProps = (state: any, ownProps: IEmailVerificationComponentProps) => { const mapStateToProps = (state: any, ownProps: IEmailVerificationComponentProps) => {
return { return {
translate: getTranslate(state.locale) translate: getTranslate(state.get('locale'))
} }
} }

View File

@@ -1,5 +1,6 @@
// - Import react components // - Import react components
import { HomeRouter } from 'routes' import { HomeRouter } from 'routes'
import { Map } from 'immutable'
import React, { Component } from 'react' import React, { Component } from 'react'
import _ from 'lodash' import _ from 'lodash'
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom' import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
@@ -172,7 +173,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
if (!isVerifide) { if (!isVerifide) {
goTo!('/emailVerification') goTo!('/emailVerification')
} else if (!global.defaultLoadDataStatus) { } else if (!global.get('defaultLoadDataStatus')) {
clearData!() clearData!()
loadData!() loadData!()
@@ -188,7 +189,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
* @memberof Home * @memberof Home
*/ */
render() { render() {
const HR = HomeRouter as any const HR = HomeRouter
const { loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback, translate, classes, theme } = this.props const { loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback, translate, classes, theme } = this.props
const { drawerOpen } = this.state const { drawerOpen } = this.state
const drawer = ( const drawer = (
@@ -337,29 +338,28 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IHomeComponentProps) => {
const { authorize, global, user, post, imageGallery, notify, circle } = state const uid = state.getIn(['authorize', 'uid'], {})
const { uid } = authorize const global = state.get('global', {})
let mergedPosts = {} let mergedPosts = Map({})
const circles = circle ? (circle.circleList || {}) : {} const circles = state.getIn(['circle', 'circleList'], {})
const followingUsers = circle ? circle.userTies : {} const followingUsers: Map<string, any> = state.getIn(['circle', 'userTies'], {})
const posts = post.userPosts ? post.userPosts[authorize.uid] : {} const posts = state.getIn(['post', 'userPosts', uid ], {})
const hasMorePosts = post.stream.hasMoreData const hasMorePosts = state.getIn(['post', 'stream', 'hasMoreData' ], true)
Object.keys(followingUsers).forEach((userId) => { followingUsers.forEach((user, userId) => {
let newPosts = post.userPosts ? post.userPosts[userId] : {} let newPosts = state.getIn(['post', 'userPosts', userId], {})
_.merge(mergedPosts, newPosts) mergedPosts = mergedPosts.merge(newPosts)
}) })
_.merge(mergedPosts, posts) mergedPosts = mergedPosts.merge(posts)
return { return {
authed: authorize.authed, authed: state.getIn(['authorize', 'authed'], false),
isVerifide: authorize.isVerifide, isVerifide: state.getIn(['authorize', 'isVerifide'], false),
mainStyle: global.sidebarMainStyle, translate: getTranslate(state.get('locale')),
translate: getTranslate(state.locale), currentLanguage: getActiveLanguage(state.get('locale')).code,
currentLanguage: getActiveLanguage(state.locale).code,
mergedPosts, mergedPosts,
global, global,
hasMorePosts, hasMorePosts,
loaded: user.loaded && imageGallery.loaded && notify.loaded && circle.loaded loaded: state.getIn(['user', 'loaded']) && state.getIn(['imageGallery', 'loaded']) && state.getIn(['notify', 'loaded']) && state.getIn(['circle', 'loaded'])
} }
} }

View File

@@ -12,7 +12,7 @@ import Divider from 'material-ui/Divider'
import ActionAndroid from 'material-ui-icons/Android' import ActionAndroid from 'material-ui-icons/Android'
import { withStyles } from 'material-ui/styles' import { withStyles } from 'material-ui/styles'
import config from 'src/config' import config from 'src/config'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { localize } from 'react-localize-redux'
// - Import actions // - Import actions
import * as authorizeActions from 'src/store/actions/authorizeActions' import * as authorizeActions from 'src/store/actions/authorizeActions'
@@ -20,6 +20,7 @@ import { ILoginComponentProps } from './ILoginComponentProps'
import { ILoginComponentState } from './ILoginComponentState' import { ILoginComponentState } from './ILoginComponentState'
import { OAuthType } from 'src/core/domain/authorize' import { OAuthType } from 'src/core/domain/authorize'
import Grid from 'material-ui/Grid/Grid' import Grid from 'material-ui/Grid/Grid'
import CommonAPI from 'api/CommonAPI'
const styles = (theme: any) => ({ const styles = (theme: any) => ({
textField: { textField: {
@@ -253,9 +254,8 @@ const mapDispatchToProps = (dispatch: any, ownProps: ILoginComponentProps) => {
*/ */
const mapStateToProps = (state: any, ownProps: ILoginComponentProps) => { const mapStateToProps = (state: any, ownProps: ILoginComponentProps) => {
return { return {
translate: getTranslate(state.locale)
} }
} }
// - Connect component to redux store // - Connect component to redux store
export default withRouter<any>(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(LoginComponent as any) as any)) as typeof LoginComponent export default withRouter<any>(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(localize(LoginComponent, 'locale', CommonAPI.getStateSlice) as any) as any)) as typeof LoginComponent

View File

@@ -6,6 +6,8 @@ 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/Progress' import { LinearProgress } from 'material-ui/Progress'
import {Helmet} from 'react-helmet'
import {Map} from 'immutable'
// - Import components // - Import components
@@ -53,7 +55,6 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
} }
// Binding functions to `this` // Binding functions to `this`
this.handleLoading = this.handleLoading.bind(this)
this.handleMessage = this.handleMessage.bind(this) this.handleMessage = this.handleMessage.bind(this)
} }
@@ -63,14 +64,6 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
this.props.closeMessage() this.props.closeMessage()
} }
// Handle loading
handleLoading = (status: boolean) => {
this.setState({
loading: status,
authed: false
})
}
componentDidCatch (error: any, info: any) { componentDidCatch (error: any, info: any) {
console.log('===========Catched by React componentDidCatch==============') console.log('===========Catched by React componentDidCatch==============')
console.log(error, info) console.log(error, info)
@@ -130,6 +123,11 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
return ( return (
<div id='master'> <div id='master'>
<Helmet>
<meta charSet='utf-8' />
<title>React Social Network</title>
<link rel='canonical' href='https://github.com/Qolzam/react-social-network' />
</Helmet>
{sendFeedbackStatus ? <SendFeedback /> : ''} {sendFeedbackStatus ? <SendFeedback /> : ''}
<div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}> <div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}>
<LinearProgress variant='determinate' value={progress.percent} /> <LinearProgress variant='determinate' value={progress.percent} />
@@ -137,7 +135,7 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
<div className='master__loading animate-fading2' style={{ display: (global.showTopLoading ? 'flex' : 'none') }}> <div className='master__loading animate-fading2' style={{ display: (global.showTopLoading ? 'flex' : 'none') }}>
<div className='title'>Loading ... </div> <div className='title'>Loading ... </div>
</div> </div>
<MasterLoading activeLoading={global.showMasterLoading} handleLoading={this.handleLoading} /> {progress.visible ? <MasterLoading /> : ''}
<MasterRouter enabled={!loading} data={{uid}} /> <MasterRouter enabled={!loading} data={{uid}} />
<Snackbar <Snackbar
open={this.props.global.messageOpen} open={this.props.global.messageOpen}
@@ -196,15 +194,16 @@ const mapDispatchToProps = (dispatch: any, ownProps: IMasterComponentProps) => {
* Map state to props * Map state to props
* @param {object} state * @param {object} state
*/ */
const mapStateToProps = (state: any) => { const mapStateToProps = (state: Map<string, any>) => {
const { authorize, global, user, post, comment, imageGallery, vote, notify, circle } = state const authorize = Map(state.get('authorize', {})).toJS()
const { sendFeedbackStatus } = global const global = Map(state.get('global', {})).toJS()
const { sendFeedbackStatus, progress } = global
return { return {
sendFeedbackStatus, sendFeedbackStatus,
progress,
guest: authorize.guest, guest: authorize.guest,
uid: authorize.uid, uid: authorize.uid,
authed: authorize.authed, authed: authorize.authed,
progress: global.progress,
global: global global: global
} }

View File

@@ -9,6 +9,7 @@ import { push } from 'react-router-redux'
import AppBar from 'material-ui/AppBar' import AppBar from 'material-ui/AppBar'
import Typography from 'material-ui/Typography' import Typography from 'material-ui/Typography'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components // - Import app components
import FindPeople from 'src/components/findPeople' import FindPeople from 'src/components/findPeople'
@@ -187,12 +188,12 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPeopleComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IPeopleComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IPeopleComponentProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
uid: state.authorize.uid, uid: state.getIn(['authorize', 'uid'], 0),
circlesLoaded: state.circle.loaded circlesLoaded: state.getIn(['circle', 'loaded'])
} }
} }

View File

@@ -2,6 +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 {Map} from 'immutable'
// - Import app components // - Import app components
import Stream from 'containers/stream' import Stream from 'containers/stream'
@@ -76,12 +77,15 @@ const mapDispatchToProps = (dispatch: any,ownProps: IPostPageComponentProps) =>
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any,ownProps: IPostPageComponentProps) => { const mapStateToProps = (state: Map<string, any>,ownProps: IPostPageComponentProps) => {
const {userId,postId} = ownProps.match.params const {userId,postId} = ownProps.match.params
const userInfo = state.getIn(['state', 'user', 'info', userId])
let posts: Map<string, Map<string, any>> = Map({})
posts = posts.set(postId, state.getIn(['post', 'userPosts', userId, postId], Map({})))
return{ return{
avatar: state.user.info && state.user.info[userId] ? state.user.info[userId].avatar : '', avatar: userInfo ? userInfo.avatar : '',
name: state.user.info && state.user.info[userId] ? state.user.info[userId].fullName : '', name: userInfo ? userInfo.fullName : '',
posts: state.post.userPosts && state.post.userPosts[userId] ? {[postId] : { ...state.post.userPosts[userId][postId]}} : {} posts
} }
} }

View File

@@ -6,6 +6,7 @@ import Dialog from 'material-ui/Dialog'
import Button from 'material-ui/Button' import Button from 'material-ui/Button'
import RaisedButton from 'material-ui/Button' import RaisedButton from 'material-ui/Button'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components // - Import app components
import ProfileHeader from 'src/components/profileHeader' import ProfileHeader from 'src/components/profileHeader'
@@ -19,6 +20,7 @@ import * as userActions from 'src/store/actions/userActions'
import * as globalActions from 'src/store/actions/globalActions' import * as globalActions from 'src/store/actions/globalActions'
import { IProfileComponentProps } from './IProfileComponentProps' import { IProfileComponentProps } from './IProfileComponentProps'
import { IProfileComponentState } from './IProfileComponentState' import { IProfileComponentState } from './IProfileComponentState'
import { Profile } from 'core/domain/users'
/** /**
* Create component class * Create component class
@@ -78,13 +80,13 @@ export class ProfileComponent extends Component<IProfileComponentProps,IProfileC
} }
const {loadPosts, hasMorePosts, translate} = this.props const {loadPosts, hasMorePosts, translate} = this.props
const St = StreamComponent as any const St = StreamComponent as any
const posts = Map(this.props.posts)
return ( return (
<div style={styles.profile}> <div style={styles.profile}>
<div style={styles.header}> <div style={styles.header}>
<ProfileHeader tagLine={this.props.tagLine} avatar={this.props.avatar} isAuthedUser={this.props.isAuthedUser} banner={this.props.banner} fullName={this.props.name} followerCount={0} userId={this.props.userId}/> <ProfileHeader tagLine={this.props.tagLine} avatar={this.props.avatar} isAuthedUser={this.props.isAuthedUser} banner={this.props.banner} fullName={this.props.name} followerCount={0} userId={this.props.userId}/>
</div> </div>
{this.props.posts && Object.keys(this.props.posts).length !== 0 {posts
? (<div style={styles.content}> ? (<div style={styles.content}>
<div className='profile__title'> <div className='profile__title'>
{translate!('profile.headPostsLabel', {userName: this.props.name})} {translate!('profile.headPostsLabel', {userName: this.props.name})}
@@ -92,7 +94,7 @@ export class ProfileComponent extends Component<IProfileComponentProps,IProfileC
<div style={{ height: '24px' }}></div> <div style={{ height: '24px' }}></div>
<St <St
posts={this.props.posts} posts={posts}
loadStream={loadPosts} loadStream={loadPosts}
hasMorePosts={hasMorePosts} hasMorePosts={hasMorePosts}
displayWriting={false} /> displayWriting={false} />
@@ -128,17 +130,18 @@ const mapDispatchToProps = (dispatch: any, ownProps: IProfileComponentProps) =>
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IProfileComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IProfileComponentProps) => {
const { userId } = ownProps.match.params const { userId } = ownProps.match.params
const {uid} = state.authorize const uid = state.getIn(['authorize', 'uid'], 0)
const hasMorePosts = state.post.profile.hasMoreData const hasMorePosts = state.getIn(['post', 'profile', 'hasMoreData'])
const posts = state.post.userPosts ? state.post.userPosts[userId] : {} const posts = state.getIn(['post', 'userPosts', userId])
const userProfile = state.getIn(['user', 'info', userId], {}) as Profile
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
avatar: state.user.info && state.user.info[userId] ? state.user.info[userId].avatar || '' : '', avatar: userProfile.avatar,
name: state.user.info && state.user.info[userId] ? state.user.info[userId].fullName || '' : '', name: userProfile.fullName,
banner: state.user.info && state.user.info[userId] ? state.user.info[userId].banner || '' : '', banner: userProfile.banner,
tagLine: state.user.info && state.user.info[userId] ? state.user.info[userId].tagLine || '' : '', tagLine: userProfile.tagLine,
isAuthedUser: userId === uid, isAuthedUser: userId === uid,
userId, userId,
posts, posts,

View File

@@ -179,7 +179,7 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IResetPasswordComponen
*/ */
const mapStateToProps = (state: any, ownProps: IResetPasswordComponentProps) => { const mapStateToProps = (state: any, ownProps: IResetPasswordComponentProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
} }
} }

View File

@@ -225,7 +225,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: ISettingComponentProps) =>
*/ */
const mapStateToProps = (state: any, ownProps: ISettingComponentProps) => { const mapStateToProps = (state: any, ownProps: ISettingComponentProps) => {
return { return {
translate: getTranslate(state.locale) translate: getTranslate(state.get('locale'))
} }
} }

View File

@@ -287,7 +287,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: ISignupComponentProps) => {
*/ */
const mapStateToProps = (state: any, ownProps: ISignupComponentProps) => { const mapStateToProps = (state: any, ownProps: ISignupComponentProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
} }
} }

View File

@@ -1,4 +1,5 @@
import { Post } from 'src/core/domain/posts' import { Post } from 'src/core/domain/posts'
import {Map} from 'immutable'
export interface IStreamComponentProps { export interface IStreamComponentProps {
@@ -82,7 +83,7 @@ export interface IStreamComponentProps {
* @type {{[postId: string]: Post}} * @type {{[postId: string]: Post}}
* @memberof IStreamComponentProps * @memberof IStreamComponentProps
*/ */
posts: {[postId: string]: Post} posts: Map<string, Map<string, any>>
/** /**
* Router match property * Router match property

View File

@@ -10,6 +10,7 @@ import Paper from 'material-ui/Paper'
import List, { ListItem, ListItemIcon, ListItemText } 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 { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map, List as ImuList } from 'immutable'
// - Import app components // - Import app components
import PostComponent from 'src/components/post' import PostComponent from 'src/components/post'
@@ -128,56 +129,58 @@ export class StreamComponent extends Component<IStreamComponentProps, IStreamCom
*/ */
postLoad = () => { postLoad = () => {
let { posts, match } = this.props let { match } = this.props
let posts: Map<string, Map<string, any>> = this.props.posts
let { tag } = match.params let { tag } = match.params
if (posts === undefined || !(Object.keys(posts).length > 0)) { if (posts === undefined || !(posts.keySeq().count() > 0)) {
return ( return (
<h1> <h1>
'Nothing has shared.' 'Nothing has shared.'
</h1> </h1>
) )
} else { } else {
let postBack = { divided: false, oddPostList: [], evenPostList: [] } let postBack = { divided: false, oddPostList: [], evenPostList: [] }
let parsedPosts: any = [] let parsedPosts: ImuList<any> = ImuList()
Object.keys(posts).forEach((postId) => { posts.forEach((post: Map<string, any>) => {
if (tag) { if (tag) {
let regex = new RegExp('#' + tag, 'g') let regex = new RegExp('#' + tag, 'g')
let postMatch = posts[postId].body!.match(regex) let postMatch = String(post.get('body', '')).match(regex)
if (postMatch !== null) { if (postMatch !== null) {
parsedPosts.push({ ...posts[postId] }) parsedPosts = parsedPosts.push(post)
} }
} else { } else {
parsedPosts.push({ ...posts[postId] }) parsedPosts = parsedPosts.push(post)
} }
}) })
const sortedPosts = PostAPI.sortObjectsDate(parsedPosts) const sortedPosts = PostAPI.sortImuObjectsDate(parsedPosts)
if (sortedPosts.length > 6) { if (sortedPosts.count() > 6) {
postBack.divided = true postBack.divided = true
} else { } else {
postBack.divided = false postBack.divided = false
} }
sortedPosts.forEach((post: Post, index: any) => { let index = 0
sortedPosts.forEach((post) => {
let newPost: any = ( let newPost: any = (
<div key={`${post.id!}-stream-div`}> <div key={`${post!.get('id')!}-stream-div`}>
{index > 1 || (!postBack.divided && index > 0) ? <div style={{ height: '16px' }}></div> : ''} {index > 1 || (!postBack.divided && index > 0) ? <div style={{ height: '16px' }}></div> : ''}
<PostComponent key={`${post.id!}-stream-div-post`} post={post} /> <PostComponent key={`${post!.get('id')}-stream-div-post`} post={post! as any} />
</div> </div>
) )
if ((index % 2) === 1 && postBack.divided) { if ((index % 2) === 1 && postBack.divided) {
postBack.oddPostList.push(newPost as never) postBack.oddPostList.push(newPost as never)
} else { } else {
postBack.evenPostList.push(newPost as never) postBack.evenPostList.push(newPost as never)
} }
++index
}) })
return postBack return postBack
} }
@@ -276,14 +279,15 @@ const mapDispatchToProps = (dispatch: any, ownProps: IStreamComponentProps) => {
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IStreamComponentProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IStreamComponentProps) => {
const { post } = state const uid = state.getIn(['authorize', 'uid'])
const user = state.getIn(['user', 'info', uid])
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '', avatar: user.avatar || '',
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : '' fullName: user.fullName || ''
} }
} }
// - Connect component to redux store // - Connect component to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(StreamComponent as any) as any) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(StreamComponent as any) as any) as typeof StreamComponent

View File

@@ -1,24 +0,0 @@
import React, { Component } from 'react'
const asyncComponent = (importComponent: any) => {
return class extends Component {
state = {
component: null
}
componentDidMount () {
importComponent()
.then((cmp: any) => {
this.setState({component: cmp.default})
})
}
render () {
const C: any = this.state.component
return C ? <C {...this.props} /> : null
}
}
}
export default asyncComponent

View File

@@ -1,29 +1,31 @@
// - Import react components // - Import react components
import PrivateRoute from './PrivateRoute' import PrivateRoute from './PrivateRoute'
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom' import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import Loadable from 'react-loadable'
import { Map } from 'immutable'
import asyncComponent from 'hoc/asyncComponent/asyncComponent'
import { IRouterProps } from './IRouterProps' import { IRouterProps } from './IRouterProps'
import MasterLoadingComponent from 'components/masterLoading/MasterLoadingComponent'
// - Async Components // - Async Components
const AsyncStream = asyncComponent(() => { const AsyncStream = Loadable({
return import('containers/stream') loader: () => import('containers/stream'),
loading: MasterLoadingComponent,
}) })
const AsyncProfile = Loadable({
const AsyncProfile = asyncComponent(() => { loader: () => import('containers/profile'),
return import('containers/profile') loading: MasterLoadingComponent,
}) })
const AsyncPostPage = Loadable({
const AsyncPostPage = asyncComponent(() => { loader: () => import('containers/postPage'),
return import('containers/postPage') loading: MasterLoadingComponent,
}) })
const AsyncPeople = Loadable({
const AsyncPeople = asyncComponent(() => { loader: () => import('containers/people'),
return import('containers/people') loading: MasterLoadingComponent,
}) })
/** /**
@@ -32,7 +34,7 @@ const AsyncPeople = asyncComponent(() => {
export class HomeRouter extends Component<IRouterProps, any> { export class HomeRouter extends Component<IRouterProps, any> {
render () { render () {
const { enabled, match, data, translate } = this.props const { enabled, match, data, translate } = this.props
const St = AsyncStream as any const St = AsyncStream
return ( return (
enabled ? ( enabled ? (
<Switch> <Switch>
@@ -71,10 +73,10 @@ const mapDispatchToProps = (dispatch: any, ownProps: IRouterProps) => {
/** /**
* Map state to props * Map state to props
*/ */
const mapStateToProps = (state: any, ownProps: IRouterProps) => { const mapStateToProps = (state: Map<string, any>, ownProps: IRouterProps) => {
return { return {
translate: getTranslate(state.locale), translate: getTranslate(state.get('locale')),
currentLanguage: getActiveLanguage(state.locale).code, currentLanguage: getActiveLanguage(state.get('locale')).code,
} }
} }

View File

@@ -5,33 +5,35 @@ 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 { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom' import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
import Loadable from 'react-loadable'
import asyncComponent from 'hoc/asyncComponent/asyncComponent'
import { IRouterProps } from './IRouterProps' import { IRouterProps } from './IRouterProps'
import MasterLoadingComponent from 'components/masterLoading/MasterLoadingComponent'
// - Async Components // - Async Components
const AsyncHome: any = asyncComponent(() => { const AsyncHome: any = Loadable({
return import('containers/home') loader: () => import('containers/home'),
loading: MasterLoadingComponent,
}) })
const AsyncSignup = Loadable({
const AsyncSignup = asyncComponent(() => { loader: () => import('containers/signup'),
return import('containers/signup') loading: MasterLoadingComponent,
}) })
const AsyncEmailVerification = Loadable({
const AsyncEmailVerification = asyncComponent(() => { loader: () => import('containers/emailVerification'),
return import('containers/emailVerification') loading: MasterLoadingComponent,
}) })
const AsyncResetPassword = Loadable({
const AsyncResetPassword = asyncComponent(() => { loader: () => import('containers/resetPassword'),
return import('containers/resetPassword') loading: MasterLoadingComponent,
}) })
const AsyncLogin = Loadable({
const AsyncLogin = asyncComponent(() => { loader: () => import('containers/login'),
return import('containers/login') loading: MasterLoadingComponent,
}) })
const AsyncSetting = Loadable({
const AsyncSetting = asyncComponent(() => { loader: () => import('containers/setting'),
return import('containers/setting') loading: MasterLoadingComponent,
}) })
/** /**

View File

@@ -2,6 +2,7 @@ import React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { Route, Redirect } from 'react-router-dom' import { Route, Redirect } from 'react-router-dom'
import { IRoute } from './IRoute' import { IRoute } from './IRoute'
import { Map } from 'immutable'
export class PrivateRoute extends Component<IRoute, any> { export class PrivateRoute extends Component<IRoute, any> {
@@ -19,10 +20,10 @@ export class PrivateRoute extends Component<IRoute, any> {
} }
} }
const mapStateToProps = (state: any, nexProps: IRoute) => { const mapStateToProps = (state: Map<string, any>, nexProps: IRoute) => {
const { authorize } = state
return { return {
authed: authorize.authed authed: state.getIn(['authorize', 'authed'])
} }
} }

View File

@@ -2,6 +2,7 @@ import React, { Component } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { Route, Redirect } from 'react-router-dom' import { Route, Redirect } from 'react-router-dom'
import { IRoute } from './IRoute' import { IRoute } from './IRoute'
import {Map} from 'immutable'
export class PublicRoute extends Component<IRoute, any> { export class PublicRoute extends Component<IRoute, any> {
@@ -19,10 +20,10 @@ export class PublicRoute extends Component<IRoute, any> {
} }
} }
const mapStateToProps = (state: any, nexProps: IRoute) => { const mapStateToProps = (state: Map<string, any>, nexProps: IRoute) => {
const { authorize } = state
return { return {
authed: authorize.authed authed: state.getIn(['authorize', 'authed', false])
} }
} }

View File

@@ -7,7 +7,7 @@ import CommonAPI from 'api/CommonAPI'
/** /**
* Developer tools * Developer tools
*/ */
console.trace = CommonAPI.logger window['console']['trace'] = CommonAPI.logger
/** /**
* Initialize container * Initialize container

View File

@@ -3,6 +3,7 @@ import { User } from 'src/core/domain/users'
import { Circle, UserTie } from 'src/core/domain/circles' import { Circle, UserTie } from 'src/core/domain/circles'
import { SocialError } from 'src/core/domain/common' import { SocialError } from 'src/core/domain/common'
import * as moment from 'moment/moment' import * as moment from 'moment/moment'
import { Map, List } from 'immutable'
// - Import action types // - Import action types
import { CircleActionType } from 'constants/circleActionType' import { CircleActionType } from 'constants/circleActionType'
@@ -37,7 +38,8 @@ const userTieService: IUserTieService = provider.get<IUserTieService>(SocialProv
export let dbAddCircle = (circleName: string) => { export let dbAddCircle = (circleName: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
let circle: Circle = { let circle: Circle = {
creationDate: moment().unix(), creationDate: moment().unix(),
name: circleName, name: circleName,
@@ -47,7 +49,7 @@ export let dbAddCircle = (circleName: string) => {
return circleService.addCircle(uid, circle).then((circleKey: string) => { return circleService.addCircle(uid, circle).then((circleKey: string) => {
circle.id = circleKey circle.id = circleKey
circle.ownerId = uid circle.ownerId = uid
dispatch(addCircle(circle)) dispatch(addCircle(Map(circle)))
}, (error: SocialError) => dispatch(globalActions.showMessage(error.message))) }, (error: SocialError) => dispatch(globalActions.showMessage(error.message)))
@@ -59,9 +61,9 @@ export let dbAddCircle = (circleName: string) => {
*/ */
export const dbFollowUser = (followingCircleId: string, userFollowing: UserTie) => { export const dbFollowUser = (followingCircleId: string, userFollowing: UserTie) => {
return (dispatch: Function, getState: Function) => { return (dispatch: Function, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
let user: User = { ...state.user.info[uid], userId: uid } let user: User = { ...state.getIn(['user', 'info', uid]), userId: uid }
// Set server request status to {Sent} for following user // Set server request status to {Sent} for following user
const followReqestModel = createFollowRequest(userFollowing.userId!) const followReqestModel = createFollowRequest(userFollowing.userId!)
@@ -74,15 +76,16 @@ export const dbFollowUser = (followingCircleId: string, userFollowing: UserTie)
[followingCircleId] [followingCircleId]
) )
.then(() => { .then(() => {
dispatch(addFollowingUser( let userTie: Map<string, any> = Map(new UserTie(
new UserTie( userFollowing.userId!,
userFollowing.userId!, moment().unix(),
moment().unix(), userFollowing.fullName,
userFollowing.fullName, userFollowing.avatar,
userFollowing.avatar, false,
false, ))
[followingCircleId] userTie = userTie.set('circleIdList', List([followingCircleId]))
)))
dispatch(addFollowingUser(userTie))
// Set server request status to {OK} for following user // Set server request status to {OK} for following user
followReqestModel.status = ServerRequestStatusType.OK followReqestModel.status = ServerRequestStatusType.OK
@@ -111,11 +114,11 @@ export const dbFollowUser = (followingCircleId: string, userFollowing: UserTie)
/** /**
* Update user in circle/circles * Update user in circle/circles
*/ */
export let dbUpdateUserInCircles = (circleIdList: string[], userFollowing: UserTie) => { export let dbUpdateUserInCircles = (circleIdList: List<string>, userFollowing: UserTie) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
let user: User = { ...state.user.info[uid], userId: uid } let user: User = { ...state.getIn(['user', 'info', uid]), userId: uid }
// Set server request status to {Sent} // Set server request status to {Sent}
const addToCircleRequest = createAddToCircleRequest(userFollowing.userId!) const addToCircleRequest = createAddToCircleRequest(userFollowing.userId!)
@@ -127,18 +130,18 @@ export let dbUpdateUserInCircles = (circleIdList: string[], userFollowing: UserT
return userTieService.updateUsersTie( return userTieService.updateUsersTie(
{ userId: user.userId!, fullName: user.fullName, avatar: user.avatar, approved: false }, { userId: user.userId!, fullName: user.fullName, avatar: user.avatar, approved: false },
{ userId: userFollowing.userId!, fullName: userFollowing.fullName, avatar: userFollowing.avatar, approved: false }, { userId: userFollowing.userId!, fullName: userFollowing.fullName, avatar: userFollowing.avatar, approved: false },
circleIdList circleIdList.toJS()
) )
.then(() => { .then(() => {
dispatch(addFollowingUser( let userTie: Map<string, any> = Map(new UserTie(
new UserTie( userFollowing.userId!,
userFollowing.userId!, moment().unix(),
moment().unix(), userFollowing.fullName,
userFollowing.fullName, userFollowing.avatar,
userFollowing.avatar, false
false, ))
circleIdList userTie = userTie.set('circleIdList', circleIdList)
))) dispatch(addFollowingUser(userTie))
// Set server request status to {OK} // Set server request status to {OK}
addToCircleRequest.status = ServerRequestStatusType.OK addToCircleRequest.status = ServerRequestStatusType.OK
@@ -167,7 +170,8 @@ export let dbUpdateUserInCircles = (circleIdList: string[], userFollowing: UserT
export let dbDeleteFollowingUser = (userFollowingId: string) => { export let dbDeleteFollowingUser = (userFollowingId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
// Set server request status to {Sent} // Set server request status to {Sent}
const deleteFollowingUserRequest = createdeleteFollowingUserRequest(userFollowingId) const deleteFollowingUserRequest = createdeleteFollowingUserRequest(userFollowingId)
@@ -208,16 +212,17 @@ export let dbDeleteFollowingUser = (userFollowingId: string) => {
*/ */
export const dbUpdateCircle = (newCircle: Circle) => { export const dbUpdateCircle = (newCircle: Circle) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
// Get current user id // Get current user id
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
// Write the new data simultaneously in the list // Write the new data simultaneously in the list
let circle: Circle = {...getState().circle.circleList[newCircle.id!]} let circle: Map<string, any> = state.getIn(['circle', 'circleList', newCircle.id!])
circle.name = newCircle.name circle = circle.set('name', newCircle.name)
return circleService.updateCircle(uid, newCircle.id!, circle) return circleService.updateCircle(uid, newCircle.id!, circle.toJS())
.then(() => { .then(() => {
dispatch(updateCircle({ id: newCircle.id, ...circle })) circle = circle.set('id', newCircle.id)
dispatch(updateCircle(circle))
}, (error: SocialError) => { }, (error: SocialError) => {
dispatch(globalActions.showMessage(error.message)) dispatch(globalActions.showMessage(error.message))
}) })
@@ -232,7 +237,8 @@ export const dbDeleteCircle = (circleId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
// Get current user id // Get current user id
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
return circleService.deleteCircle(uid, circleId) return circleService.deleteCircle(uid, circleId)
.then(() => { .then(() => {
@@ -249,7 +255,8 @@ export const dbDeleteCircle = (circleId: string) => {
*/ */
export const dbGetCircles = () => { export const dbGetCircles = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
return circleService.getCircles(uid) return circleService.getCircles(uid)
@@ -269,7 +276,8 @@ export const dbGetCircles = () => {
*/ */
export const dbGetUserTies = () => { export const dbGetUserTies = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
userTieService.getUserTies(uid).then((result) => { userTieService.getUserTies(uid).then((result) => {
@@ -289,7 +297,8 @@ export const dbGetUserTies = () => {
*/ */
export const dbGetFollowers = () => { export const dbGetFollowers = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
userTieService.getUserTieSender(uid).then((result) => { userTieService.getUserTieSender(uid).then((result) => {
@@ -367,7 +376,7 @@ const createdeleteFollowingUserRequest = (userFollowingId: string) => {
/** /**
* Add a circle * Add a circle
*/ */
export const addCircle = (circle: Circle) => { export const addCircle = (circle: Map<string, any>) => {
return { return {
type: CircleActionType.ADD_CIRCLE, type: CircleActionType.ADD_CIRCLE,
payload: { circle } payload: { circle }
@@ -377,7 +386,7 @@ export const addCircle = (circle: Circle) => {
/** /**
* Update a circle * Update a circle
*/ */
export const updateCircle = (circle: Circle) => { export const updateCircle = (circle: Map<string, any>) => {
return { return {
type: CircleActionType.UPDATE_CIRCLE, type: CircleActionType.UPDATE_CIRCLE,
payload: { circle } payload: { circle }
@@ -438,7 +447,7 @@ export const closeCircleSettings = (circleId: string) => {
/** /**
* Add following user * Add following user
*/ */
export const addFollowingUser = (userTie: UserTie) => { export const addFollowingUser = (userTie: Map<string, any>) => {
return { return {
type: CircleActionType.ADD_FOLLOWING_USER, type: CircleActionType.ADD_FOLLOWING_USER,
payload: { userTie } payload: { userTie }

View File

@@ -1,6 +1,7 @@
// - Import react components // - Import react components
import moment from 'moment/moment' import moment from 'moment/moment'
import _ from 'lodash' import _ from 'lodash'
import {Map} from 'immutable'
// - Import domain // - Import domain
import { Comment } from 'src/core/domain/comments' import { Comment } from 'src/core/domain/comments'
@@ -40,14 +41,14 @@ export const dbAddComment = (ownerPostUserId: string, newComment: Comment, callB
dispatch(globalActions.showTopLoading()) dispatch(globalActions.showTopLoading())
const state = getState() const state: Map<string, any> = getState()
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
const currentUser = state.getIn(['user', 'info', uid])
let comment: Comment = { let comment: Comment = {
score: 0, score: 0,
creationDate: moment().unix(), creationDate: moment().unix(),
userDisplayName: state.user.info[uid].fullName, userDisplayName: currentUser.fullName,
userAvatar: state.user.info[uid].avatar, userAvatar: currentUser.avatar,
userId: uid, userId: uid,
postId: newComment.postId, postId: newComment.postId,
text: newComment.text text: newComment.text

View File

@@ -1,6 +1,7 @@
// - Import image gallery action types // - Import image gallery action types
import { GlobalActionType } from 'constants/globalActionType' import { GlobalActionType } from 'constants/globalActionType'
import { getTranslate, getActiveLanguage } from 'react-localize-redux' import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map } from 'immutable'
// - Import actions // - Import actions
import * as serverActions from 'store/actions/serverActions' import * as serverActions from 'store/actions/serverActions'
@@ -24,8 +25,8 @@ const commonService: ICommonService = provider.get<ICommonService>(SocialProvide
*/ */
export let dbSendFeed = (newFeed: Feed) => { export let dbSendFeed = (newFeed: Feed) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state: Map<string, any> = getState()
let uid: string = getState().authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
// Set server request status to {Sent} // Set server request status to {Sent}
const feedbackRequest = createFeedbackRequest(uid) const feedbackRequest = createFeedbackRequest(uid)
@@ -49,8 +50,8 @@ export let dbSendFeed = (newFeed: Feed) => {
// - Show notification of request // - Show notification of request
export const showNotificationRequest = () => { export const showNotificationRequest = () => {
return (dispatch: Function, getState: Function) => { return (dispatch: Function, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
const translate = getTranslate(state.locale) const translate = getTranslate(state.get('locale'))
return dispatch(showMessage(String(translate('common.sentRequestMessage')))) return dispatch(showMessage(String(translate('common.sentRequestMessage'))))
} }
} }
@@ -58,8 +59,8 @@ export const showNotificationRequest = () => {
// - Show notification of success // - Show notification of success
export const showNotificationSuccess = () => { export const showNotificationSuccess = () => {
return (dispatch: Function, getState: Function) => { return (dispatch: Function, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
const translate = getTranslate(state.locale) const translate = getTranslate(state.get('locale'))
return dispatch(showMessage(String(translate('common.successfulRequestMessage')))) return dispatch(showMessage(String(translate('common.successfulRequestMessage'))))
} }
} }

View File

@@ -1,5 +1,6 @@
// - Import react componetns // - Import react componetns
import moment from 'moment/moment' import moment from 'moment/moment'
import { Map } from 'immutable'
// - Import domain // - Import domain
import { Image } from 'src/core/domain/imageGallery' import { Image } from 'src/core/domain/imageGallery'
@@ -28,7 +29,8 @@ const imageGalleryService: IImageGalleryService = provider.get<IImageGalleryServ
*/ */
export const dbGetImageGallery = () => { export const dbGetImageGallery = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
return imageGalleryService.getImageGallery(uid) return imageGalleryService.getImageGallery(uid)
@@ -51,7 +53,8 @@ export const dbGetImageGallery = () => {
export const dbSaveImage = (imageURL: string,imageFullPath: string) => { export const dbSaveImage = (imageURL: string,imageFullPath: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
let image: Image = { let image: Image = {
creationDate: moment().unix(), creationDate: moment().unix(),
deleteDate: '', deleteDate: '',
@@ -82,7 +85,8 @@ export const dbDeleteImage = (id: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
// Get current user id // Get current user id
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
return imageGalleryService.deleteImage(uid,id) return imageGalleryService.deleteImage(uid,id)
.then(() => { .then(() => {

View File

@@ -2,6 +2,7 @@
// - Import domain // - Import domain
import { Notification } from 'src/core/domain/notifications' import { Notification } from 'src/core/domain/notifications'
import { SocialError } from 'src/core/domain/common' import { SocialError } from 'src/core/domain/common'
import { Map, fromJS } from 'immutable'
// - Import action types // - Import action types
import { NotificationActionType } from 'constants/notificationActionType' import { NotificationActionType } from 'constants/notificationActionType'
@@ -23,7 +24,6 @@ const notificationService: INotificationService = provider.get<INotificationServ
/** /**
* Add notificaition to database * Add notificaition to database
* @param {object} newNotify user notificaition
*/ */
export const dbAddNotification = (newNotify: Notification) => { export const dbAddNotification = (newNotify: Notification) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
@@ -50,16 +50,17 @@ export const dbAddNotification = (newNotify: Notification) => {
*/ */
export const dbGetNotifications = () => { export const dbGetNotifications = () => {
return (dispatch: Function , getState: Function) => { return (dispatch: Function , getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
return notificationService.getNotifications(uid, return notificationService.getNotifications(uid,
(notifications: { [notifyId: string]: Notification} ) => { (notifications: { [notifyId: string]: Notification} ) => {
Object.keys(notifications).forEach((key => { Object.keys(notifications).forEach((key => {
if (!getState().user.info[notifications[key].notifierUserId]) { if (!state.getIn(['user', 'info', 'notifications', 'key','notifierUserId'])) {
dispatch(userActions.dbGetUserInfoByUserId(notifications[key].notifierUserId,'')) dispatch(userActions.dbGetUserInfoByUserId(notifications[key].notifierUserId,''))
} }
})) }))
dispatch(addNotifyList(notifications)) dispatch(addNotifyList(fromJS(notifications)))
}) })
} }
} }
@@ -73,7 +74,8 @@ export const dbDeleteNotification = (id: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
// Get current user id // Get current user id
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
return notificationService.deleteNotification(id,uid).then(() => { return notificationService.deleteNotification(id,uid).then(() => {
dispatch(deleteNotify(id)) dispatch(deleteNotify(id))
@@ -90,14 +92,14 @@ export const dbDeleteNotification = (id: string) => {
export const dbSeenNotification = (id: string) => { export const dbSeenNotification = (id: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
// Get current user id const state: Map<string, any> = getState()
let uid: string = getState().authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
let notify: Notification = getState().notify.userNotifies[id] let notify: Map<string, any> = state.getIn(['notify', 'userNotifies', id])
let updatedNotification: Notification = { let updatedNotification: Notification = {
description: notify.description, description: notify.get('description'),
url: notify.url, url: notify.get('url'),
notifierUserId: notify.notifierUserId, notifierUserId: notify.get('notifierUserId'),
notifyRecieverUserId: uid, notifyRecieverUserId: uid,
isSeen: true isSeen: true
} }
@@ -127,7 +129,7 @@ export const addNotify = () => {
* Add notificaition list * Add notificaition list
* @param {[notifyId: string]: Notification} userNotifies an array of notificaitions * @param {[notifyId: string]: Notification} userNotifies an array of notificaitions
*/ */
export const addNotifyList = (userNotifies: {[notifyId: string]: Notification}) => { export const addNotifyList = (userNotifies: Map<string, any>) => {
return { return {
type: NotificationActionType.ADD_NOTIFY_LIST, type: NotificationActionType.ADD_NOTIFY_LIST,

View File

@@ -1,6 +1,7 @@
// - Import domain // - Import domain
import { Post } from 'src/core/domain/posts' import { Post } from 'src/core/domain/posts'
import { SocialError } from 'src/core/domain/common' import { SocialError } from 'src/core/domain/common'
import { Map } from 'immutable'
// - Import utility components // - Import utility components
import moment from 'moment/moment' import moment from 'moment/moment'
@@ -27,8 +28,8 @@ const postService: IPostService = provider.get<IPostService>(SocialProviderTypes
*/ */
export let dbAddPost = (newPost: Post, callBack: Function) => { export let dbAddPost = (newPost: Post, callBack: Function) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state: Map<string, any> = getState()
let uid: string = getState().authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
let post: Post = { let post: Post = {
postTypeId: 0, postTypeId: 0,
creationDate: moment().unix(), creationDate: moment().unix(),
@@ -70,8 +71,8 @@ export const dbAddImagePost = (newPost: Post, callBack: Function) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
dispatch(globalActions.showTopLoading()) dispatch(globalActions.showTopLoading())
const state: Map<string, any> = getState()
let uid: string = getState().authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
let post: Post = { let post: Post = {
postTypeId: 1, postTypeId: 1,
creationDate: moment().unix(), creationDate: moment().unix(),
@@ -111,12 +112,12 @@ export const dbAddImagePost = (newPost: Post, callBack: Function) => {
/** /**
* Update a post from database * Update a post from database
*/ */
export const dbUpdatePost = (updatedPost: Post, callBack: Function) => { export const dbUpdatePost = (updatedPost: Map<string, any>, callBack: Function) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
console.trace('update post ', updatedPost)
dispatch(globalActions.showTopLoading()) dispatch(globalActions.showTopLoading())
return postService.updatePost(updatedPost).then(() => { return postService.updatePost(updatedPost.toJS()).then(() => {
dispatch(updatePost(updatedPost)) dispatch(updatePost(updatedPost))
callBack() callBack()
@@ -141,8 +142,9 @@ export const dbDeletePost = (id: string) => {
dispatch(globalActions.showTopLoading()) dispatch(globalActions.showTopLoading())
const state: Map<string, any> = getState()
// Get current user id // Get current user id
let uid: string = getState().authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
return postService.deletePost(id).then(() => { return postService.deletePost(id).then(() => {
dispatch(deletePost(uid, id)) dispatch(deletePost(uid, id))
@@ -162,12 +164,12 @@ export const dbDeletePost = (id: string) => {
*/ */
export const dbGetPosts = (page: number = 0, limit: number = 10) => { export const dbGetPosts = (page: number = 0, limit: number = 10) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
const {stream} = state.post const stream: Map<string, any> = state.getIn(['post', 'stream'])
const lastPageRequest = stream.lastPageRequest const lastPageRequest = stream.get('lastPageRequest')
const lastPostId = stream.lastPostId const lastPostId = stream.get('lastPostId')
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
if (uid && lastPageRequest !== page) { if (uid && lastPageRequest !== page) {
return postService.getPosts(uid, lastPostId, page, limit).then((result) => { return postService.getPosts(uid, lastPostId, page, limit).then((result) => {
if (!result.posts || !(result.posts.length > 0)) { if (!result.posts || !(result.posts.length > 0)) {
@@ -207,12 +209,12 @@ export const dbGetPosts = (page: number = 0, limit: number = 10) => {
*/ */
export const dbGetPostsByUserId = (userId: string, page: number = 0, limit: number = 10) => { export const dbGetPostsByUserId = (userId: string, page: number = 0, limit: number = 10) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
const {profile} = state.post const {profile} = state.get('post')
const lastPageRequest = profile[userId] ? profile[userId].lastPageRequest : -1 const lastPageRequest = state.getIn(['post','profile', userId, 'lastPageRequest'], -1 )
const lastPostId = profile[userId] ? profile[userId].lastPostId : '' const lastPostId = state.getIn(['post','profile', userId, 'lastPostId'], '' )
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
if (uid && lastPageRequest !== page) { if (uid && lastPageRequest !== page) {
@@ -282,13 +284,33 @@ export const addPost = (uid: string, post: Post) => {
/** /**
* Update a post * Update a post
*/ */
export const updatePost = (post: Post) => { export const updatePost = (post: Map<string, any>) => {
return { return {
type: PostActionType.UPDATE_POST, type: PostActionType.UPDATE_POST,
payload: { post } payload: { post }
} }
} }
/**
* Update the comments of post
*/
export const updatePostComments = (comments: Map<string, any>) => {
return {
type: PostActionType.UPDATE_POST,
payload: comments
}
}
/**
* Update the votes of post
*/
export const updatePostVotes = (votes: Map<string, any>) => {
return {
type: PostActionType.UPDATE_POST,
payload: votes
}
}
/** /**
* Delete a post * Delete a post
*/ */

View File

@@ -1,5 +1,6 @@
export enum ServerRequestStatusType { export enum ServerRequestStatusType {
Sent = 'Sent', Sent = 'Sent',
NoAction = 'NoAction',
OK = 'OK', OK = 'OK',
Error = 'Error' Error = 'Error'
} }

View File

@@ -1,5 +1,7 @@
// - Import react components // - Import react components
import { provider } from 'src/socialEngine' import { provider } from 'src/socialEngine'
import { Map } from 'immutable'
// - Import domain // - Import domain
import { Profile } from 'src/core/domain/users' import { Profile } from 'src/core/domain/users'
import { SocialError } from 'src/core/domain/common' import { SocialError } from 'src/core/domain/common'
@@ -25,7 +27,8 @@ const userService: IUserService = provider.get<IUserService>(SocialProviderTypes
*/ */
export const dbGetUserInfo = () => { export const dbGetUserInfo = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
return userService.getUserProfile(uid).then((userProfile: Profile) => { return userService.getUserProfile(uid).then((userProfile: Profile) => {
dispatch(addUserInfo(uid, { dispatch(addUserInfo(uid, {
@@ -54,7 +57,8 @@ export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
return (dispatch: Function, getState: Function) => { return (dispatch: Function, getState: Function) => {
if (uid) { if (uid) {
let caller = getState().global.temp.caller const state: Map<string, any> = getState()
let caller = state.getIn(['global', 'temp', 'caller'])
if ( caller && caller.indexOf(`dbGetUserInfoByUserId-${uid}`) > -1) { if ( caller && caller.indexOf(`dbGetUserInfoByUserId-${uid}`) > -1) {
return undefined return undefined
} }
@@ -95,11 +99,10 @@ export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
*/ */
export const dbUpdateUserInfo = (newProfile: Profile) => { export const dbUpdateUserInfo = (newProfile: Profile) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
console.trace('newProfile', newProfile) const state: Map<string, any> = getState()
// Get current user id let uid: string = state.getIn(['authorize', 'uid'])
let uid: string = getState().authorize.uid
let profile: Profile = getState().user.info[uid] let profile: Profile = state.getIn(['user', 'info', uid])
let updatedProfile: Profile = { let updatedProfile: Profile = {
avatar: newProfile.avatar || profile.avatar || '', avatar: newProfile.avatar || profile.avatar || '',
banner: newProfile.banner || profile.banner || 'https://firebasestorage.googleapis.com/v0/b/open-social-33d92.appspot.com/o/images%2F751145a1-9488-46fd-a97e-04018665a6d3.JPG?alt=media&token=1a1d5e21-5101-450e-9054-ea4a20e06c57', banner: newProfile.banner || profile.banner || 'https://firebasestorage.googleapis.com/v0/b/open-social-33d92.appspot.com/o/images%2F751145a1-9488-46fd-a97e-04018665a6d3.JPG?alt=media&token=1a1d5e21-5101-450e-9054-ea4a20e06c57',
@@ -126,33 +129,27 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
// - Get people info from database // - Get people info from database
export const dbGetPeopleInfo = (page: number, limit: number) => { export const dbGetPeopleInfo = (page: number, limit: number) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
const {people} = state.user const people: Map<string, any> = state.getIn(['user', 'people'])
const lastPageRequest = people.lastPageRequest const lastPageRequest = people.get('lastPageRequest')
const lastUserId = people.lastUserId const lastUserId = people.get('lastUserId')
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
if (uid && lastPageRequest !== page) { if (uid && lastPageRequest !== page) {
return userService.getUsersProfile(uid, lastUserId, page, limit).then((result) => { return userService.getUsersProfile(uid, lastUserId, page, limit).then((result) => {
if (!result.users || !(result.users.length > 0)) { if (!result.users || !(result.users.length > 0)) {
return dispatch(notMoreDataPeople()) return dispatch(notMoreDataPeople())
} }
// Store last user Id // Store last user Id
dispatch(lastUserPeople(result.newLastUserId)) dispatch(lastUserPeople(result.newLastUserId))
let parsedData: {[userId: string]: Profile} = {} let parsedData: Map<string, Profile> = Map({})
result.users.forEach((post) => { result.users.forEach((user) => {
const userId = Object.keys(post)[0] const userId = Object.keys(user)[0]
const postData = post[userId] const userData = user[userId]
parsedData = { parsedData = parsedData.set(userId, userData)
...parsedData,
[userId]: {
...postData
}
}
}) })
dispatch(addPeopleInfo(parsedData)) dispatch(addPeopleInfo(parsedData))
}) })
@@ -176,9 +173,8 @@ export const addUserInfo = (uid: string, info: Profile) => {
/** /**
* Add people information * Add people information
* @param {[userId: string]: Profile} infoList is the lst of information about users
*/ */
export const addPeopleInfo = (infoList: {[userId: string]: Profile}) => { export const addPeopleInfo = (infoList: Map<string, Profile>) => {
return { return {
type: UserActionType.ADD_PEOPLE_INFO, type: UserActionType.ADD_PEOPLE_INFO,
payload: infoList payload: infoList

View File

@@ -1,4 +1,5 @@
import moment from 'moment/moment' import moment from 'moment/moment'
import {Map} from 'immutable'
// - Import action types // - Import action types
import { VoteActionType } from 'constants/voteActionType' import { VoteActionType } from 'constants/voteActionType'
@@ -31,20 +32,22 @@ const voteService: IVoteService = provider.get<IVoteService>(SocialProviderTypes
export const dbAddVote = (postId: string,ownerPostUserId: string) => { export const dbAddVote = (postId: string,ownerPostUserId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
let uid: string = state.authorize.uid let uid: string = state.getIn(['authorize', 'uid'])
const currentUser = state.getIn(['user', 'info', uid])
let vote: Vote = { let vote: Vote = {
postId: postId, postId: postId,
creationDate: moment().unix(), creationDate: moment().unix(),
userDisplayName: getState().user.info[uid].fullName, userDisplayName: currentUser.fullName,
userAvatar: getState().user.info[uid].avatar, userAvatar: currentUser.avatar,
userId: uid userId: uid
} }
const post: Post = state.post.userPosts[ownerPostUserId][postId] const post: Map<string, any> = state.getIn(['post', 'userPosts', ownerPostUserId, postId])
const score = Number(post.get('score', 0)) + 1
post.score! += 1 const votedPost = post
post.votes = { ...post.votes!, [uid]: true} .set('score', score)
dispatch(postActions.updatePost(post)) .setIn(['votes',uid], true)
dispatch(postActions.updatePost(votedPost))
return voteService.addVote(vote).then((voteKey: string) => { return voteService.addVote(vote).then((voteKey: string) => {
if (uid !== ownerPostUserId) { if (uid !== ownerPostUserId) {
@@ -59,9 +62,11 @@ export const dbAddVote = (postId: string,ownerPostUserId: string) => {
}) })
.catch((error) => { .catch((error) => {
post.score! -= 1 const score = post.get('score', 0) - 1
post.votes = { ...post.votes!, [uid]: false} const votedPost = post
dispatch(postActions.updatePost(post)) .set('score', score)
.setIn(['votes',uid], false)
dispatch(postActions.updatePost(votedPost))
dispatch(globalActions.showMessage(error.message)) dispatch(globalActions.showMessage(error.message))
}) })
} }
@@ -72,15 +77,15 @@ export const dbAddVote = (postId: string,ownerPostUserId: string) => {
*/ */
export const dbGetVotes = (userId: string, postId: string) => { export const dbGetVotes = (userId: string, postId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
if (uid) { if (uid) {
return voteService return voteService
.getVotes(postId) .getVotes(postId)
.then((postVotes: { [postId: string]: { [voteId: string]: Vote } }) => { .then((postVotes: { [postId: string]: { [voteId: string]: Vote } }) => {
dispatch(addVoteList(postVotes)) dispatch(addVoteList(postVotes))
const state = getState() const post: Post = state.getIn(['post', 'userPosts', userId, postId])
const post: Post = state.post.userPosts[userId][postId]
if (!post) { if (!post) {
return return
} }
@@ -101,18 +106,21 @@ export const dbGetVotes = (userId: string, postId: string) => {
*/ */
export const dbDeleteVote = (postId: string, ownerPostUserId: string) => { export const dbDeleteVote = (postId: string, ownerPostUserId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState() const state: Map<string, any> = getState()
// Get current user id let uid: string = state.getIn(['authorize', 'uid'])
let uid: string = state.authorize.uid const post: Map<string, any> = state.getIn(['post', 'userPosts', ownerPostUserId, postId])
const post: Post = state.post.userPosts[ownerPostUserId][postId] const score = post.get('score', 0) - 1
post.score! -= 1 const votedPost = post
post.votes = { ...post.votes!, [uid]: false} .set('score', score)
dispatch(postActions.updatePost(post)) .setIn(['votes',uid], false)
dispatch(postActions.updatePost(votedPost))
return voteService.deleteVote(uid, postId).then(x => x) return voteService.deleteVote(uid, postId).then(x => x)
.catch((error: any) => { .catch((error: any) => {
post.score! += 1 const score = post.get('score', 0) + 1
post.votes = { ...post.votes!, [uid]: true} const votedPost = post
dispatch(postActions.updatePost(post)) .set('score', score)
.setIn(['votes',uid], true)
dispatch(postActions.updatePost(votedPost))
dispatch(globalActions.showMessage(error.message)) dispatch(globalActions.showMessage(error.message))
}) })
} }

View File

@@ -1,17 +1,25 @@
// - Import external components // - Import external components
import * as redux from 'redux' import { createStore, applyMiddleware, compose, Store } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk' import thunk from 'redux-thunk'
import { routerMiddleware } from 'react-router-redux' import { routerMiddleware } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory' import createHistory from 'history/createBrowserHistory'
import createSagaMiddleware, { END } from 'redux-saga' import createSagaMiddleware, { END } from 'redux-saga'
import { createLogger } from 'redux-logger' import { createLogger } from 'redux-logger'
import { rootReducer } from 'store/reducers' import { rootReducer } from 'store/reducers'
import { fromJS, Iterable, Map } from 'immutable'
import DevTools from './devTools' import DevTools from './devTools'
// Create a history of your choosing (we're using a browser history in this case) // Create a history of your choosing (we're using a browser history in this case)
export const history = createHistory() export const history = createHistory()
// - Build the middleware for intercepting and dispatching navigation actions // Logger option for transforming immutable js
const logger = createLogger() const logger = createLogger({
stateTransformer: (state: Map<string, any>) => {
return state.toJS()
}
})
const sagaMiddleware = createSagaMiddleware() const sagaMiddleware = createSagaMiddleware()
// - initial state // - initial state
let initialState = { let initialState = {
@@ -19,9 +27,11 @@ let initialState = {
} }
// - Config and create store of redux // - Config and create store of redux
let store: redux.Store<any> = redux.createStore(rootReducer, initialState, redux.compose( const composeEnhancers = composeWithDevTools({
redux.applyMiddleware(logger,thunk, routerMiddleware(history), sagaMiddleware), // Specify extensions options like name, actionsBlacklist, actionsCreators, serialize...
DevTools.instrument() })
let store: Store<any> = createStore(rootReducer, fromJS(initialState), composeEnhancers(
applyMiddleware(logger,thunk, routerMiddleware(history), sagaMiddleware)
)) ))
export default {store, runSaga: sagaMiddleware.run, close: () => store.dispatch(END), history} export default {store, runSaga: sagaMiddleware.run, close: () => store.dispatch(END), history}

View File

@@ -5,6 +5,7 @@ import { routerMiddleware } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory' import createHistory from 'history/createBrowserHistory'
import createSagaMiddleware, { END } from 'redux-saga' import createSagaMiddleware, { END } from 'redux-saga'
import { rootReducer } from 'store/reducers' import { rootReducer } from 'store/reducers'
import { fromJS } from 'immutable'
// Create a history of your choosing (we're using a browser history in this case) // Create a history of your choosing (we're using a browser history in this case)
export const history = createHistory() export const history = createHistory()
@@ -16,7 +17,7 @@ let initialState = {
} }
// - Config and create store of redux // - Config and create store of redux
let store: redux.Store<any> = redux.createStore(rootReducer, initialState, redux.compose( let store: redux.Store<any> = redux.createStore(rootReducer, fromJS(initialState), redux.compose(
redux.applyMiddleware(thunk, routerMiddleware(history), sagaMiddleware) redux.applyMiddleware(thunk, routerMiddleware(history), sagaMiddleware)
)) ))

View File

@@ -6,41 +6,35 @@ import { AuthorizeActionType } from 'constants/authorizeActionType'
import { IAuthorizeAction } from './IAuthorizeAction' import { IAuthorizeAction } from './IAuthorizeAction'
import { AuthorizeState } from './AuthorizeState' import { AuthorizeState } from './AuthorizeState'
import { Map } from 'immutable'
/** /**
* Authorize reducer * Authorize reducer
* @param {object} state * @param {object} state
* @param {object} action * @param {object} action
*/ */
export let authorizeReducer = (state: AuthorizeState = new AuthorizeState(), action: IAuthorizeAction) => { export let authorizeReducer = (state = Map(new AuthorizeState()), action: IAuthorizeAction) => {
const { payload } = action const { payload } = action
switch (action.type) { switch (action.type) {
case AuthorizeActionType.LOGIN: case AuthorizeActionType.LOGIN:
return{ return state
...state, .set('uid', payload.uid)
uid: payload.uid, .set('authed', true)
authed: true, .set('guest', false)
guest: false, .set('isVerifide', payload.isVerifide)
isVerifide: payload.isVerifide
}
case AuthorizeActionType.LOGOUT:
return{
...state,
uid: 0,
authed: false,
guest: true
}
case AuthorizeActionType.LOGOUT:
return state
.set('uid', 0)
.set('authed', false)
.set('guest', true)
.set('isVerifide', false)
case AuthorizeActionType.SIGNUP: case AuthorizeActionType.SIGNUP:
return{ return state
...state, .set('uid', payload.userId)
uid: payload.userId
}
case AuthorizeActionType.UPDATE_PASSWORD: case AuthorizeActionType.UPDATE_PASSWORD:
return{ return state
...state, .set('updatePassword', payload.updatePassword)
updatePassword: payload.updatePassword
}
default: default:
return state return state

View File

@@ -1,4 +1,8 @@
const getCurrentUser = (state: any) => (state.user.info && state.authorize.uid) ? state.user.info[state.authorize.uid] : null import {Map} from 'immutable'
const getCurrentUser = (state: Map<any, string>) => {
const uid = state.getIn(['authorize', 'uid'])
return state.getIn(['user', 'info', uid])
}
export const authorizeSelector = { export const authorizeSelector = {
getCurrentUser getCurrentUser

View File

@@ -1,4 +1,5 @@
import { Circle, UserTie } from 'src/core/domain/circles' import { Circle, UserTie } from 'src/core/domain/circles'
import {Map} from 'immutable'
/** /**
* Circle state * Circle state
@@ -12,19 +13,19 @@ export class CircleState {
* *
* @memberof CircleState * @memberof CircleState
*/ */
userTies: { [userId: string]: UserTie } = {} userTies: Map<string, UserTie> = Map({})
/** /**
* The list of users belong to users circle * The list of users belong to users circle
* *
* @memberof CircleState * @memberof CircleState
*/ */
userTieds: { [userId: string]: UserTie } = {} userTieds: Map<string, UserTie> = Map({})
/** /**
* The list of circle of current user * The list of circle of current user
*/ */
circleList: { [circleId: string]: Circle } circleList: Map<string, Circle> = Map({})
/** /**
* Whether select circle box is open for the selected user * Whether select circle box is open for the selected user
@@ -39,7 +40,7 @@ export class CircleState {
/** /**
* Keep selected circles for refere user * Keep selected circles for refere user
*/ */
selectedCircles: { [userId: string]: string[] } selectedCircles: Map<string, string[]> = Map({})
/** /**
* Whether the select circles box for referer user is open * Whether the select circles box for referer user is open

View File

@@ -1,6 +1,7 @@
// - Import react components // - Import react components
import moment from 'moment/moment' import moment from 'moment/moment'
import _ from 'lodash' import _ from 'lodash'
import { Map, List } from 'immutable'
// - Import domain // - Import domain
import { User } from 'src/core/domain/users' import { User } from 'src/core/domain/users'
@@ -12,245 +13,120 @@ import { CircleActionType } from 'constants/circleActionType'
import { CircleState } from './CircleState' import { CircleState } from './CircleState'
import { ICircleAction } from './ICircleAction' import { ICircleAction } from './ICircleAction'
/**
* Add circle
*/
const addCircle = (state: any , payload: any) => {
const circle: Map<string, any> = payload.circle
return state
.setIn(['circleList', circle.get('id')], payload.circle)
}
/**
* Update circle
*/
const updateCircle = (state: any , payload: any) => {
const circle: Map<string, any> = payload.circle
return state
.setIn(['openSetting', circle.get('id')], false)
.setIn(['circleList', circle.get('id')], payload.circle)
}
/** /**
* Circle reducer * Circle reducer
* @param state * @param state
* @param action * @param action
*/ */
export let circleReducer = (state: CircleState = new CircleState(), action: ICircleAction) => { export let circleReducer = (state = Map(new CircleState()), action: ICircleAction) => {
const { payload } = action const { payload } = action
switch (action.type) { switch (action.type) {
case CircleActionType.CLEAR_ALL_CIRCLES: case CircleActionType.CLEAR_ALL_CIRCLES:
return new CircleState() return Map(new CircleState())
case CircleActionType.ADD_CIRCLE: case CircleActionType.ADD_CIRCLE: return addCircle(state, payload)
return { case CircleActionType.UPDATE_CIRCLE: return updateCircle(state, payload)
...state,
circleList: {
...state.circleList,
[payload.circle.id]: {
...payload.circle
}
}
}
case CircleActionType.UPDATE_CIRCLE:
return {
...state,
openSetting: {
...state.openSetting,
[payload.circle.id]: false
},
circleList: {
...state.circleList,
[payload.circle.id]: {
...payload.circle
}
}
}
case CircleActionType.DELETE_CIRCLE: case CircleActionType.DELETE_CIRCLE:
let filteredDeleteCircles = {} return state
Object.keys(state.circleList).map((key) => { .deleteIn(['circleList', payload.circleId])
if (key !== payload.circleId) {
return _.merge(filteredDeleteCircles, { [key]: { ...state.circleList![key] } })
}
})
return {
...state,
circleList: {
...state.circleList,
...filteredDeleteCircles
}
}
case CircleActionType.ADD_LIST_CIRCLE: case CircleActionType.ADD_LIST_CIRCLE:
return { return state
...state, .set('loaded', true)
circleList: { .mergeIn(['circleList'], payload.circleList)
...state.circleList,
...payload.circleList
},
loaded: true
}
case CircleActionType.ADD_FOLLOWING_USER: case CircleActionType.ADD_FOLLOWING_USER:
return { const userTie: Map<string, any> = payload.userTie
...state, return state
userTies: { .setIn(['userTies', userTie.get('userId')], payload.userTie)
...state.userTies, .setIn(['selectedCircles', userTie.get('userId')], userTie.get('circleIdList'))
[payload.userTie.userId]: {
...payload.userTie
}
},
selectedCircles: {
...state.selectedCircles,
[payload.userTie.userId]: payload.userTie.circleIdList
}
}
case CircleActionType.UPDATE_USER_TIE: case CircleActionType.UPDATE_USER_TIE:
return { return state
...state, .setIn(['userTies', payload.userTie.user.userId], payload.userTie)
userTies: {
...state.userTies,
[payload.userTie.user.userId]: {
...payload.userTie
}
}
}
case CircleActionType.ADD_USER_TIE_LIST: case CircleActionType.ADD_USER_TIE_LIST:
return { return state
...state, .mergeIn(['userTies'], payload.userTies)
userTies: { .set('selectedCircles', getSelectedCircles(payload.userTies))
...state.userTies,
...payload.userTies
},
selectedCircles : getSelectedCircles(payload.userTies)
}
case CircleActionType.ADD_USER_TIED_LIST: case CircleActionType.ADD_USER_TIED_LIST:
return { return state
...state, .mergeIn(['userTieds'], payload.userTieds)
userTieds: {
...state.userTieds,
...payload.userTieds
}
}
case CircleActionType.DELETE_USER_FROM_CIRCLE: case CircleActionType.DELETE_USER_FROM_CIRCLE:
let filteredCircles: string[] = [] return state
Object.keys(state.userTies[payload.userId].circleIdList!).forEach((circleId) => { .deleteIn(['userTies', payload.userTie.user.userId, 'circleIdList', payload.circleId])
if (circleId !== payload.circleId) {
filteredCircles.push(circleId)
}
})
return {
...state,
userTies: {
...state.userTies,
[payload.userTie.user.userId]: {
...payload.userTie,
circleIdList: filteredCircles
}
}
}
case CircleActionType.DELETE_FOLLOWING_USER: case CircleActionType.DELETE_FOLLOWING_USER:
let filteredUserTies: {[userId: string]: UserTie } = {} return state
.deleteIn(['userTies', payload.userId])
.deleteIn(['selectedCircles', payload.userId])
Object.keys(state.userTies).forEach((userId) => { /**
if (userId !== payload.userId) { * User interface stuffs
return _.merge(filteredUserTies, { [userId]: { ...state.userTies[userId] } }) */
}
})
return {
...state,
userTies: {
...filteredUserTies
},
selectedCircles : getSelectedCircles(filteredUserTies)
}
/**
* User interface stuffs
*/
case CircleActionType.CLOSE_CIRCLE_SETTINGS: case CircleActionType.CLOSE_CIRCLE_SETTINGS:
return { return state
...state, .setIn(['openSetting', payload.circleId], false)
openSetting: {
...state.openSetting,
[payload.circleId]: false
}
}
case CircleActionType.OPEN_CIRCLE_SETTINGS: case CircleActionType.OPEN_CIRCLE_SETTINGS:
return { return state
...state, .setIn(['openSetting', payload.circleId], true)
openSetting: {
...state.openSetting,
[payload.circleId]: true
}
}
case CircleActionType.SHOW_SELECT_CIRCLE_BOX: case CircleActionType.SHOW_SELECT_CIRCLE_BOX:
return { return state
...state, .setIn(['selectCircleStatus', payload.userId], true)
selectCircleStatus: {
...state.selectCircleStatus,
[payload.userId]: true
}
}
case CircleActionType.HIDE_SELECT_CIRCLE_BOX: case CircleActionType.HIDE_SELECT_CIRCLE_BOX:
return { return state
...state, .setIn(['selectCircleStatus', payload.userId], false)
selectCircleStatus: {
...state.selectCircleStatus,
[payload.userId]: false
}
}
case CircleActionType.SHOW_FOLLOWING_USER_LOADING: case CircleActionType.SHOW_FOLLOWING_USER_LOADING:
return { return state
...state, .setIn(['followingLoadingStatus', payload.userId], true)
followingLoadingStatus: {
...state.followingLoadingStatus,
[payload.userId]: true
}
}
case CircleActionType.HIDE_FOLLOWING_USER_LOADING: case CircleActionType.HIDE_FOLLOWING_USER_LOADING:
return { return state
...state, .setIn(['followingLoadingStatus', payload.userId], false)
followingLoadingStatus: {
...state.followingLoadingStatus,
[payload.userId]: false
}
}
/**
* User box component
*/
case CircleActionType.SET_SELECTED_CIRCLES_USER_BOX_COMPONENT: case CircleActionType.SET_SELECTED_CIRCLES_USER_BOX_COMPONENT:
return { return state
...state, .setIn(['selectedCircles', payload.userId], payload.circleList)
selectedCircles: {
...state.selectedCircles,
[payload.userId]: payload.circleList
}
}
/**
* User box component
*/
case CircleActionType.REMOVE_SELECTED_CIRCLES_USER_BOX_COMPONENT: case CircleActionType.REMOVE_SELECTED_CIRCLES_USER_BOX_COMPONENT:
return { return state
...state, .setIn(['selectedCircles', payload.userId], [])
selectedCircles: {
...state.selectedCircles,
[payload.userId]: []
}
}
/**
* User box component
*/
case CircleActionType.OPEN_SELECT_CIRCLES_USER_BOX_COMPONENT: case CircleActionType.OPEN_SELECT_CIRCLES_USER_BOX_COMPONENT:
return { return state
...state, .setIn(['openSelecteCircles', payload.userId], true)
openSelecteCircles: {
...state.openSelecteCircles,
[payload.userId]: true
}
}
case CircleActionType.CLOSE_SELECT_CIRCLES_USER_BOX_COMPONENT: case CircleActionType.CLOSE_SELECT_CIRCLES_USER_BOX_COMPONENT:
return { return state
...state, .setIn(['openSelecteCircles', payload.userId], false)
openSelecteCircles: {
...state.openSelecteCircles,
[payload.userId]: false
}
}
default: default:
return state return state
@@ -260,14 +136,11 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
/** /**
* Map user ties selected to selected circles * Map user ties selected to selected circles
*/ */
const getSelectedCircles = (userTies: {[userId: string]: UserTie }) => { const getSelectedCircles = (userTies: { [userId: string]: UserTie }) => {
let selectedCircles: {[userId: string]: string[]} = {} let selectedCircles: Map<string, List<string>> = Map({})
Object.keys(userTies).forEach((userId: string) => { Object.keys(userTies).forEach((userId: string) => {
const userTie = (userTies as {[userId: string]: UserTie })[userId] const userTie = (userTies as { [userId: string]: UserTie })[userId]
selectedCircles = { selectedCircles = selectedCircles.set(userTie.userId!, List(userTie.circleIdList!))
...selectedCircles,
[userTie.userId!]: userTie.circleIdList!
}
}) })
return selectedCircles return selectedCircles

View File

@@ -1,6 +1,6 @@
import { Comment } from 'src/core/domain/comments' import { Comment } from 'src/core/domain/comments'
import {Map} from 'immutable'
/** /**
* Comment state * Comment state
* *
@@ -11,22 +11,16 @@ export class CommentState {
/** /**
* The list of comments on the posts * The list of comments on the posts
*
* @type {({[postId: string]: {[commentId: string]: Comment}} | null)}
* @memberof CommentState
*/ */
postComments: {[postId: string]: {[commentId: string]: Comment}} = {} postComments: Map<string, {[commentId: string]: Comment}> = Map({})
/** /**
* Whether comment editor is open * Whether comment editor is open
*/ */
editorStatus: {[postId: string]: {[commentId: string]: boolean}} = {} editorStatus: Map<string, {[commentId: string]: boolean}> = Map({})
/** /**
* If the comments are loaded {true} or not {false} * If the comments are loaded {true} or not {false}
*
* @type {Boolean}
* @memberof CommentState
*/ */
loaded: Boolean = false loaded: Boolean = false
} }

View File

@@ -1,6 +1,7 @@
// - Import react components // - Import react components
import moment from 'moment/moment' import moment from 'moment/moment'
import _ from 'lodash' import _ from 'lodash'
import { Map } from 'immutable'
// - Import domain // - Import domain
import { User } from 'src/core/domain/users' import { User } from 'src/core/domain/users'
@@ -17,96 +18,38 @@ import { ICommentAction } from './ICommentAction'
* @param state * @param state
* @param action * @param action
*/ */
export let commentReducer = (state: CommentState = new CommentState(), action: ICommentAction) => { export let commentReducer = (state = Map(new CommentState()), action: ICommentAction) => {
let { payload } = action let { payload } = action
switch (action.type) { switch (action.type) {
/* _____________ CRUD _____________ */ /* _____________ CRUD _____________ */
case CommentActionType.ADD_COMMENT: case CommentActionType.ADD_COMMENT:
return { return state
...state, .setIn(['postComments', payload.postId, payload.id], payload)
postComments: {
...state.postComments,
[payload.postId]: {
...state.postComments![payload.postId],
[payload.id]: {
...payload,
editorStatus: false
}
}
}
}
case CommentActionType.ADD_COMMENT_LIST: case CommentActionType.ADD_COMMENT_LIST:
return { return state
...state, .mergeIn(['postComments'], payload)
postComments: { .set('loaded', true)
...state.postComments,
...payload case CommentActionType.UPDATE_COMMENT:
}, const { comment } = payload
loaded: true return state
} .updateIn(['postComments', comment.postId, comment.id, 'text'], (text: string) => comment.text)
case CommentActionType.UPDATE_COMMENT:
const {comment} = payload case CommentActionType.DELETE_COMMENT:
return { return state.deleteIn(['postComments', payload.postId, payload.id])
...state,
postComments: {
...state.postComments,
[comment.postId]: {
...state.postComments![comment.postId],
[comment.id]: {
...state.postComments![comment.postId][comment.id],
text: comment.text,
editorStatus: false
}
}
}
}
case CommentActionType.DELETE_COMMENT:
if (!state.postComments![payload.postId]) {
return state
}
let parsedComments = {}
Object.keys(state.postComments![payload.postId]).map((id) => {
if (id !== payload.id) {
_.merge(parsedComments, { [id]: { ...state.postComments![payload.postId][id] } })
}
})
return {
...state,
postComments: {
...state.postComments,
[payload.postId]: {
...parsedComments
}
}
}
case CommentActionType.CLOSE_COMMENT_EDITOR: case CommentActionType.CLOSE_COMMENT_EDITOR:
return { return state
...state, .setIn(['editorStatus', payload.postId, payload.id], false)
editorStatus: {
...state.editorStatus,
[payload.postId]: {
...state.editorStatus![payload.postId],
[payload.id]: false
}
}
}
case CommentActionType.OPEN_COMMENT_EDITOR: case CommentActionType.OPEN_COMMENT_EDITOR:
return { return state
...state, .setIn(['editorStatus', payload.postId, payload.id], true)
editorStatus: {
...state.editorStatus,
[payload.postId]: {
...state.editorStatus![payload.postId],
[payload.id]: true
}
}
}
case CommentActionType.CLEAR_ALL_DATA_COMMENT: case CommentActionType.CLEAR_ALL_DATA_COMMENT:
return new CommentState() return Map(new CommentState())
default: default:
return state return state

View File

@@ -1,3 +1,4 @@
import { Map, fromJS, List } from 'immutable'
/** /**
* Global state * Global state
* *
@@ -15,13 +16,10 @@ export class GlobalState {
* }} * }}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
progress: { progress = Map({
percent: number
visible: boolean
} = {
percent: 0, percent: 0,
visible: false visible: false
} })
/** /**
* If loading is enabled {true} or not false * If loading is enabled {true} or not false
@@ -122,7 +120,7 @@ export class GlobalState {
* @type {*} * @type {*}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
temp: any = { temp: any = Map({
caller: [] caller: List()
} })
} }

View File

@@ -3,112 +3,86 @@ import { GlobalActionType } from 'constants/globalActionType'
import { GlobalState } from './GlobalState' import { GlobalState } from './GlobalState'
import { IGlobalAction } from './IGlobalAction' import { IGlobalAction } from './IGlobalAction'
import { Map, fromJS } from 'immutable'
/** /**
* Global reducer * Global reducer
* @param {object} state * @param {object} state
* @param {object} action * @param {object} action
*/ */
export const globalReducer = (state: GlobalState = new GlobalState(), action: IGlobalAction) => { export const globalReducer = (state = Map(new GlobalState()), action: IGlobalAction) => {
const { payload } = action const { payload } = action
switch (action.type) { switch (action.type) {
case GlobalActionType.PROGRESS_CHANGE: case GlobalActionType.PROGRESS_CHANGE:
return { return state
...state, .setIn(['progress', 'percent'], payload.percent)
progress: { .setIn(['progress', 'visible'], payload.visible)
...state.progress,
percent: payload.percent,
visible: payload.visible
}
}
case GlobalActionType.DEFAULT_DATA_DISABLE: case GlobalActionType.DEFAULT_DATA_DISABLE:
return { return state
...state, .set('defaultLoadDataStatus', false)
defaultLoadDataStatus: false
}
case GlobalActionType.DEFAULT_DATA_ENABLE: case GlobalActionType.DEFAULT_DATA_ENABLE:
return { return state
...state, .set('defaultLoadDataStatus', true)
defaultLoadDataStatus: true
}
case GlobalActionType.SHOW_MESSAGE_GLOBAL: case GlobalActionType.SHOW_MESSAGE_GLOBAL:
return { return state
...state, .set('message', action.payload)
message: action.payload, .set('messageOpen', true)
messageOpen: true
}
case GlobalActionType.SHOW_NORMAL_MESSAGE_GLOBAL: case GlobalActionType.SHOW_NORMAL_MESSAGE_GLOBAL:
return { return state
...state, .set('message', action.payload)
message: action.payload, .set('messageOpen', true)
messageOpen: true
}
case GlobalActionType.HIDE_MESSAGE_GLOBAL: case GlobalActionType.HIDE_MESSAGE_GLOBAL:
return { return state
...state, .set('message', action.payload)
message: '', .set('messageOpen', false)
messageOpen: false, .set('messageColor', '')
messageColor: ''
}
case GlobalActionType.SET_HEADER_TITLE: case GlobalActionType.SET_HEADER_TITLE:
return { return state
...state, .set('headerTitle', action.payload)
headerTitle: action.payload
}
case GlobalActionType.SHOW_SEND_FEEDBACK: case GlobalActionType.SHOW_SEND_FEEDBACK:
return { return state
...state, .set('sendFeedbackStatus', true)
sendFeedbackStatus: true
}
case GlobalActionType.HIDE_SEND_FEEDBACK: case GlobalActionType.HIDE_SEND_FEEDBACK:
return { return state
...state, .set('sendFeedbackStatus', false)
sendFeedbackStatus: false
}
case GlobalActionType.HIDE_TOP_LOADING: case GlobalActionType.HIDE_TOP_LOADING:
const queueTopLoading = state.topLoadingQueue > 0 ? (state.topLoadingQueue - 1) : 0 const queueTopLoading = Number(state.get('topLoadingQueue')) > 0 ? (Number(state.get('topLoadingQueue')) - 1) : 0
return { return state
...state, .set('topLoadingQueue', queueTopLoading)
topLoadingQueue: queueTopLoading, .set('showTopLoading', (queueTopLoading > 0 ? true : false))
showTopLoading: (queueTopLoading > 0 ? true : false)
}
case GlobalActionType.SHOW_TOP_LOADING: case GlobalActionType.SHOW_TOP_LOADING:
return { return state
...state, .set('topLoadingQueue', (Number(state.get('topLoadingQueue')) + 1))
topLoadingQueue: (state.topLoadingQueue + 1), .set('showTopLoading', true)
showTopLoading: true
} case GlobalActionType.HIDE_MASTER_LOADING:
case GlobalActionType.HIDE_MASTER_LOADING: const queueMasterLoading = Number(state.get('masterLoadingQueue')) > 0 ? (Number(state.get('masterLoadingQueue')) - 1) : 0
const queueMasterLoading = state.masterLoadingQueue > 0 ? (state.masterLoadingQueue - 1) : 0 return state
return { .set('masterLoadingQueue', queueMasterLoading)
...state, .set('showMasterLoading', (queueMasterLoading > 0 ? true : false))
masterLoadingQueue: queueMasterLoading,
showMasterLoading: (queueMasterLoading > 0 ? true : false)
}
case GlobalActionType.SHOW_MASTER_LOADING: case GlobalActionType.SHOW_MASTER_LOADING:
return { return state
...state, .set('masterLoadingQueue', Number(state.get('masterLoadingQueue')) + 1)
masterLoadingQueue: (state.masterLoadingQueue + 1), .set('showMasterLoading', true)
showMasterLoading: true
}
case GlobalActionType.TEMP: case GlobalActionType.TEMP:
return { return state
...state, .mergeIn(['temp', 'caller'], fromJS([payload.caller]))
temp: {
...state.temp, case GlobalActionType.CLEAR_ALL_GLOBAL:
caller: [ return state
...state.temp.caller, .set('sendFeedbackStatus', false)
payload.caller
]
}
}
case GlobalActionType.CLEAR_ALL_GLOBAL:
return {
...state,
sendFeedbackStatus: false,
}
default: default:
return state return state

View File

@@ -1,4 +1,5 @@
import { Image } from 'src/core/domain/imageGallery' import { Image } from 'src/core/domain/imageGallery'
import {Map, Collection, List} from 'immutable'
/** /**
* ImageGallery state * ImageGallery state
@@ -18,49 +19,31 @@ export class ImageGalleryState {
/** /**
* The list of image * The list of image
*
* @type {(Image[] | null)}
* @memberof ImageGalleryState
*/ */
images: Image[] = [] images: List<Image> = List()
/** /**
* Selected image name * Selected image name
*
* @type {string}
* @memberof ImageGalleryState
*/ */
selectImage: string = '' selectImage: string = ''
/** /**
* Selected image address * Selected image address
*
* @type {string}
* @memberof ImageGalleryState
*/ */
selectURL: string = '' selectURL: string = ''
/** /**
* If image gallery is loaded {true} or not false * If image gallery is loaded {true} or not false
*
* @type {Boolean}
* @memberof ImageGalleryState
*/ */
loaded: Boolean = false loaded: Boolean = false
/** /**
* Images address list * Images address list
*
* @type {*}
* @memberof ImageGalleryState
*/ */
imageURLList: any = {} imageURLList: any = {}
/** /**
* Store image requested * Store image requested
*
* @type {*}
* @memberof ImageGalleryState
*/ */
imageRequests: any = {} imageRequests: any = {}

View File

@@ -1,5 +1,6 @@
// - Import react components // - Import react components
import _ from 'lodash' import _ from 'lodash'
import { Map, List } from 'immutable'
// - Import domain // - Import domain
import { User } from 'src/core/domain/users' import { User } from 'src/core/domain/users'
@@ -14,52 +15,36 @@ import { ImageGalleryState } from './ImageGalleryState'
/** /**
* Image gallery reducer * Image gallery reducer
*/ */
export let imageGalleryReducer = (state: ImageGalleryState = new ImageGalleryState(), action: IImageGalleryAction) => { export let imageGalleryReducer = (state = Map(new ImageGalleryState()), action: IImageGalleryAction) => {
const { payload } = action const { payload } = action
switch (action.type) { switch (action.type) {
/* ----------------- CRUD ----------------- */ /* ----------------- CRUD ----------------- */
case ImageGalleryActionType.ADD_IMAGE_GALLERY: case ImageGalleryActionType.ADD_IMAGE_GALLERY:
return { return state
...state, .mergeIn(['images'], List([payload]))
images: [...state.images!, payload]
}
case ImageGalleryActionType.ADD_IMAGE_LIST_GALLERY: case ImageGalleryActionType.ADD_IMAGE_LIST_GALLERY:
return { return state
...state, .set('images', List(payload))
images: [...payload], .set('loaded', true)
loaded: true
}
case ImageGalleryActionType.DELETE_IMAGE: case ImageGalleryActionType.DELETE_IMAGE:
return { return state
...state, .update('images', (images: List<Image>) => {
images: [ return images.filter((image) => image!.id !== payload)
...state.images!.filter((item: Image) => { })
return item.id !== payload
})
]
}
case ImageGalleryActionType.SET_IMAGE_URL: case ImageGalleryActionType.SET_IMAGE_URL:
return { return state
...state, .setIn(['imageURLList', payload.name], payload.url)
imageURLList: {
...state.imageURLList,
[payload.name]: payload.url
}
}
case ImageGalleryActionType.SEND_IMAGE_REQUEST: case ImageGalleryActionType.SEND_IMAGE_REQUEST:
return { return state
...state, .mergeIn(['imageRequests'], payload)
imageRequests: [
...state.imageRequests,
payload
]
}
case ImageGalleryActionType.CLEAT_ALL_DATA_IMAGE_GALLERY: case ImageGalleryActionType.CLEAT_ALL_DATA_IMAGE_GALLERY:
return new ImageGalleryState() return Map(new ImageGalleryState())
default: default:
return state return state

View File

@@ -1,4 +1,5 @@
import { Notification } from 'src/core/domain/notifications' import { Notification } from 'src/core/domain/notifications'
import {Map} from 'immutable'
/** /**
* Notification state * Notification state
@@ -10,17 +11,11 @@ export class NotificationState {
/** /**
* The list of users notification * The list of users notification
*
* @type {({[userId: string]: {[notificationId: string]: Notification}} | null)}
* @memberof NotificationState
*/ */
userNotifies: {[userId: string]: {[notificationId: string]: Notification}} = {} userNotifies: Map<string, Map<string, any>> = Map({})
/** /**
* If user notifications are loaded {true} or not {false} * If user notifications are loaded {true} or not {false}
*
* @type {Boolean}
* @memberof NotificationState
*/ */
loaded: Boolean = false loaded: Boolean = false
} }

View File

@@ -1,6 +1,7 @@
// - Import react components // - Import react components
import moment from 'moment/moment' import moment from 'moment/moment'
import _ from 'lodash' import _ from 'lodash'
import { Map } from 'immutable'
// - Import domain // - Import domain
import { Notification } from 'src/core/domain/notifications' import { Notification } from 'src/core/domain/notifications'
@@ -16,7 +17,7 @@ import { INotificationAction } from './INotificationAction'
* @param {object} state * @param {object} state
* @param {object} action * @param {object} action
*/ */
export let notificationReducer = (state: NotificationState = new NotificationState(), action: INotificationAction) => { export let notificationReducer = (state = Map(new NotificationState()), action: INotificationAction) => {
let { payload } = action let { payload } = action
switch (action.type) { switch (action.type) {
@@ -25,44 +26,21 @@ export let notificationReducer = (state: NotificationState = new NotificationSta
return state return state
case NotificationActionType.ADD_NOTIFY_LIST: case NotificationActionType.ADD_NOTIFY_LIST:
return { return state
...state, .set('userNotifies', payload)
userNotifies: { .set('loaded', true)
...payload
},
loaded: true
}
case NotificationActionType.SEEN_NOTIFY: case NotificationActionType.SEEN_NOTIFY:
return { return state
...state, .setIn(['userNotifies', payload, 'isSeen'], true)
userNotifies: { .set('loaded', true)
...state.userNotifies,
[payload]: {
...state.userNotifies![payload],
isSeen: true
}
},
loaded: true
}
case NotificationActionType.DELETE_NOTIFY: case NotificationActionType.DELETE_NOTIFY:
let parsedNotifies = {} return state
Object.keys(state.userNotifies!).map((id) => { .deleteIn(['userNotifies', payload])
if (id !== payload) {
_.merge(parsedNotifies, { [id]: { ...state.userNotifies![id] } })
}
})
return {
...state,
userNotifies: {
...parsedNotifies
}
}
case NotificationActionType.CLEAR_ALL_DATA_NOTIFY: case NotificationActionType.CLEAR_ALL_DATA_NOTIFY:
return new NotificationState() return Map(new NotificationState())
default: default:
return state return state

View File

@@ -1,4 +1,5 @@
import { Post } from 'src/core/domain/posts' import { Post } from 'src/core/domain/posts'
import { Map, fromJS, List } from 'immutable'
/** /**
* Post state * Post state
@@ -14,7 +15,7 @@ export class PostState {
* @type {*} * @type {*}
* @memberof PostState * @memberof PostState
*/ */
userPosts: any = {} userPosts = Map({})
/** /**
* If user posts are loaded {true} or not {false} * If user posts are loaded {true} or not {false}
@@ -27,12 +28,12 @@ export class PostState {
/** /**
* Stream data storage * Stream data storage
*/ */
stream?: {hasMoreData: boolean, lastPageRequest: number, lastPostId: string} = stream?: Map<string,any> =
{hasMoreData: true, lastPageRequest: -1, lastPostId: ''} Map({hasMoreData: true, lastPageRequest: -1, lastPostId: ''})
/** /**
* Profile posts data storage * Profile posts data storage
*/ */
profile?: {[userId: string]: {hasMoreData: boolean, lastPageRequest: number, lastPostId: string}} = profile?: Map<string, any> =
{} Map({})
} }

View File

@@ -2,6 +2,7 @@
import moment from 'moment/moment' import moment from 'moment/moment'
import _ from 'lodash' import _ from 'lodash'
import { Reducer, Action } from 'redux' import { Reducer, Action } from 'redux'
import { Map } from 'immutable'
// - Import action types // - Import action types
import { PostActionType } from 'constants/postActionType' import { PostActionType } from 'constants/postActionType'
@@ -11,167 +12,94 @@ import { IPostAction } from './IPostAction'
import { Post } from 'src/core/domain/posts/post' import { Post } from 'src/core/domain/posts/post'
import CommonAPI from 'src/api/CommonAPI' import CommonAPI from 'src/api/CommonAPI'
const updatePost = (state: any, payload: any) => {
const post: Map<string, any> = payload.post
const updatePostOwnerId = post.get('ownerUserId')
const updatePostId = post.get('id')
return state
.setIn(['userPosts', updatePostOwnerId, updatePostId], Map(post))
}
const updatePostComments = (state: any, payload: any) => {
const post: Map<string, any> = payload.post
const updatePostOwnerId = post.get('ownerUserId')
const updatePostId = post.get('id')
return state
.setIn(['userPosts', updatePostOwnerId, updatePostId, 'comments'], post.get('comments'))
}
const updatePostVotes = (state: any, payload: any) => {
const post: Map<string, any> = payload.post
const updatePostOwnerId = post.get('ownerUserId')
const updatePostId = post.get('id')
return state
.setIn(['userPosts', updatePostOwnerId, updatePostId, 'votes'], post.get('votes'))
}
/** /**
* Post reducer * Post reducer
* @param {object} state * @param {object} state
* @param {object} action * @param {object} action
*/ */
export let postReducer = (state: PostState = new PostState(), action: IPostAction) => { export let postReducer = (state = Map(new PostState()), action: IPostAction) => {
const { payload } = action const { payload } = action
switch (action.type) { switch (action.type) {
case PostActionType.CLEAR_ALL_DATA_POST: case PostActionType.CLEAR_ALL_DATA_POST:
return new PostState() return Map(new PostState())
case PostActionType.ADD_IMAGE_POST: case PostActionType.ADD_IMAGE_POST:
return { return state
...state, .setIn(['userPosts', payload.uid, payload.post.id], Map(payload.post))
userPosts: {
...state.userPosts,
[payload.uid]: {
...state.userPosts[payload.uid],
[payload.post.id]: { ...payload.post }
}
}
}
case PostActionType.ADD_POST: case PostActionType.ADD_POST:
return { return state
...state, .setIn(['userPosts', payload.uid, payload.post.id], Map(payload.post))
userPosts: {
...state.userPosts,
[payload.uid]: {
...state.userPosts[payload.uid],
[payload.post.id]: { ...payload.post }
}
}
}
case PostActionType.UPDATE_POST: case PostActionType.UPDATE_POST: return updatePost(state, payload)
const post: Post = payload.post case PostActionType.UPDATE_POST_COMMENTS: return updatePostComments(state, payload)
return { case PostActionType.UPDATE_POST_VOTES: return updatePostVotes(state, payload)
...state,
userPosts: {
...state.userPosts,
[post.ownerUserId!]: {
...state.userPosts[post.ownerUserId!],
[payload.post.id]: {
...payload.post,
comments: post.comments,
votes: post.votes
}
}
}
}
case PostActionType.DELETE_POST: case PostActionType.DELETE_POST:
let filteredPosts = {} return state
Object.keys(state.userPosts[payload.uid]).map((key) => { .deleteIn(['userPosts', payload.uid, payload.id])
if (key !== payload.id) {
return _.merge(filteredPosts, { [key]: { ...state.userPosts[payload.uid][key] } })
}
})
return {
...state,
userPosts: {
...state.userPosts,
[payload.uid]: {
...filteredPosts
}
}
}
case PostActionType.ADD_LIST_POST: case PostActionType.ADD_LIST_POST:
const newUserPosts = payload.userPosts as { [userId: string]: { [postId: string]: Post } } return state
const mergedObject = _.merge(state.userPosts, newUserPosts) .mergeDeepIn(['userPosts'], payload.userPosts)
return { .set('loaded', true)
...state,
userPosts: {
...mergedObject
},
loaded: true
}
case PostActionType.HAS_MORE_DATA_STREAM: case PostActionType.HAS_MORE_DATA_STREAM:
return { return state
...state, .setIn(['stream', 'hasMoreData'], true)
stream: {
...state.stream,
hasMoreData: true
}
}
case PostActionType.NOT_MORE_DATA_STREAM: case PostActionType.NOT_MORE_DATA_STREAM:
return { return state
...state, .setIn(['stream', 'hasMoreData'], false)
stream: {
...state.stream,
hasMoreData: false
}
}
case PostActionType.REQUEST_PAGE_STREAM: case PostActionType.REQUEST_PAGE_STREAM:
return { return state
...state, .setIn(['stream', 'lastPageRequest'], payload.page)
stream: {
...state.stream,
lastPageRequest: payload.page
}
}
case PostActionType.LAST_POST_STREAM: case PostActionType.LAST_POST_STREAM:
return { return state
...state, .setIn(['stream', 'lastPostId'], payload.lastPostId)
stream: {
...state.stream,
lastPostId: payload.lastPostId
}
}
case PostActionType.HAS_MORE_DATA_PROFILE: case PostActionType.HAS_MORE_DATA_PROFILE:
return { return state
...state, .setIn(['profile', 'hasMoreData'], true)
profile: {
...state.profile,
hasMoreData: true
}
}
case PostActionType.NOT_MORE_DATA_PROFILE: case PostActionType.NOT_MORE_DATA_PROFILE:
return { return state
...state, .setIn(['profile', payload.userId, 'hasMoreData'], false)
profile: {
...state.profile,
[payload.userId]: {
...state.profile![payload.userId],
hasMoreData: false
}
}
}
case PostActionType.REQUEST_PAGE_PROFILE: case PostActionType.REQUEST_PAGE_PROFILE:
return { return state
...state, .setIn(['profile', payload.userId, 'lastPageRequest'], payload.page)
profile: {
...state.profile,
[payload.userId]: {
...state.profile![payload.userId],
lastPageRequest: payload.page
}
}
}
case PostActionType.LAST_POST_PROFILE: case PostActionType.LAST_POST_PROFILE:
return { return state
...state, .setIn(['profile', payload.userId, 'lastPostId'], payload.lastPostId)
profile: {
...state.profile,
[payload.userId]: {
...state.profile![payload.userId],
lastPostId: payload.lastPostId
}
}
}
default: default:
return state return state

View File

@@ -1,7 +1,7 @@
const getPost = (state: any, userId: string, postId: string) => { import {Map} from 'immutable'
return (state.post.userPosts && state.post.userPosts[userId] && state.post.userPosts[userId][postId])
? state.post.userPosts[userId][postId] const getPost = (state: Map<string, any>, userId: string, postId: string) => {
: null return state.getIn(['post', 'userPosts', userId, postId])
} }
export const postSelector = { export const postSelector = {

View File

@@ -1,5 +1,7 @@
import * as redux from 'redux'
import { localeReducer as locale } from 'react-localize-redux' import { localeReducer as locale } from 'react-localize-redux'
import {
combineReducers
} from 'redux-immutable'
// - Import reducers // - Import reducers
import { authorizeReducer } from './authorize' import { authorizeReducer } from './authorize'
@@ -15,7 +17,7 @@ import { serverReducer } from './server'
import { routerReducer, routerMiddleware } from 'react-router-redux' import { routerReducer, routerMiddleware } from 'react-router-redux'
// - Reducers // - Reducers
export const rootReducer = redux.combineReducers({ export const rootReducer = combineReducers({
locale, locale,
imageGallery: imageGalleryReducer, imageGallery: imageGalleryReducer,
post: postReducer, post: postReducer,

View File

@@ -1,4 +1,5 @@
import { ServerRequestModel } from 'src/models/server' import { ServerRequestModel } from 'src/models/server'
import {Map} from 'immutable'
/** /**
* Server state * Server state
@@ -12,5 +13,5 @@ export class ServerState {
* The list of posts server * The list of posts server
* @memberof ServerState * @memberof ServerState
*/ */
request: {[requestId: string]: ServerRequestModel} | null = {} request: Map<string, ServerRequestModel> = Map({})
} }

View File

@@ -1,5 +1,6 @@
// - Import react components // - Import react components
import _ from 'lodash' import _ from 'lodash'
import { Map } from 'immutable'
// - Import action types // - Import action types
import { ServerActionType } from 'constants/serverActionType' import { ServerActionType } from 'constants/serverActionType'
@@ -16,61 +17,30 @@ import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
* @param {object} state * @param {object} state
* @param {object} action * @param {object} action
*/ */
export let serverReducer = (state: ServerState = new ServerState(), action: IServerAction) => { export let serverReducer = (state = Map(new ServerState()), action: IServerAction) => {
let { payload } = action let { payload } = action
const request = (payload ? payload.request : {}) as ServerRequestModel const request = (payload ? payload.request : {}) as ServerRequestModel
switch (action.type) { switch (action.type) {
/* _____________ CRUD _____________ */ /* _____________ CRUD _____________ */
case ServerActionType.ADD_REQUEST: case ServerActionType.ADD_REQUEST:
return { return state
...state, .setIn(['request', request.id], request)
request: {
...state.request,
[request.id]: {
...request
}
}
}
case ServerActionType.DELETE_REQUEST:
let parsedRequests = {}
Object.keys(state.request!).forEach((id) => {
if (id !== request.id) {
_.merge(parsedRequests, { [id]: { ...state.request![id] } })
}
}) case ServerActionType.DELETE_REQUEST:
return { return state
...state, .deleteIn(['request', request.id])
request: parsedRequests
}
case ServerActionType.ERROR_REQUEST: case ServerActionType.ERROR_REQUEST:
return { return state
...state, .setIn(['request', request.id, 'status'], ServerRequestStatusType.Error)
request: {
...state.request,
[request.id]: {
...state.request![request.id],
status: ServerRequestStatusType.Error
}
}
}
case ServerActionType.OK_REQUEST: case ServerActionType.OK_REQUEST:
return { return state
...state, .setIn(['request', request.id, 'status'], ServerRequestStatusType.OK)
request: {
...state.request,
[request.id]: {
...state.request![request.id],
status: ServerRequestStatusType.OK
}
}
}
case ServerActionType.CLEAR_ALL_DATA_REQUEST: case ServerActionType.CLEAR_ALL_DATA_REQUEST:
return new ServerState() return Map(new ServerState())
default: default:
return state return state

View File

@@ -1,4 +1,5 @@
import { User,Profile } from 'src/core/domain/users' import { User,Profile } from 'src/core/domain/users'
import { Map, fromJS, List } from 'immutable'
/** /**
* User state * User state
@@ -9,31 +10,21 @@ import { User,Profile } from 'src/core/domain/users'
export class UserState { export class UserState {
/** /**
* The list of users information * The list of users information
*
* @type {({[userId: string]: Profile} | null)}
* @memberof UserState
*/ */
info: {[userId: string]: Profile} = {} info: Map<string, Profile> = Map({})
/** /**
* If users profile are loaded * If users profile are loaded
*
* @type {Boolean}
* @memberof UserState
*/ */
loaded: Boolean = false loaded: Boolean = false
/** /**
* If edit profile is open {true} or not {false} * If edit profile is open {true} or not {false}
*
* @type {Boolean}
* @memberof UserState
*/ */
openEditProfile: Boolean = false openEditProfile: Boolean = false
/** /**
* People data storage * People data storage
*/ */
people?: {hasMoreData: boolean, lastPageRequest: number, lastUserId: string} = people?: Map<string, any> = Map({hasMoreData: true, lastPageRequest: -1, lastUserId: ''})
{hasMoreData: true, lastPageRequest: -1, lastUserId: ''}
} }

View File

@@ -1,8 +1,9 @@
// - Import action types // - Import action types
import { UserActionType } from 'constants/userActionType' import { UserActionType } from 'constants/userActionType'
import { Map } from 'immutable'
// - Import domain // - Import domain
import { User,Profile } from 'src/core/domain/users' import { User, Profile } from 'src/core/domain/users'
import { UserState } from './UserState' import { UserState } from './UserState'
import { IUserAction } from './IUserAction' import { IUserAction } from './IUserAction'
@@ -10,101 +11,52 @@ import { IUserAction } from './IUserAction'
/** /**
* User reducer * User reducer
*/ */
export let userReducer = (state: UserState = new UserState(), action: IUserAction) => { export let userReducer = (state = Map(new UserState()), action: IUserAction) => {
const { payload } = action const { payload } = action
switch (action.type) { switch (action.type) {
case UserActionType.USER_INFO: case UserActionType.USER_INFO:
return { return state
...state, .setIn(['info', payload.uid], payload.info)
info: {
...state.info,
[payload.uid]: {
...payload.info
}
}
}
case UserActionType.ADD_USER_INFO: case UserActionType.ADD_USER_INFO:
return { return state
...state, .setIn(['info', payload.uid], payload.info)
info: { .set('loaded', true)
...state.info,
[payload.uid]: {
...payload.info
}
},
loaded: true
}
case UserActionType.ADD_PEOPLE_INFO: case UserActionType.ADD_PEOPLE_INFO:
return { return state
...state, .mergeIn(['info'], payload)
info: {
...state.info,
...payload
}
}
case UserActionType.UPDATE_USER_INFO: case UserActionType.UPDATE_USER_INFO:
return { return state
...state, .mergeIn(['info', payload.uid], payload.info)
info: {
...state.info,
[payload.uid]: {
...state.info![payload.uid],
...payload.info
}
}
}
case UserActionType.CLEAR_ALL_DATA_USER: case UserActionType.CLEAR_ALL_DATA_USER:
return new UserState() return Map(new UserState())
case UserActionType.CLOSE_EDIT_PROFILE: case UserActionType.CLOSE_EDIT_PROFILE:
return { return state
...state, .set('openEditProfile', false)
openEditProfile: false
}
case UserActionType.OPEN_EDIT_PROFILE: case UserActionType.OPEN_EDIT_PROFILE:
return { return state
...state, .set('openEditProfile', true)
openEditProfile: true
} case UserActionType.HAS_MORE_DATA_PEOPLE:
case UserActionType.HAS_MORE_DATA_PEOPLE: return state
return { .setIn(['people', 'hasMoreData'], true)
...state,
people: {
...state.people,
hasMoreData: true
}
}
case UserActionType.NOT_MORE_DATA_PEOPLE: case UserActionType.NOT_MORE_DATA_PEOPLE:
return { return state
...state, .setIn(['people', 'hasMoreData'], false)
people: {
...state.people,
hasMoreData: false
}
}
case UserActionType.REQUEST_PAGE_PEOPLE: case UserActionType.REQUEST_PAGE_PEOPLE:
return { return state
...state, .setIn(['people', 'lastPageRequest'], payload.page)
people: {
...state.people,
lastPageRequest: payload.page
}
}
case UserActionType.LAST_USER_PEOPLE: case UserActionType.LAST_USER_PEOPLE:
return { return state
...state, .setIn(['people', 'lastUserId'], payload.lastUserId)
people: {
...state.people,
lastUserId: payload.lastUserId
}
}
default: default:
return state return state

View File

@@ -1,6 +1,7 @@
// - Import react components // - Import react components
import moment from 'moment/moment' import moment from 'moment/moment'
import _ from 'lodash' import _ from 'lodash'
import { Map } from 'immutable'
// - Import action types // - Import action types
import { VoteActionType } from 'constants/voteActionType' import { VoteActionType } from 'constants/voteActionType'
@@ -16,56 +17,26 @@ import { IVoteAction } from './IVoteAction'
* @param {object} state * @param {object} state
* @param {object} action * @param {object} action
*/ */
export let voteReducer = (state: VoteState = new VoteState(), action: IVoteAction) => { export let voteReducer = (state = Map(new VoteState()), action: IVoteAction) => {
let { payload } = action let { payload } = action
switch (action.type) { switch (action.type) {
/* _____________ CRUD _____________ */ /* _____________ CRUD _____________ */
case VoteActionType.ADD_VOTE: case VoteActionType.ADD_VOTE:
return { return state
...state, .setIn(['postVotes', payload.postId, payload.userId], payload)
postVotes: {
...state.postVotes,
[payload.postId]: {
...state.postVotes![payload.postId],
[payload.userId]: {
...payload
}
}
}
}
case VoteActionType.ADD_VOTE_LIST: case VoteActionType.ADD_VOTE_LIST:
return { return state
...state, .set('postVotes', payload)
postVotes: { .set('loaded', true)
...payload
},
loaded: true
}
case VoteActionType.DELETE_VOTE: case VoteActionType.DELETE_VOTE:
let parsedVotes = {} return state
if (state.postVotes![payload.postId]) { .deleteIn(['postVotes', payload.postId, payload.userId])
Object.keys(state.postVotes![payload.postId]).map((id) => {
if (id !== payload.userId) {
_.merge(parsedVotes, { [id]: { ...state.postVotes![payload.postId][id] } })
}
})
}
return {
...state,
postVotes: {
...state.postVotes,
[payload.postId]: {
...parsedVotes
}
}
}
case VoteActionType.CLEAR_ALL_DATA_VOTE: case VoteActionType.CLEAR_ALL_DATA_VOTE:
return new VoteState() return Map(new VoteState())
default: default:
return state return state

View File

@@ -11,6 +11,7 @@ import { eventChannel, Channel } from 'redux-saga'
import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType' import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
import { Post } from 'core/domain/posts' import { Post } from 'core/domain/posts'
import { postSelector } from 'store/reducers/posts/postSelector' import { postSelector } from 'store/reducers/posts/postSelector'
import {Map} from 'immutable'
/** /**
* Get service providers * Get service providers
*/ */
@@ -40,15 +41,15 @@ function* setComments(ownerId: string, postId: string, comments: postComments) {
*/ */
yield put(commentActions.addCommentList(comments)) yield put(commentActions.addCommentList(comments))
let commentsCount: number let commentsCount: number
const post: Post = yield select(postSelector.getPost, ownerId, postId) const post: Map<string, any> = yield select(postSelector.getPost, ownerId, postId)
if (post) { if (post) {
const desiredComments = comments[postId] const desiredComments = comments[postId]
if (desiredComments) { if (desiredComments) {
commentsCount = Object.keys(desiredComments).length commentsCount = Object.keys(desiredComments).length
let sortedObjects = yield CommentAPI.sortCommentsByDate(desiredComments) let sortedObjects = yield CommentAPI.sortCommentsByDate(desiredComments)
post.comments = sortedObjects const updatedPost = post.set('comments', Map(sortedObjects))
post.commentCounter = commentsCount .set('commentCounter', commentsCount)
yield put(postActions.updatePost(post)) yield put(postActions.updatePost(updatedPost))
} }
} }
} }
@@ -72,10 +73,8 @@ function* dbFetchComments(ownerId: string, postId: string) {
yield call(setComments, ownerId, postId, comments) yield call(setComments, ownerId, postId, comments)
} }
} finally { } finally {
console.trace('FiNALLY')
if (yield cancelled()) { if (yield cancelled()) {
channelSubscription.close() channelSubscription.close()
console.trace('comments cancelled')
} }
} }

1
src/typings/react-loadable.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module 'react-loadable'

View File

@@ -141,6 +141,12 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-helmet@^5.0.5":
version "5.0.5"
resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-5.0.5.tgz#30a12b0431732a20882f0fa9b75bf56ff91700d3"
dependencies:
"@types/react" "*"
"@types/react-infinite-scroller@^1.0.4": "@types/react-infinite-scroller@^1.0.4":
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/react-infinite-scroller/-/react-infinite-scroller-1.0.6.tgz#ab36d95ce25712b70ccff439f2a3204bb15476d9" resolved "https://registry.yarnpkg.com/@types/react-infinite-scroller/-/react-infinite-scroller-1.0.6.tgz#ab36d95ce25712b70ccff439f2a3204bb15476d9"
@@ -211,6 +217,13 @@
"@types/react" "*" "@types/react" "*"
redux "^3.6.0" redux "^3.6.0"
"@types/redux-immutable@^3.0.38":
version "3.0.38"
resolved "https://registry.yarnpkg.com/@types/redux-immutable/-/redux-immutable-3.0.38.tgz#9e2fcce4a9c64e60f13de2165d2765f7af85d487"
dependencies:
immutable "^3.8.1"
redux "^3.6.0"
"@types/redux-logger@^3.0.4": "@types/redux-logger@^3.0.4":
version "3.0.5" version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.5.tgz#d1a02758f90845899cd304aa0912daeba2028eb6" resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.5.tgz#d1a02758f90845899cd304aa0912daeba2028eb6"
@@ -2250,6 +2263,10 @@ execa@^0.7.0:
signal-exit "^3.0.0" signal-exit "^3.0.0"
strip-eof "^1.0.0" strip-eof "^1.0.0"
exenv@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
expand-brackets@^0.1.4: expand-brackets@^0.1.4:
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@@ -3175,6 +3192,10 @@ ignore-walk@^3.0.1:
dependencies: dependencies:
minimatch "^3.0.4" minimatch "^3.0.4"
immutable@^3.8.1, immutable@^3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
import-lazy@^2.1.0: import-lazy@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
@@ -5846,7 +5867,7 @@ promzard@^0.3.0:
dependencies: dependencies:
read "1" read "1"
prop-types@^15.0.0, prop-types@^15.5.7: prop-types@^15.0.0, prop-types@^15.5.0, prop-types@^15.5.7:
version "15.6.1" version "15.6.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
dependencies: dependencies:
@@ -6133,6 +6154,15 @@ react-event-listener@^0.5.1:
prop-types "^15.6.0" prop-types "^15.6.0"
warning "^3.0.0" warning "^3.0.0"
react-helmet@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-5.2.0.tgz#a81811df21313a6d55c5f058c4aeba5d6f3d97a7"
dependencies:
deep-equal "^1.0.1"
object-assign "^4.1.1"
prop-types "^15.5.4"
react-side-effect "^1.1.0"
react-infinite-scroller@^1.1.2: react-infinite-scroller@^1.1.2:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.1.3.tgz#c4230024bc237ce876c76b2e38d77dc1f6aabb26" resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.1.3.tgz#c4230024bc237ce876c76b2e38d77dc1f6aabb26"
@@ -6169,9 +6199,15 @@ react-linkify@^0.2.1:
prop-types "^15.5.8" prop-types "^15.5.8"
tlds "^1.57.0" tlds "^1.57.0"
react-localize-redux@^2.15.1: react-loadable@^5.3.1:
version "2.15.1" version "5.3.1"
resolved "https://registry.yarnpkg.com/react-localize-redux/-/react-localize-redux-2.15.1.tgz#b32969eae191bcbcca2f0ffdc20802768fec0601" resolved "https://registry.yarnpkg.com/react-loadable/-/react-loadable-5.3.1.tgz#9699e9a08fed49bacd69caaa282034b62a76bcdd"
dependencies:
prop-types "^15.5.0"
react-localize-redux@^2.16.0:
version "2.16.0"
resolved "https://registry.yarnpkg.com/react-localize-redux/-/react-localize-redux-2.16.0.tgz#9e4b1c4703984decfb33fcf8f9ab51454af5211e"
dependencies: dependencies:
flat "^2.0.1" flat "^2.0.1"
reselect "^3.0.1" reselect "^3.0.1"
@@ -6290,6 +6326,13 @@ react-share@^2.0.0:
jsonp "^0.2.1" jsonp "^0.2.1"
prop-types "^15.5.8" prop-types "^15.5.8"
react-side-effect@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.5.tgz#f26059e50ed9c626d91d661b9f3c8bb38cd0ff2d"
dependencies:
exenv "^1.2.1"
shallowequal "^1.0.1"
react-string-replace@^0.4.0: react-string-replace@^0.4.0:
version "0.4.1" version "0.4.1"
resolved "https://registry.yarnpkg.com/react-string-replace/-/react-string-replace-0.4.1.tgz#024c6ac0a91163ecae439a1b197d3e1660c98c0d" resolved "https://registry.yarnpkg.com/react-string-replace/-/react-string-replace-0.4.1.tgz#024c6ac0a91163ecae439a1b197d3e1660c98c0d"
@@ -6513,6 +6556,10 @@ redux-devtools-dock-monitor@^1.1.3:
react-dock "^0.2.4" react-dock "^0.2.4"
react-pure-render "^1.0.2" react-pure-render "^1.0.2"
redux-devtools-extension@^2.13.2:
version "2.13.2"
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d"
redux-devtools-instrument@^1.0.1: redux-devtools-instrument@^1.0.1:
version "1.8.3" version "1.8.3"
resolved "https://registry.yarnpkg.com/redux-devtools-instrument/-/redux-devtools-instrument-1.8.3.tgz#c510d67ab4e5e4525acd6e410c25ab46b85aca7c" resolved "https://registry.yarnpkg.com/redux-devtools-instrument/-/redux-devtools-instrument-1.8.3.tgz#c510d67ab4e5e4525acd6e410c25ab46b85aca7c"
@@ -6544,6 +6591,10 @@ redux-devtools@^3.4.1:
prop-types "^15.5.7" prop-types "^15.5.7"
redux-devtools-instrument "^1.0.1" redux-devtools-instrument "^1.0.1"
redux-immutable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3"
redux-logger@^3.0.1: redux-logger@^3.0.1:
version "3.0.6" version "3.0.6"
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
@@ -7006,6 +7057,10 @@ sha@~2.0.1:
graceful-fs "^4.1.2" graceful-fs "^4.1.2"
readable-stream "^2.0.2" readable-stream "^2.0.2"
shallowequal@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f"
shebang-command@^1.2.0: shebang-command@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"