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

View File

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

View File

@@ -4,11 +4,14 @@ import * as moment from 'moment/moment'
* @param title log title
* @param data log data object
*/
const logger = (title: string, data: any) => {
const logger = (title: string, ...data: any[]) => {
const randomColor = getRandomColor()
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 =========================================`)
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`)
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 {
logger,
getRandomColor,
updateObject
updateObject,
getStateSlice
}

View File

@@ -1,3 +1,4 @@
import {List} from 'immutable'
// Get tags from post content
export const detectTags: (content: string, character: string) => string[] = (content: string, character: string) => {
@@ -27,3 +28,15 @@ export const sortObjectsDate = (objects: any) => {
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 { connect } from 'react-redux'
import { push } from 'react-router-redux'
import {Map, List as ImuList} from 'immutable'
// - Material UI
import List, {
ListItem,
ListItemIcon,
@@ -119,7 +122,7 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
/**
* 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
*/
@@ -210,9 +213,9 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
let usersParsed: any = []
if (usersOfCircle) {
Object.keys(usersOfCircle).forEach((userId, index) => {
const { fullName } = usersOfCircle[userId]
let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : ''
usersOfCircle.forEach((user: Map<string, any>, userId) => {
const fullName = user.get('fullName')
let avatar = user.get('avatar', '')
usersParsed.push(
<ListItem
button
@@ -303,9 +306,9 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
<ListItemIcon className={classes.icon}>
<SvgGroup />
</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>
{circle.isSystem ? null : rightIconMenu}
{circle.get('isSystem') ? null : rightIconMenu}
</ListItemSecondaryAction>
</ListItem>
<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
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
const { circle, authorize, server } = state
const { userTies } = circle
const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle
const circleId = ownProps.circle.id!
let usersOfCircle: { [userId: string]: UserTie } = {}
Object.keys(userTies).forEach((userTieId) => {
const theUserTie = userTies[userTieId] as UserTie
if (theUserTie.circleIdList!.indexOf(ownProps.id) > -1) {
usersOfCircle = {
...usersOfCircle,
[userTieId]: theUserTie
}
const mapStateToProps = (state: Map<string, any>, ownProps: ICircleComponentProps) => {
const userTies: Map<string, any> = state.getIn(['circle', 'userTies'])
const uid = state.getIn(['authorize', 'uid'])
const circles: Map<string, Map<string, any>> = state.getIn(['circle', 'circleList'], {})
const currentCircle: Map<string, any> = circles.get(ownProps.id, Map({}))
const circleId = ownProps.circle.get('id')
let usersOfCircle: Map<string, any> = Map({})
userTies.forEach((userTie , userTieId) => {
const theUserTie: Map<string, any> = userTie
const circleList: ImuList<string> = theUserTie.getIn(['circleIdList'])
if ( circleList.indexOf(ownProps.id) > -1) {
usersOfCircle = usersOfCircle.set(userTieId!, theUserTie)
}
})
return {
usersOfCircle,
openSetting: (state.circle && state.circle.openSetting && state.circle.openSetting[circleId]) ? state.circle.openSetting[circleId] : false,
userInfo: state.user.info
openSetting: state.getIn(['circle', 'openSetting', circleId], false),
userInfo: state.getIn(['user', 'info'])
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { Profile } from 'core/domain/users/profile'
import { UserTie } from 'core/domain/circles'
export interface IFindPeopleComponentProps {
@@ -11,11 +12,8 @@ export interface IFindPeopleComponentProps {
/**
* 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}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,8 +2,11 @@
import React, { Component } from 'react'
import { CircularProgress } from 'material-ui/Progress'
import Dialog from 'material-ui/Dialog'
import red from 'material-ui/colors/red'
import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps'
import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
import Grid from 'material-ui/Grid/Grid'
import { Typography } from 'material-ui'
// - Import app components
@@ -11,26 +14,78 @@ import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
export default class MasterLoadingComponent extends Component<IMasterLoadingComponentProps, IMasterLoadingComponentState> {
// Constructor
constructor (props: IMasterLoadingComponentProps) {
constructor(props: IMasterLoadingComponentProps) {
super(props)
// 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 () {
const {activeLoading} = this.props
render() {
return (
<div className='mLoading__loading' style={{ display: (activeLoading ? 'flex' : 'none') }}>
<CircularProgress
color='secondary'
size={50}
variant='determinate'
value={25}
min={0}
max={50}
/>
<div className='mLoading__loading'>
{
this.loadProgress()
}
</div>
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map } from 'immutable'
import { Card, CardActions, CardHeader, CardMedia, CardContent } from 'material-ui'
import List, {
@@ -108,15 +109,15 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
/**
* 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
*/
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
*/
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
*/
@@ -132,11 +133,11 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
/**
* 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
*/
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)
}
} else { // In edit status we pass post to update functions
postModel!.body = postText
postModel!.tags = tags
postModel!.image = image
postModel!.imageFullPath = imageFullPath
postModel!.disableComments = disableComments
postModel!.disableSharing = disableSharing
const updatedPost = postModel!.set('body', postText)
.set('tags', tags)
.set('image', image)
.set('imageFullPath', imageFullPath)
.set('disableComments', disableComments)
.set('disableSharing', disableSharing)
update!(postModel!, onRequestClose)
update!(updatedPost, onRequestClose)
}
}
@@ -337,34 +338,38 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps, IPos
if (!nextProps.open) {
const { postModel } = this.props
this.setState({
/**
* Post text
*/
postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '') : '',
/**
* The URL image of the post
*/
image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '') : '',
/**
* The path identifier of image on the server
*/
imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '') : '',
/**
* If it's true gallery will be open
*/
galleryOpen: false,
/**
* If it's true post button will be disabled
*/
disabledPost: true,
/**
* If it's true comment will be disabled on post
*/
disableComments: this.props.edit && postModel ? postModel.disableComments! : false,
/**
* If it's true share will be disabled on post
*/
disableSharing: this.props.edit && postModel ? postModel.disableSharing! : false
/**
* Post text
*/
postText: this.props.edit && postModel ? postModel.get('body', '') : '',
/**
* The URL image of the post
*/
image: this.props.edit && postModel ? postModel.get('image', '') : '',
/**
* The path identifier of image on the server
*/
imageFullPath: this.props.edit && postModel ? postModel.get('imageFullPath', '') : '',
/**
* If it's true gallery will be open
*/
galleryOpen: false,
/**
* Whether menu is open
*/
menuOpen: false,
/**
* If it's true post button will be disabled
*/
disabledPost: true,
/**
* If it's true comment will be disabled on post
*/
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) => {
return {
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
* @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 {
translate: getTranslate(state.locale),
postImageState: state.imageGallery.status,
ownerAvatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
ownerDisplayName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : ''
translate: getTranslate(state.get('locale')),
postImageState: state.getIn(['imageGallery', 'status']),
ownerAvatar: user.avatar || '',
ownerDisplayName: user.fullName || ''
}
}

View File

@@ -1,8 +1,8 @@
// - Import react components
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import config from 'src/config'
import {Map} from 'immutable'
// - Material UI
import { grey } from 'material-ui/colors'
@@ -33,39 +33,6 @@ import { IProfileHeaderComponentState } from './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
* @param {object} props is an object properties of component
@@ -186,7 +153,7 @@ export class ProfileHeaderComponent extends Component<IProfileHeaderComponentPro
/>
<div className='left'>
{/* 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='fullName'>
{this.props.fullName}
@@ -235,11 +202,11 @@ const mapDispatchToProps = (dispatch: any, ownProps: IProfileHeaderComponentProp
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: IProfileHeaderComponentProps) => {
const mapStateToProps = (state: Map<string, any>, ownProps: IProfileHeaderComponentProps) => {
return {
translate: getTranslate(state.locale),
editProfileOpen: state.user.openEditProfile
translate: getTranslate(state.get('locale')),
editProfileOpen: state.getIn(['user', 'openEditProfile'])
}
}

View File

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

View File

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

View File

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

View File

@@ -105,9 +105,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div>
<FacebookShareButton
onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`}
quote={post.body}
hashtag={`#${post.tags![0]}`}>
url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.get('body')}
hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}>
<FacebookIcon
@@ -121,9 +121,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div>
<TwitterShareButton
onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`}
quote={post.body}
hashtag={`#${post.tags![0]}`}>
url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.get('body')}
hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}>
<TwitterIcon
@@ -137,9 +137,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div>
<LinkedinShareButton
onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`}
quote={post.body}
hashtag={`#${post.tags![0]}`}>
url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.get('body')}
hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}>
<LinkedinIcon
@@ -153,9 +153,9 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
<div>
<GooglePlusShareButton
onShareWindowClose={onClose}
url={`${location.origin}/${post.ownerUserId}/posts/${post.id}`}
quote={post.body}
hashtag={`#${post.tags![0]}`}>
url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`}
quote={post.get('body')}
hashtag={`#${post.getIn(['tags', 0], '')}`}>
<MenuItem >
<ListItemIcon classes={{ root: classes.networkShare }}>
<GooglePlusIcon
@@ -174,7 +174,7 @@ export class ShareDialogComponent extends Component<IShareDialogComponentProps,
</MenuItem>
</MenuList>)
: <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'>
Link has been copied to clipboard ...
</Typography>
@@ -205,7 +205,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IShareDialogComponentProps)
*/
const mapStateToProps = (state: any, ownProps: IShareDialogComponentProps) => {
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 { connect } from 'react-redux'
import Avatar from 'material-ui/Avatar'
import { Map } from 'immutable'
// - Import app components
@@ -95,8 +96,8 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IUserAvatarComponentPr
*/
const mapStateToProps = (state: any, ownProps: IUserAvatarComponentProps) => {
return {
avatarURL: state.imageGallery.imageURLList,
imageRequests: state.imageGallery.imageRequests
avatarURL: state.getIn(['imageGallery', 'imageURLList']),
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 { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
import { ServerRequestModel } from 'models/server/serverRequestModel'
import {Map, List} from 'immutable'
export interface IUserBoxComponentProps {
/**
* User identifier
*
* @type {string}
* @memberof IUserBoxComponentProps
*/
userId: string
/**
* User
*
* @type {User}
* @memberof IUserBoxComponentProps
*/
user: UserTie
/**
* Circles
*
* @type {{[circleId: string]: Circle}}
* @memberof IUserBoxComponentProps
*/
circles?: {[circleId: string]: Circle}
circles?: Map<string, Map<string, any>>
/**
* List of circles' id
*
* @type {string[]}
* @memberof IUserBoxComponentProps
*/
userBelongCircles?: string[]
userBelongCircles?: List<string>
/**
* Whether current user followed this user
@@ -45,54 +33,38 @@ export interface IUserBoxComponentProps {
/**
* The number of circles
*
* @type {number}
* @memberof IUserBoxComponentProps
*/
belongCirclesCount?: number
/**
* The first circle
*
* @type {User}
* @memberof IUserBoxComponentProps
*/
firstBelongCircle?: Circle
firstBelongCircle?: Map<string, any>
/**
* Avatar address
*
* @type {string}
* @memberof IUserBoxComponentProps
*/
avatar?: string
/**
* User full name
*
* @type {string}
* @memberof IUserBoxComponentProps
*/
fullName?: string
/**
* The `Following` circle identifier of current user
*/
followingCircleId?: string
followingCircle?: Map<string, any>
/**
* Create a circle
*
* @memberof IUserBoxComponentProps
*/
createCircle?: (name: string) => any
/**
* 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
@@ -107,12 +79,12 @@ export interface IUserBoxComponentProps {
/**
* 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
*/
removeSelectedCircles?: (userId: string, circleList: string[]) => any
removeSelectedCircles?: (userId: string, circleList: List<string>) => any
/**
* Open select circle box
@@ -126,8 +98,6 @@ export interface IUserBoxComponentProps {
/**
* Redirect page to [url]
*
* @memberof IUserBoxComponentProps
*/
goTo?: (url: string) => any
@@ -149,7 +119,7 @@ export interface IUserBoxComponentProps {
/**
* Keep selected circles for refere user
*/
selectedCircles?: string[]
selectedCircles?: List<string>
/**
* 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 { getTranslate, getActiveLanguage } from 'react-localize-redux'
import classNames from 'classnames'
import {Map, List as ImuList} from 'immutable'
// - Material UI
import Paper from 'material-ui/Paper'
@@ -112,7 +113,6 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
borderRadius: '4px'
}
}
selectedCircles: string[]
/**
* Component constructor
@@ -140,7 +140,6 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/
disabledDoneCircles: true
}
this.selectedCircles = userBelongCircles!.slice()
// Binding functions to `this`
this.handleChangeName = this.handleChangeName.bind(this)
this.onCreateCircle = this.onCreateCircle.bind(this)
@@ -153,11 +152,10 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Handle follow user
*/
handleDoneAddCircle = () => {
const { userId, user, addUserToCircle, selectedCircles, deleteFollowingUser } = this.props
const { avatar, fullName } = user
const { userId, user, addUserToCircle, selectedCircles, deleteFollowingUser, avatar, fullName } = this.props
const { disabledDoneCircles } = this.state
if (!disabledDoneCircles) {
if (selectedCircles!.length > 0) {
if (selectedCircles!.count() > 0) {
addUserToCircle!(selectedCircles!, { avatar, userId, fullName })
} else {
deleteFollowingUser!(userId)
@@ -171,14 +169,13 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
onFollowUser = (event: any) => {
// This prevents ghost click
event.preventDefault()
const { isFollowed, followUser, followingCircleId, userId, user, followRequest } = this.props
const { isFollowed, followUser, followingCircle, userId, user, followRequest, avatar, fullName } = this.props
if (followRequest && followRequest.status === ServerRequestStatusType.Sent) {
return
}
const { avatar, fullName } = user
if (!isFollowed) {
followUser!(followingCircleId!, { avatar, userId, fullName })
followUser!(followingCircle!.get('id'), { avatar, userId, fullName })
} else {
this.onRequestOpenAddCircle()
}
@@ -234,17 +231,13 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => {
const { userBelongCircles, circles, setSelectedCircles, selectedCircles, userId } = this.props
let newSelectedCircles = selectedCircles!.slice()
let newSelectedCircles = selectedCircles!
if (isInputChecked) {
newSelectedCircles = [
...selectedCircles!,
circleId
]
newSelectedCircles = selectedCircles!.push(circleId)
} else {
const circleIndex = selectedCircles!.indexOf(circleId)
newSelectedCircles.splice(circleIndex, 1)
newSelectedCircles = newSelectedCircles.remove(circleIndex)
}
setSelectedCircles!(userId, newSelectedCircles)
@@ -258,39 +251,39 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/
circleList = () => {
let { circles, userId, userBelongCircles, selectedCircles, classes } = this.props
const circleDomList: any[] = []
if (circles) {
const parsedDate = Object.keys(circles).map((circleId, index) => {
let isBelong = selectedCircles ? selectedCircles!.indexOf(circleId) > -1 : false
circles.forEach((circle, circleId) => {
let isBelong = selectedCircles ? selectedCircles!.indexOf(circleId!) > -1 : false
// Create checkbox for selected/unselected circle
return (
circleDomList.push(
<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>
<Checkbox
onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId)}
onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId!)}
checked={isBelong}
/>
</ListItemSecondaryAction>
</ListItem>)
})
return parsedDate
return circleDomList
}
}
/**
* Check if the the selected circles changed
*/
selectedCircleChange = (selectedCircles: string[]) => {
selectedCircleChange = (selectedCircles: ImuList<string>) => {
let isChanged = false
const { userBelongCircles, circles } = this.props
if (selectedCircles.length === userBelongCircles!.length) {
for (let circleIndex: number = 0; circleIndex < selectedCircles.length; circleIndex++) {
const selectedCircleId = selectedCircles[circleIndex]
if (selectedCircles.count() === userBelongCircles!.count()) {
for (let circleIndex: number = 0; circleIndex < selectedCircles.count(); circleIndex++) {
const selectedCircleId = selectedCircles.get(circleIndex)
if (!(userBelongCircles!.indexOf(selectedCircleId) > -1)) {
isChanged = true
break
@@ -308,7 +301,18 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/
render () {
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 (
<Paper key={userId} elevation={1} className={classNames('grid-cell', classes.paper)}>
@@ -331,7 +335,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
</div>
<div onClick={() => this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{ cursor: 'pointer' }}>
<div>
{this.props.user.fullName}
{this.props.fullName}
</div>
</div>
<div style={this.styles.followButton as any}>
@@ -344,7 +348,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
}
>
{!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>
</div>
</div>
@@ -414,7 +418,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps) => {
return {
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)),
deleteFollowingUser: (followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)),
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
* @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 } = authorize
const { request } = server
const uid = state.getIn(['authorize', 'uid'])
const request = state.getIn(['server', 'request'])
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : []
const isFollowed = userBelongCircles.length > 0
const followingCircleId = circles ? Object.keys(circles)
.filter((circleId) => circles[circleId].isSystem && circles[circleId].name === `Following`)[0] : ''
const followRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)] : null
const addToCircleRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, ownProps.userId)] : null
const deleteFollowingUserRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, ownProps.userId)] : null
const selectedCircles = circle.selectedCircles ? circle.selectedCircles[ownProps.userId] : []
const isSelecteCirclesOpen = circle.openSelecteCircles ? circle.openSelecteCircles[ownProps.userId] : []
const circles: Map<string, Map<string, any>> = state.getIn(['circle', 'circleList'], {})
const userBelongCircles: ImuList<any> = state.getIn(['circle', 'userTies', ownProps.userId, 'circleIdList'], ImuList())
const isFollowed = userBelongCircles.count() > 0
const followingCircle = circles
.filter((followingCircle) => followingCircle!.get('isSystem', false) && followingCircle!.get('name') === `Following`)
.toArray()[0]
const followRequestId = StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)
const followRequest = state.getIn(['server', 'request', followRequestId])
const addToCircleRequestId = StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, 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 {
translate: getTranslate(state.locale),
translate: getTranslate(state.get('locale')),
isSelecteCirclesOpen,
isFollowed,
selectedCircles,
circles,
followingCircleId,
followingCircle,
userBelongCircles,
followRequest,
belongCirclesCount: userBelongCircles.length || 0,
firstBelongCircle: userBelongCircles ? (circles ? circles[userBelongCircles[0]] : {}) : {},
avatar: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].avatar || '' : '',
fullName: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].fullName || '' : ''
belongCirclesCount: userBelongCircles.count() || 0,
firstBelongCircle: userBelongCircles ? circles.get(userBelongCircles.get(0), Map({})) : Map({}),
avatar: userBox.avatar || '' ,
fullName: userBox.fullName || ''
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,8 @@
ADD_VIDEO_POST = 'ADD_VIDEO_POST',
ADD_POST = 'ADD_POST',
UPDATE_POST = 'UPDATE_POST',
UPDATE_POST_COMMENTS = 'UPDATE_POST_COMMENTS',
UPDATE_POST_VOTES = 'UPDATE_POST_VOTES',
DELETE_POST = 'DELETE_POST',
ADD_LIST_POST = 'ADD_LIST_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) => {
return {
translate: getTranslate(state.locale)
translate: getTranslate(state.get('locale'))
}
}

View File

@@ -1,5 +1,6 @@
// - Import react components
import { HomeRouter } from 'routes'
import { Map } from 'immutable'
import React, { Component } from 'react'
import _ from 'lodash'
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
@@ -172,7 +173,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
if (!isVerifide) {
goTo!('/emailVerification')
} else if (!global.defaultLoadDataStatus) {
} else if (!global.get('defaultLoadDataStatus')) {
clearData!()
loadData!()
@@ -188,7 +189,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
* @memberof Home
*/
render() {
const HR = HomeRouter as any
const HR = HomeRouter
const { loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback, translate, classes, theme } = this.props
const { drawerOpen } = this.state
const drawer = (
@@ -337,29 +338,28 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
const { authorize, global, user, post, imageGallery, notify, circle } = state
const { uid } = authorize
let mergedPosts = {}
const circles = circle ? (circle.circleList || {}) : {}
const followingUsers = circle ? circle.userTies : {}
const posts = post.userPosts ? post.userPosts[authorize.uid] : {}
const hasMorePosts = post.stream.hasMoreData
Object.keys(followingUsers).forEach((userId) => {
let newPosts = post.userPosts ? post.userPosts[userId] : {}
_.merge(mergedPosts, newPosts)
const mapStateToProps = (state: Map<string, any>, ownProps: IHomeComponentProps) => {
const uid = state.getIn(['authorize', 'uid'], {})
const global = state.get('global', {})
let mergedPosts = Map({})
const circles = state.getIn(['circle', 'circleList'], {})
const followingUsers: Map<string, any> = state.getIn(['circle', 'userTies'], {})
const posts = state.getIn(['post', 'userPosts', uid ], {})
const hasMorePosts = state.getIn(['post', 'stream', 'hasMoreData' ], true)
followingUsers.forEach((user, userId) => {
let newPosts = state.getIn(['post', 'userPosts', userId], {})
mergedPosts = mergedPosts.merge(newPosts)
})
_.merge(mergedPosts, posts)
mergedPosts = mergedPosts.merge(posts)
return {
authed: authorize.authed,
isVerifide: authorize.isVerifide,
mainStyle: global.sidebarMainStyle,
translate: getTranslate(state.locale),
currentLanguage: getActiveLanguage(state.locale).code,
authed: state.getIn(['authorize', 'authed'], false),
isVerifide: state.getIn(['authorize', 'isVerifide'], false),
translate: getTranslate(state.get('locale')),
currentLanguage: getActiveLanguage(state.get('locale')).code,
mergedPosts,
global,
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 { withStyles } from 'material-ui/styles'
import config from 'src/config'
import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { localize } from 'react-localize-redux'
// - Import actions
import * as authorizeActions from 'src/store/actions/authorizeActions'
@@ -20,6 +20,7 @@ import { ILoginComponentProps } from './ILoginComponentProps'
import { ILoginComponentState } from './ILoginComponentState'
import { OAuthType } from 'src/core/domain/authorize'
import Grid from 'material-ui/Grid/Grid'
import CommonAPI from 'api/CommonAPI'
const styles = (theme: any) => ({
textField: {
@@ -253,9 +254,8 @@ const mapDispatchToProps = (dispatch: any, ownProps: ILoginComponentProps) => {
*/
const mapStateToProps = (state: any, ownProps: ILoginComponentProps) => {
return {
translate: getTranslate(state.locale)
}
}
// - 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 Snackbar from 'material-ui/Snackbar'
import { LinearProgress } from 'material-ui/Progress'
import {Helmet} from 'react-helmet'
import {Map} from 'immutable'
// - Import components
@@ -53,7 +55,6 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
}
// Binding functions to `this`
this.handleLoading = this.handleLoading.bind(this)
this.handleMessage = this.handleMessage.bind(this)
}
@@ -63,14 +64,6 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
this.props.closeMessage()
}
// Handle loading
handleLoading = (status: boolean) => {
this.setState({
loading: status,
authed: false
})
}
componentDidCatch (error: any, info: any) {
console.log('===========Catched by React componentDidCatch==============')
console.log(error, info)
@@ -130,6 +123,11 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
return (
<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 /> : ''}
<div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}>
<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='title'>Loading ... </div>
</div>
<MasterLoading activeLoading={global.showMasterLoading} handleLoading={this.handleLoading} />
{progress.visible ? <MasterLoading /> : ''}
<MasterRouter enabled={!loading} data={{uid}} />
<Snackbar
open={this.props.global.messageOpen}
@@ -196,15 +194,16 @@ const mapDispatchToProps = (dispatch: any, ownProps: IMasterComponentProps) => {
* Map state to props
* @param {object} state
*/
const mapStateToProps = (state: any) => {
const { authorize, global, user, post, comment, imageGallery, vote, notify, circle } = state
const { sendFeedbackStatus } = global
const mapStateToProps = (state: Map<string, any>) => {
const authorize = Map(state.get('authorize', {})).toJS()
const global = Map(state.get('global', {})).toJS()
const { sendFeedbackStatus, progress } = global
return {
sendFeedbackStatus,
progress,
guest: authorize.guest,
uid: authorize.uid,
authed: authorize.authed,
progress: global.progress,
global: global
}

View File

@@ -9,6 +9,7 @@ import { push } from 'react-router-redux'
import AppBar from 'material-ui/AppBar'
import Typography from 'material-ui/Typography'
import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import {Map} from 'immutable'
// - Import app components
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
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: IPeopleComponentProps) => {
const mapStateToProps = (state: Map<string, any>, ownProps: IPeopleComponentProps) => {
return {
translate: getTranslate(state.locale),
uid: state.authorize.uid,
circlesLoaded: state.circle.loaded
translate: getTranslate(state.get('locale')),
uid: state.getIn(['authorize', 'uid'], 0),
circlesLoaded: state.getIn(['circle', 'loaded'])
}
}

View File

@@ -2,6 +2,7 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import {Map} from 'immutable'
// - Import app components
import Stream from 'containers/stream'
@@ -76,12 +77,15 @@ const mapDispatchToProps = (dispatch: any,ownProps: IPostPageComponentProps) =>
* @param {object} ownProps is the props belong to 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 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{
avatar: state.user.info && state.user.info[userId] ? state.user.info[userId].avatar : '',
name: state.user.info && state.user.info[userId] ? state.user.info[userId].fullName : '',
posts: state.post.userPosts && state.post.userPosts[userId] ? {[postId] : { ...state.post.userPosts[userId][postId]}} : {}
avatar: userInfo ? userInfo.avatar : '',
name: userInfo ? userInfo.fullName : '',
posts
}
}

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { Post } from 'src/core/domain/posts'
import {Map} from 'immutable'
export interface IStreamComponentProps {
@@ -82,7 +83,7 @@ export interface IStreamComponentProps {
* @type {{[postId: string]: Post}}
* @memberof IStreamComponentProps
*/
posts: {[postId: string]: Post}
posts: Map<string, Map<string, any>>
/**
* 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 InfiniteScroll from 'react-infinite-scroller'
import { getTranslate, getActiveLanguage } from 'react-localize-redux'
import { Map, List as ImuList } from 'immutable'
// - Import app components
import PostComponent from 'src/components/post'
@@ -128,56 +129,58 @@ export class StreamComponent extends Component<IStreamComponentProps, IStreamCom
*/
postLoad = () => {
let { posts, match } = this.props
let { match } = this.props
let posts: Map<string, Map<string, any>> = this.props.posts
let { tag } = match.params
if (posts === undefined || !(Object.keys(posts).length > 0)) {
if (posts === undefined || !(posts.keySeq().count() > 0)) {
return (
<h1>
'Nothing has shared.'
</h1>
)
} else {
let postBack = { divided: false, oddPostList: [], evenPostList: [] }
let parsedPosts: any = []
Object.keys(posts).forEach((postId) => {
)
} else {
let postBack = { divided: false, oddPostList: [], evenPostList: [] }
let parsedPosts: ImuList<any> = ImuList()
posts.forEach((post: Map<string, any>) => {
if (tag) {
let regex = new RegExp('#' + tag, 'g')
let postMatch = posts[postId].body!.match(regex)
let postMatch = String(post.get('body', '')).match(regex)
if (postMatch !== null) {
parsedPosts.push({ ...posts[postId] })
parsedPosts = parsedPosts.push(post)
}
} else {
parsedPosts.push({ ...posts[postId] })
parsedPosts = parsedPosts.push(post)
}
})
const sortedPosts = PostAPI.sortObjectsDate(parsedPosts)
if (sortedPosts.length > 6) {
const sortedPosts = PostAPI.sortImuObjectsDate(parsedPosts)
if (sortedPosts.count() > 6) {
postBack.divided = true
} else {
postBack.divided = false
}
sortedPosts.forEach((post: Post, index: any) => {
let index = 0
sortedPosts.forEach((post) => {
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> : ''}
<PostComponent key={`${post.id!}-stream-div-post`} post={post} />
<PostComponent key={`${post!.get('id')}-stream-div-post`} post={post! as any} />
</div>
)
if ((index % 2) === 1 && postBack.divided) {
postBack.oddPostList.push(newPost as never)
} else {
postBack.evenPostList.push(newPost as never)
}
++index
})
return postBack
}
@@ -276,14 +279,15 @@ const mapDispatchToProps = (dispatch: any, ownProps: IStreamComponentProps) => {
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: IStreamComponentProps) => {
const { post } = state
const mapStateToProps = (state: Map<string, any>, ownProps: IStreamComponentProps) => {
const uid = state.getIn(['authorize', 'uid'])
const user = state.getIn(['user', 'info', uid])
return {
translate: getTranslate(state.locale),
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : ''
translate: getTranslate(state.get('locale')),
avatar: user.avatar || '',
fullName: user.fullName || ''
}
}
// - 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 PrivateRoute from './PrivateRoute'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
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 MasterLoadingComponent from 'components/masterLoading/MasterLoadingComponent'
// - Async Components
const AsyncStream = asyncComponent(() => {
return import('containers/stream')
const AsyncStream = Loadable({
loader: () => import('containers/stream'),
loading: MasterLoadingComponent,
})
const AsyncProfile = asyncComponent(() => {
return import('containers/profile')
const AsyncProfile = Loadable({
loader: () => import('containers/profile'),
loading: MasterLoadingComponent,
})
const AsyncPostPage = asyncComponent(() => {
return import('containers/postPage')
const AsyncPostPage = Loadable({
loader: () => import('containers/postPage'),
loading: MasterLoadingComponent,
})
const AsyncPeople = asyncComponent(() => {
return import('containers/people')
const AsyncPeople = Loadable({
loader: () => import('containers/people'),
loading: MasterLoadingComponent,
})
/**
@@ -32,7 +34,7 @@ const AsyncPeople = asyncComponent(() => {
export class HomeRouter extends Component<IRouterProps, any> {
render () {
const { enabled, match, data, translate } = this.props
const St = AsyncStream as any
const St = AsyncStream
return (
enabled ? (
<Switch>
@@ -71,10 +73,10 @@ const mapDispatchToProps = (dispatch: any, ownProps: IRouterProps) => {
/**
* Map state to props
*/
const mapStateToProps = (state: any, ownProps: IRouterProps) => {
const mapStateToProps = (state: Map<string, any>, ownProps: IRouterProps) => {
return {
translate: getTranslate(state.locale),
currentLanguage: getActiveLanguage(state.locale).code,
translate: getTranslate(state.get('locale')),
currentLanguage: getActiveLanguage(state.get('locale')).code,
}
}

View File

@@ -5,33 +5,35 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
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 MasterLoadingComponent from 'components/masterLoading/MasterLoadingComponent'
// - Async Components
const AsyncHome: any = asyncComponent(() => {
return import('containers/home')
const AsyncHome: any = Loadable({
loader: () => import('containers/home'),
loading: MasterLoadingComponent,
})
const AsyncSignup = asyncComponent(() => {
return import('containers/signup')
const AsyncSignup = Loadable({
loader: () => import('containers/signup'),
loading: MasterLoadingComponent,
})
const AsyncEmailVerification = asyncComponent(() => {
return import('containers/emailVerification')
const AsyncEmailVerification = Loadable({
loader: () => import('containers/emailVerification'),
loading: MasterLoadingComponent,
})
const AsyncResetPassword = asyncComponent(() => {
return import('containers/resetPassword')
const AsyncResetPassword = Loadable({
loader: () => import('containers/resetPassword'),
loading: MasterLoadingComponent,
})
const AsyncLogin = asyncComponent(() => {
return import('containers/login')
const AsyncLogin = Loadable({
loader: () => import('containers/login'),
loading: MasterLoadingComponent,
})
const AsyncSetting = asyncComponent(() => {
return import('containers/setting')
const AsyncSetting = Loadable({
loader: () => import('containers/setting'),
loading: MasterLoadingComponent,
})
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
// - Import react components
import { provider } from 'src/socialEngine'
import { Map } from 'immutable'
// - Import domain
import { Profile } from 'src/core/domain/users'
import { SocialError } from 'src/core/domain/common'
@@ -25,7 +27,8 @@ const userService: IUserService = provider.get<IUserService>(SocialProviderTypes
*/
export const dbGetUserInfo = () => {
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) {
return userService.getUserProfile(uid).then((userProfile: Profile) => {
dispatch(addUserInfo(uid, {
@@ -54,7 +57,8 @@ export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
return (dispatch: Function, getState: Function) => {
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) {
return undefined
}
@@ -95,11 +99,10 @@ export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
*/
export const dbUpdateUserInfo = (newProfile: Profile) => {
return (dispatch: any, getState: Function) => {
console.trace('newProfile', newProfile)
// Get current user id
let uid: string = getState().authorize.uid
const state: Map<string, any> = getState()
let uid: string = state.getIn(['authorize', 'uid'])
let profile: Profile = getState().user.info[uid]
let profile: Profile = state.getIn(['user', 'info', uid])
let updatedProfile: Profile = {
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',
@@ -126,33 +129,27 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
// - Get people info from database
export const dbGetPeopleInfo = (page: number, limit: number) => {
return (dispatch: any, getState: Function) => {
const state = getState()
const {people} = state.user
const lastPageRequest = people.lastPageRequest
const lastUserId = people.lastUserId
const state: Map<string, any> = getState()
const people: Map<string, any> = state.getIn(['user', 'people'])
const lastPageRequest = people.get('lastPageRequest')
const lastUserId = people.get('lastUserId')
let uid: string = state.authorize.uid
let uid: string = state.getIn(['authorize', 'uid'])
if (uid && lastPageRequest !== page) {
return userService.getUsersProfile(uid, lastUserId, page, limit).then((result) => {
if (!result.users || !(result.users.length > 0)) {
return dispatch(notMoreDataPeople())
}
// Store last user Id
dispatch(lastUserPeople(result.newLastUserId))
let parsedData: {[userId: string]: Profile} = {}
result.users.forEach((post) => {
const userId = Object.keys(post)[0]
const postData = post[userId]
parsedData = {
...parsedData,
[userId]: {
...postData
}
}
let parsedData: Map<string, Profile> = Map({})
result.users.forEach((user) => {
const userId = Object.keys(user)[0]
const userData = user[userId]
parsedData = parsedData.set(userId, userData)
})
dispatch(addPeopleInfo(parsedData))
})
@@ -176,9 +173,8 @@ export const addUserInfo = (uid: string, info: Profile) => {
/**
* 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 {
type: UserActionType.ADD_PEOPLE_INFO,
payload: infoList

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ import { routerMiddleware } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory'
import createSagaMiddleware, { END } from 'redux-saga'
import { rootReducer } from 'store/reducers'
import { fromJS } from 'immutable'
// Create a history of your choosing (we're using a browser history in this case)
export const history = createHistory()
@@ -16,7 +17,7 @@ let initialState = {
}
// - 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)
))

View File

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

View File

@@ -1,4 +1,5 @@
import { Circle, UserTie } from 'src/core/domain/circles'
import {Map} from 'immutable'
/**
* Circle state
@@ -12,19 +13,19 @@ export class CircleState {
*
* @memberof CircleState
*/
userTies: { [userId: string]: UserTie } = {}
userTies: Map<string, UserTie> = Map({})
/**
* The list of users belong to users circle
*
* @memberof CircleState
*/
userTieds: { [userId: string]: UserTie } = {}
userTieds: Map<string, UserTie> = Map({})
/**
* 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
@@ -39,7 +40,7 @@ export class CircleState {
/**
* 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

View File

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

View File

@@ -1,6 +1,6 @@
import { Comment } from 'src/core/domain/comments'
import {Map} from 'immutable'
/**
* Comment state
*
@@ -11,22 +11,16 @@ export class CommentState {
/**
* 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
*/
editorStatus: {[postId: string]: {[commentId: string]: boolean}} = {}
editorStatus: Map<string, {[commentId: string]: boolean}> = Map({})
/**
* If the comments are loaded {true} or not {false}
*
* @type {Boolean}
* @memberof CommentState
*/
loaded: Boolean = false
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { Notification } from 'src/core/domain/notifications'
import {Map} from 'immutable'
/**
* Notification state
@@ -10,17 +11,11 @@ export class NotificationState {
/**
* 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}
*
* @type {Boolean}
* @memberof NotificationState
*/
loaded: Boolean = false
}

View File

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

View File

@@ -1,4 +1,5 @@
import { Post } from 'src/core/domain/posts'
import { Map, fromJS, List } from 'immutable'
/**
* Post state
@@ -14,7 +15,7 @@ export class PostState {
* @type {*}
* @memberof PostState
*/
userPosts: any = {}
userPosts = Map({})
/**
* If user posts are loaded {true} or not {false}
@@ -27,12 +28,12 @@ export class PostState {
/**
* Stream data storage
*/
stream?: {hasMoreData: boolean, lastPageRequest: number, lastPostId: string} =
{hasMoreData: true, lastPageRequest: -1, lastPostId: ''}
stream?: Map<string,any> =
Map({hasMoreData: true, lastPageRequest: -1, lastPostId: ''})
/**
* 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 _ from 'lodash'
import { Reducer, Action } from 'redux'
import { Map } from 'immutable'
// - Import action types
import { PostActionType } from 'constants/postActionType'
@@ -11,167 +12,94 @@ import { IPostAction } from './IPostAction'
import { Post } from 'src/core/domain/posts/post'
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
* @param {object} state
* @param {object} action
*/
export let postReducer = (state: PostState = new PostState(), action: IPostAction) => {
export let postReducer = (state = Map(new PostState()), action: IPostAction) => {
const { payload } = action
switch (action.type) {
case PostActionType.CLEAR_ALL_DATA_POST:
return new PostState()
return Map(new PostState())
case PostActionType.ADD_IMAGE_POST:
return {
...state,
userPosts: {
...state.userPosts,
[payload.uid]: {
...state.userPosts[payload.uid],
[payload.post.id]: { ...payload.post }
}
}
}
return state
.setIn(['userPosts', payload.uid, payload.post.id], Map(payload.post))
case PostActionType.ADD_POST:
return {
...state,
userPosts: {
...state.userPosts,
[payload.uid]: {
...state.userPosts[payload.uid],
[payload.post.id]: { ...payload.post }
}
}
}
return state
.setIn(['userPosts', payload.uid, payload.post.id], Map(payload.post))
case PostActionType.UPDATE_POST:
const post: Post = payload.post
return {
...state,
userPosts: {
...state.userPosts,
[post.ownerUserId!]: {
...state.userPosts[post.ownerUserId!],
[payload.post.id]: {
...payload.post,
comments: post.comments,
votes: post.votes
}
}
}
}
case PostActionType.UPDATE_POST: return updatePost(state, payload)
case PostActionType.UPDATE_POST_COMMENTS: return updatePostComments(state, payload)
case PostActionType.UPDATE_POST_VOTES: return updatePostVotes(state, payload)
case PostActionType.DELETE_POST:
let filteredPosts = {}
Object.keys(state.userPosts[payload.uid]).map((key) => {
if (key !== payload.id) {
return _.merge(filteredPosts, { [key]: { ...state.userPosts[payload.uid][key] } })
}
})
return {
...state,
userPosts: {
...state.userPosts,
[payload.uid]: {
...filteredPosts
}
}
}
return state
.deleteIn(['userPosts', payload.uid, payload.id])
case PostActionType.ADD_LIST_POST:
const newUserPosts = payload.userPosts as { [userId: string]: { [postId: string]: Post } }
const mergedObject = _.merge(state.userPosts, newUserPosts)
return {
...state,
userPosts: {
...mergedObject
},
loaded: true
return state
.mergeDeepIn(['userPosts'], payload.userPosts)
.set('loaded', true)
}
case PostActionType.HAS_MORE_DATA_STREAM:
return {
...state,
stream: {
...state.stream,
hasMoreData: true
}
return state
.setIn(['stream', 'hasMoreData'], true)
}
case PostActionType.NOT_MORE_DATA_STREAM:
return {
...state,
stream: {
...state.stream,
hasMoreData: false
}
}
return state
.setIn(['stream', 'hasMoreData'], false)
case PostActionType.REQUEST_PAGE_STREAM:
return {
...state,
stream: {
...state.stream,
lastPageRequest: payload.page
}
}
return state
.setIn(['stream', 'lastPageRequest'], payload.page)
case PostActionType.LAST_POST_STREAM:
return {
...state,
stream: {
...state.stream,
lastPostId: payload.lastPostId
}
}
return state
.setIn(['stream', 'lastPostId'], payload.lastPostId)
case PostActionType.HAS_MORE_DATA_PROFILE:
return {
...state,
profile: {
...state.profile,
hasMoreData: true
}
return state
.setIn(['profile', 'hasMoreData'], true)
}
case PostActionType.NOT_MORE_DATA_PROFILE:
return {
...state,
profile: {
...state.profile,
[payload.userId]: {
...state.profile![payload.userId],
hasMoreData: false
}
}
}
return state
.setIn(['profile', payload.userId, 'hasMoreData'], false)
case PostActionType.REQUEST_PAGE_PROFILE:
return {
...state,
profile: {
...state.profile,
[payload.userId]: {
...state.profile![payload.userId],
lastPageRequest: payload.page
}
}
}
return state
.setIn(['profile', payload.userId, 'lastPageRequest'], payload.page)
case PostActionType.LAST_POST_PROFILE:
return {
...state,
profile: {
...state.profile,
[payload.userId]: {
...state.profile![payload.userId],
lastPostId: payload.lastPostId
}
}
}
return state
.setIn(['profile', payload.userId, 'lastPostId'], payload.lastPostId)
default:
return state

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import { eventChannel, Channel } from 'redux-saga'
import { ServerRequestStatusType } from 'store/actions/serverRequestStatusType'
import { Post } from 'core/domain/posts'
import { postSelector } from 'store/reducers/posts/postSelector'
import {Map} from 'immutable'
/**
* Get service providers
*/
@@ -40,15 +41,15 @@ function* setComments(ownerId: string, postId: string, comments: postComments) {
*/
yield put(commentActions.addCommentList(comments))
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) {
const desiredComments = comments[postId]
if (desiredComments) {
commentsCount = Object.keys(desiredComments).length
let sortedObjects = yield CommentAPI.sortCommentsByDate(desiredComments)
post.comments = sortedObjects
post.commentCounter = commentsCount
yield put(postActions.updatePost(post))
const updatedPost = post.set('comments', Map(sortedObjects))
.set('commentCounter', commentsCount)
yield put(postActions.updatePost(updatedPost))
}
}
}
@@ -72,10 +73,8 @@ function* dbFetchComments(ownerId: string, postId: string) {
yield call(setComments, ownerId, postId, comments)
}
} finally {
console.trace('FiNALLY')
if (yield cancelled()) {
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:
"@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":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/react-infinite-scroller/-/react-infinite-scroller-1.0.6.tgz#ab36d95ce25712b70ccff439f2a3204bb15476d9"
@@ -211,6 +217,13 @@
"@types/react" "*"
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":
version "3.0.5"
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"
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:
version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@@ -3175,6 +3192,10 @@ ignore-walk@^3.0.1:
dependencies:
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:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
@@ -5846,7 +5867,7 @@ promzard@^0.3.0:
dependencies:
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"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
dependencies:
@@ -6133,6 +6154,15 @@ react-event-listener@^0.5.1:
prop-types "^15.6.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:
version "1.1.3"
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"
tlds "^1.57.0"
react-localize-redux@^2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/react-localize-redux/-/react-localize-redux-2.15.1.tgz#b32969eae191bcbcca2f0ffdc20802768fec0601"
react-loadable@^5.3.1:
version "5.3.1"
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:
flat "^2.0.1"
reselect "^3.0.1"
@@ -6290,6 +6326,13 @@ react-share@^2.0.0:
jsonp "^0.2.1"
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:
version "0.4.1"
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-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:
version "1.8.3"
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"
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:
version "3.0.6"
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"
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:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"