From 9cd267239541b907a622b9b443eac524504f421c Mon Sep 17 00:00:00 2001 From: Qolzam Date: Wed, 4 Apr 2018 10:33:15 +0700 Subject: [PATCH] [Enhancement] Apply immutable js. (#49) --- package.json | 11 +- src/api/CommentAPI.ts | 3 +- src/api/CommonAPI.ts | 14 +- src/api/PostAPI.ts | 13 + src/components/circle/CircleComponent.tsx | 46 +-- .../circle/ICircleComponentProps.ts | 36 +-- src/components/comment/CommentComponent.tsx | 20 +- .../comment/ICommentComponentProps.ts | 13 +- .../commentGroup/CommentGroupComponent.tsx | 51 ++-- .../ICommentGroupComponentProps.ts | 13 +- .../commentList/CommentListComponent.tsx | 21 +- .../commentList/ICommentListComponentProps.ts | 3 +- .../editProfile/EditProfileComponent.tsx | 20 +- .../findPeople/FindPeopleComponent.tsx | 16 +- .../findPeople/IFindPeopleComponentProps.ts | 6 +- .../followers/FollowersComponent.tsx | 18 +- .../followers/IFollowersComponentProps.ts | 6 +- .../following/FollowingComponent.tsx | 18 +- .../following/IFollowingComponentProps.ts | 3 +- .../homeHeader/HomeHeaderComponent.tsx | 21 +- .../IImageGalleryComponentProps.ts | 6 +- .../imageGallery/ImageGalleryComponent.tsx | 12 +- src/components/img/ImgComponent.tsx | 9 +- src/components/imgCover/ImgCoverComponent.tsx | 5 +- .../IMasterLoadingComponentProps.ts | 13 +- .../masterLoading/MasterLoadingComponent.tsx | 79 ++++- .../notify/INotifyComponentProps.ts | 20 +- src/components/notify/NotifyComponent.tsx | 30 +- src/components/post/IPostComponentProps.ts | 9 +- src/components/post/PostComponent.tsx | 87 +++--- .../postWrite/IPostWriteComponentProps.ts | 6 +- .../postWrite/PostWriteComponent.tsx | 99 +++--- .../profileHeader/ProfileHeaderComponent.tsx | 43 +-- .../ISendFeedbackComponentProps.ts | 3 +- .../sendFeedback/SendFeedbackComponent.tsx | 29 +- .../shareDialog/IShareDialogComponentProps.ts | 3 +- .../shareDialog/ShareDialogComponent.tsx | 28 +- .../userAvatar/UserAvatarComponent.tsx | 5 +- .../userBox/IUserBoxComponentProps.ts | 48 +-- src/components/userBox/UserBoxComponent.tsx | 109 +++---- .../userBoxList/IUserBoxListComponentProps.ts | 3 +- .../userBoxList/UserBoxListComponent.tsx | 14 +- .../yourCircles/IYourCirclesComponentProps.ts | 10 +- .../yourCircles/YourCirclesComponent.tsx | 13 +- src/constants/postActionType.ts | 2 + .../EmailVerificationComponent.tsx | 2 +- src/containers/home/HomeComponent.tsx | 40 +-- src/containers/login/LoginComponent.tsx | 6 +- src/containers/master/MasterComponent.tsx | 27 +- src/containers/people/PeopleComponent.tsx | 9 +- src/containers/postPage/PostPageComponent.tsx | 12 +- src/containers/profile/ProfileComponent.tsx | 27 +- .../resetPassword/ResetPasswordComponent.tsx | 2 +- src/containers/setting/SettingComponent.tsx | 2 +- src/containers/signup/SignupComponent.tsx | 2 +- .../stream/IStreamComponentProps.ts | 3 +- src/containers/stream/StreamComponent.tsx | 62 ++-- src/hoc/asyncComponent/asyncComponent.tsx | 24 -- src/routes/HomeRouter.tsx | 36 +-- src/routes/MasterRouter.tsx | 38 +-- src/routes/PrivateRoute.tsx | 7 +- src/routes/PublicRoute.tsx | 7 +- src/socialEngine.ts | 2 +- src/store/actions/circleActions.ts | 93 +++--- src/store/actions/commentActions.ts | 11 +- src/store/actions/globalActions.ts | 13 +- src/store/actions/imageGalleryActions.ts | 10 +- src/store/actions/notifyActions.ts | 26 +- src/store/actions/postActions.ts | 60 ++-- src/store/actions/serverRequestStatusType.ts | 1 + src/store/actions/userActions.ts | 44 ++- src/store/actions/voteActions.ts | 58 ++-- src/store/configureStore.dev.ts | 22 +- src/store/configureStore.prod.ts | 3 +- .../reducers/authorize/authorizeReducer.ts | 40 ++- .../reducers/authorize/authorizeSelector.ts | 6 +- src/store/reducers/circles/CircleState.ts | 9 +- src/store/reducers/circles/circleReducer.ts | 281 +++++------------- src/store/reducers/comments/CommentState.ts | 12 +- src/store/reducers/comments/commentReducer.ts | 99 ++---- src/store/reducers/global/GlobalState.ts | 14 +- src/store/reducers/global/globalReducer.ts | 140 ++++----- .../imageGallery/ImageGalleryState.ts | 21 +- .../imageGallery/imageGalleryReducer.ts | 51 ++-- .../notifications/NotificationState.ts | 9 +- .../notifications/notificationReducer.ts | 44 +-- src/store/reducers/posts/PostState.ts | 11 +- src/store/reducers/posts/postReducer.ts | 188 ++++-------- src/store/reducers/posts/postSelector.ts | 8 +- src/store/reducers/rootReducer.ts | 6 +- src/store/reducers/server/ServerState.ts | 3 +- src/store/reducers/server/serverReducer.ts | 54 +--- src/store/reducers/users/UserState.ts | 15 +- src/store/reducers/users/userReducer.ts | 106 ++----- src/store/reducers/votes/voteReducer.ts | 49 +-- src/store/sagas/commentSaga.ts | 11 +- src/typings/react-loadable.d.ts | 1 + yarn.lock | 63 +++- 98 files changed, 1294 insertions(+), 1633 deletions(-) delete mode 100644 src/hoc/asyncComponent/asyncComponent.tsx create mode 100644 src/typings/react-loadable.d.ts diff --git a/package.json b/package.json index 86dd701..d85b7f7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/api/CommentAPI.ts b/src/api/CommentAPI.ts index 3ce0af1..5f6eae0 100644 --- a/src/api/CommentAPI.ts +++ b/src/api/CommentAPI.ts @@ -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 { diff --git a/src/api/CommonAPI.ts b/src/api/CommonAPI.ts index 6f17cd4..06a3431 100644 --- a/src/api/CommonAPI.ts +++ b/src/api/CommonAPI.ts @@ -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 } diff --git a/src/api/PostAPI.ts b/src/api/PostAPI.ts index a6f9129..a8fc189 100644 --- a/src/api/PostAPI.ts +++ b/src/api/PostAPI.ts @@ -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>) => { + 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 +} diff --git a/src/components/circle/CircleComponent.tsx b/src/components/circle/CircleComponent.tsx index 849623d..848db48 100644 --- a/src/components/circle/CircleComponent.tsx +++ b/src/components/circle/CircleComponent.tsx @@ -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 { - const { fullName } = usersOfCircle[userId] - let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : '' + usersOfCircle.forEach((user: Map, userId) => { + const fullName = user.get('fullName') + let avatar = user.get('avatar', '') usersParsed.push( - {this.props.circle.name}} /> + {this.props.circle.get('name')}} /> - {circle.isSystem ? null : rightIconMenu} + {circle.get('isSystem') ? null : rightIconMenu} @@ -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, ownProps: ICircleComponentProps) => { + const userTies: Map = state.getIn(['circle', 'userTies']) + const uid = state.getIn(['authorize', 'uid']) + const circles: Map> = state.getIn(['circle', 'circleList'], {}) + const currentCircle: Map = circles.get(ownProps.id, Map({})) + const circleId = ownProps.circle.get('id') + let usersOfCircle: Map = Map({}) + userTies.forEach((userTie , userTieId) => { + const theUserTie: Map = userTie + const circleList: ImuList = 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']) } } diff --git a/src/components/circle/ICircleComponentProps.ts b/src/components/circle/ICircleComponentProps.ts index a200846..a480b90 100644 --- a/src/components/circle/ICircleComponentProps.ts +++ b/src/components/circle/ICircleComponentProps.ts @@ -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 /** * 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 /** * 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 diff --git a/src/components/comment/CommentComponent.tsx b/src/components/comment/CommentComponent.tsx index f884ff7..25184c7 100644 --- a/src/components/comment/CommentComponent.tsx +++ b/src/components/comment/CommentComponent.tsx @@ -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 * @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 } diff --git a/src/components/comment/ICommentComponentProps.ts b/src/components/comment/ICommentComponentProps.ts index 66b6ae0..9ed3d26 100644 --- a/src/components/comment/ICommentComponentProps.ts +++ b/src/components/comment/ICommentComponentProps.ts @@ -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 * diff --git a/src/components/commentGroup/CommentGroupComponent.tsx b/src/components/commentGroup/CommentGroupComponent.tsx index c18de07..db6578d 100644 --- a/src/components/commentGroup/CommentGroupComponent.tsx +++ b/src/components/commentGroup/CommentGroupComponent.tsx @@ -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 { - 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 { - 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 = (
@@ -274,8 +274,8 @@ export class CommentGroupComponent extends Component = this.props.comments || Map({}) /** * Comment list box */ @@ -314,20 +314,20 @@ export class CommentGroupComponent extends Component ) - const showComments = ( comments && Object.keys(comments).length > 0 + const showComments = ( !comments.isEmpty() ? ( ) : '') - const loadComments = (( getCommentsRequest === undefined || (getCommentsRequest && getCommentsRequest!.status !== ServerRequestStatusType.OK)) ? : showComments) + const loadComments = ((commentsRequestStatus === ServerRequestStatusType.OK) || !comments.isEmpty() ? showComments : ) /** * Return Elements */ return ( -
-
0 ? { display: 'block' } : { display: 'none' }}> +
+
@@ -339,11 +339,11 @@ export class CommentGroupComponent extends Component
+ +
{ open ? loadComments : '' } - -
{ (!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, 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']) } } diff --git a/src/components/commentGroup/ICommentGroupComponentProps.ts b/src/components/commentGroup/ICommentGroupComponentProps.ts index 8b74565..51ba287 100644 --- a/src/components/commentGroup/ICommentGroupComponentProps.ts +++ b/src/components/commentGroup/ICommentGroupComponentProps.ts @@ -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 /** * Commnets show on slide preview - * - * @type {{[commentId: string]: Comment}} - * @memberof ICommentGroupComponentProps */ - commentSlides?: {[commentId: string]: Comment} + commentSlides?: Map /** * 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 /** * 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 diff --git a/src/components/commentList/CommentListComponent.tsx b/src/components/commentList/CommentListComponent.tsx index 8452c0d..72aeebb 100644 --- a/src/components/commentList/CommentListComponent.tsx +++ b/src/components/commentList/CommentListComponent.tsx @@ -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 { - let {comments, commentsEditorStatus} = this.props - if (comments) { + let comments = Map(this.props.comments) + let commentsEditorStatus = Map(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 ) @@ -100,11 +102,11 @@ export class CommentListComponent extends Component + {this.commentList()} @@ -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, ownProps: ICommentListComponentProps) => { + const commentsEditorStatus = state.getIn(['comment', 'editorStatus', ownProps.postId ], {}) return { commentsEditorStatus } diff --git a/src/components/commentList/ICommentListComponentProps.ts b/src/components/commentList/ICommentListComponentProps.ts index ea416dd..8c3e15a 100644 --- a/src/components/commentList/ICommentListComponentProps.ts +++ b/src/components/commentList/ICommentListComponentProps.ts @@ -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 /** * Comments editor status diff --git a/src/components/editProfile/EditProfileComponent.tsx b/src/components/editProfile/EditProfileComponent.tsx index db93d1c..0e005ab 100644 --- a/src/components/editProfile/EditProfileComponent.tsx +++ b/src/components/editProfile/EditProfileComponent.tsx @@ -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 { 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 { +const mapStateToProps = (state: Map, 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']) } } diff --git a/src/components/findPeople/FindPeopleComponent.tsx b/src/components/findPeople/FindPeopleComponent.tsx index fb802cf..7e615db 100644 --- a/src/components/findPeople/FindPeopleComponent.tsx +++ b/src/components/findPeople/FindPeopleComponent.tsx @@ -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(this.props.peopleInfo!) return (
- {this.props.peopleInfo && Object.keys(this.props.peopleInfo).length !== 0 ? (
+ {peopleInfo && peopleInfo.count() > 0 ? (
{translate!('people.suggestionsForYouLabel')}
- +
) : (
{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 = state.getIn(['user', 'info']) return { - translate: getTranslate(state.locale), + translate: getTranslate(state.get('locale')), peopleInfo: info, - hasMorePeople: people.hasMoreData + hasMorePeople } } diff --git a/src/components/findPeople/IFindPeopleComponentProps.ts b/src/components/findPeople/IFindPeopleComponentProps.ts index a32697d..d32d628 100644 --- a/src/components/findPeople/IFindPeopleComponentProps.ts +++ b/src/components/findPeople/IFindPeopleComponentProps.ts @@ -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 /** * If there are more people {true} or not {false} diff --git a/src/components/followers/FollowersComponent.tsx b/src/components/followers/FollowersComponent.tsx index f01d275..e41971e 100644 --- a/src/components/followers/FollowersComponent.tsx +++ b/src/components/followers/FollowersComponent.tsx @@ -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 - {(this.props.followers && Object.keys(this.props.followers).length !== 0) ? (
+ {(followers && followers.keySeq().count() !== 0) ? (
{translate!('people.followersLabel')}
- +
) : (
@@ -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,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 } } diff --git a/src/components/followers/IFollowersComponentProps.ts b/src/components/followers/IFollowersComponentProps.ts index 3415c3b..f93c045 100644 --- a/src/components/followers/IFollowersComponentProps.ts +++ b/src/components/followers/IFollowersComponentProps.ts @@ -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 /** * Translate to locale string diff --git a/src/components/following/FollowingComponent.tsx b/src/components/following/FollowingComponent.tsx index d277029..87332fb 100644 --- a/src/components/following/FollowingComponent.tsx +++ b/src/components/following/FollowingComponent.tsx @@ -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 - {(this.props.followingUsers && Object.keys(this.props.followingUsers).length !== 0 ) ? (
+ {(followingUsers && followingUsers.keySeq().count() !== 0 ) ? (
{translate!('people.followingLabel')}
- +
) : (
@@ -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,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 diff --git a/src/components/following/IFollowingComponentProps.ts b/src/components/following/IFollowingComponentProps.ts index 54364e3..f4e9768 100644 --- a/src/components/following/IFollowingComponentProps.ts +++ b/src/components/following/IFollowingComponentProps.ts @@ -1,8 +1,9 @@ import { UserTie } from 'core/domain/circles' +import {Map} from 'immutable' export interface IFollowingComponentProps { - followingUsers?: {[userId: string]: UserTie} + followingUsers?: Map /** * Translate to locale string diff --git a/src/components/homeHeader/HomeHeaderComponent.tsx b/src/components/homeHeader/HomeHeaderComponent.tsx index 94dca36..52bb834 100644 --- a/src/components/homeHeader/HomeHeaderComponent.tsx +++ b/src/components/homeHeader/HomeHeaderComponent.tsx @@ -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, 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 = 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 } } diff --git a/src/components/imageGallery/IImageGalleryComponentProps.ts b/src/components/imageGallery/IImageGalleryComponentProps.ts index c260414..07b1d0b 100644 --- a/src/components/imageGallery/IImageGalleryComponentProps.ts +++ b/src/components/imageGallery/IImageGalleryComponentProps.ts @@ -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 /** * Styles diff --git a/src/components/imageGallery/ImageGalleryComponent.tsx b/src/components/imageGallery/ImageGalleryComponent.tsx index 7d90350..0604520 100644 --- a/src/components/imageGallery/ImageGalleryComponent.tsx +++ b/src/components/imageGallery/ImageGalleryComponent.tsx @@ -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 { - 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) => { + 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 : '' } } diff --git a/src/components/img/ImgComponent.tsx b/src/components/img/ImgComponent.tsx index 89b8106..068efa3 100644 --- a/src/components/img/ImgComponent.tsx +++ b/src/components/img/ImgComponent.tsx @@ -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, 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']) } } diff --git a/src/components/imgCover/ImgCoverComponent.tsx b/src/components/imgCover/ImgCoverComponent.tsx index c55cdd2..f696fae 100644 --- a/src/components/imgCover/ImgCoverComponent.tsx +++ b/src/components/imgCover/ImgCoverComponent.tsx @@ -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')) } } diff --git a/src/components/masterLoading/IMasterLoadingComponentProps.ts b/src/components/masterLoading/IMasterLoadingComponentProps.ts index 824d25e..8157f33 100644 --- a/src/components/masterLoading/IMasterLoadingComponentProps.ts +++ b/src/components/masterLoading/IMasterLoadingComponentProps.ts @@ -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 } diff --git a/src/components/masterLoading/MasterLoadingComponent.tsx b/src/components/masterLoading/MasterLoadingComponent.tsx index 191db6a..192026f 100644 --- a/src/components/masterLoading/MasterLoadingComponent.tsx +++ b/src/components/masterLoading/MasterLoadingComponent.tsx @@ -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 { // Constructor - constructor (props: IMasterLoadingComponentProps) { + constructor(props: IMasterLoadingComponentProps) { super(props) // Binding functions to `this` } + loadProgress() { + const { error, timedOut, pastDelay } = this.props + if (error) { + return ( + + + + + + + Unexpected Error Happened ... + + + + ) + } else if (timedOut) { + return ( + + + + + + + It takes long time ... + + + + ) + } else if (pastDelay) { + return ( + + + + + + + Loading... + + + + ) + } else { + return ( + + + + + + + Loading... + + + + ) + } + } + // Render app DOM component - render () { - const {activeLoading} = this.props + render() { return ( -
- +
+ { + this.loadProgress() + } +
) diff --git a/src/components/notify/INotifyComponentProps.ts b/src/components/notify/INotifyComponentProps.ts index 5d94dfe..681610e 100644 --- a/src/components/notify/INotifyComponentProps.ts +++ b/src/components/notify/INotifyComponentProps.ts @@ -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 /** * Users' profile - * - * @type {{[userId: string]: Profile}} - * @memberof INotifyComponentProps */ - info?: {[userId: string]: Profile} + info?: Map /** * 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 diff --git a/src/components/notify/NotifyComponent.tsx b/src/components/notify/NotifyComponent.tsx index 5ad260c..59533f4 100644 --- a/src/components/notify/NotifyComponent.tsx +++ b/src/components/notify/NotifyComponent.tsx @@ -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 { - let { notifications, info, onRequestClose } = this.props + let { info, onRequestClose } = this.props + let notifications: Map> = 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( @@ -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, ownProps: INotifyComponentProps) => { return { - notifications: state.notify.userNotifies, - info: state.user.info + notifications: state.getIn(['notify', 'userNotifies']), + info: state.getIn(['user', 'info']) } } diff --git a/src/components/post/IPostComponentProps.ts b/src/components/post/IPostComponentProps.ts index 6c2cd61..ea55ad0 100644 --- a/src/components/post/IPostComponentProps.ts +++ b/src/components/post/IPostComponentProps.ts @@ -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 /** * Owner's post avatar @@ -113,7 +110,7 @@ export interface IPostComponentProps { * @type {{[commentId: string]: Comment}} * @memberof ICommentGroupComponentProps */ - commentList?: {[commentId: string]: Comment} + commentList?: Map /** * Styles diff --git a/src/components/post/PostComponent.tsx b/src/components/post/PostComponent.tsx index e1542bd..949fb96 100644 --- a/src/components/post/PostComponent.tsx +++ b/src/components/post/PostComponent.tsx @@ -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 { 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 { 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 { 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 {translate!('post.edit')} {translate!('post.delete')} 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')} 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')} @@ -375,13 +378,23 @@ export class PostComponent extends Component ) - const { ownerUserId, ownerDisplayName, creationDate, image, body } = post + const { + ownerUserId, + ownerDisplayName, + creationDate, + image, + body, + id, + disableComments, + commentCounter, + disableSharing , + } = post.toJS() // Define variables return ( - + {ownerDisplayName}} - subheader={moment.unix(creationDate!).fromNow() + ' | ' + translate!('post.public')} + subheader={creationDate ? moment.unix(creationDate!).fromNow() + ' | ' + translate!('post.public') : } avatar={} action={isPostOwner ? rightIconMenu : ''} > @@ -426,16 +439,16 @@ export class PostComponent extends Component {this.props.voteCount! > 0 ? this.props.voteCount : ''}
- {!post.disableComments ? + {!disableComments ? (
-
{post.commentCounter! > 0 ? post.commentCounter : ''}
+
{commentCounter! > 0 ? commentCounter : ''}
) : ''} - {!post.disableSharing ? ( @@ -444,7 +457,7 @@ export class PostComponent extends Component - + { 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, 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') } } diff --git a/src/components/postWrite/IPostWriteComponentProps.ts b/src/components/postWrite/IPostWriteComponentProps.ts index c28be2b..ea4f8cb 100644 --- a/src/components/postWrite/IPostWriteComponentProps.ts +++ b/src/components/postWrite/IPostWriteComponentProps.ts @@ -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 /** * Save a post @@ -69,7 +69,7 @@ export interface IPostWriteComponentProps { * * @memberof IPostWriteComponentProps */ - update?: (post: Post, callback: Function) => any + update?: (post: Map, callback: Function) => any /** * Styles diff --git a/src/components/postWrite/PostWriteComponent.tsx b/src/components/postWrite/PostWriteComponent.tsx index 4641b52..7002eef 100644 --- a/src/components/postWrite/PostWriteComponent.tsx +++ b/src/components/postWrite/PostWriteComponent.tsx @@ -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 { return { post: (post: Post, callBack: Function) => dispatch(postActions.dbAddImagePost(post, callBack)), - update: (post: Post, callBack: Function) => dispatch(postActions.dbUpdatePost(post, callBack)) + update: (post: Map, 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, 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 || '' } } diff --git a/src/components/profileHeader/ProfileHeaderComponent.tsx b/src/components/profileHeader/ProfileHeaderComponent.tsx index 94aefe3..c9faf41 100644 --- a/src/components/profileHeader/ProfileHeaderComponent.tsx +++ b/src/components/profileHeader/ProfileHeaderComponent.tsx @@ -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 { - 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
{/* User avatar*/} -
+
{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, ownProps: IProfileHeaderComponentProps) => { return { - translate: getTranslate(state.locale), - editProfileOpen: state.user.openEditProfile + translate: getTranslate(state.get('locale')), + editProfileOpen: state.getIn(['user', 'openEditProfile']) } } diff --git a/src/components/sendFeedback/ISendFeedbackComponentProps.ts b/src/components/sendFeedback/ISendFeedbackComponentProps.ts index 78b1926..0ed5fa6 100644 --- a/src/components/sendFeedback/ISendFeedbackComponentProps.ts +++ b/src/components/sendFeedback/ISendFeedbackComponentProps.ts @@ -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 diff --git a/src/components/sendFeedback/SendFeedbackComponent.tsx b/src/components/sendFeedback/SendFeedbackComponent.tsx index a1e87ea..903c675 100644 --- a/src/components/sendFeedback/SendFeedbackComponent.tsx +++ b/src/components/sendFeedback/SendFeedbackComponent.tsx @@ -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 { - const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest, translate } = this.props + const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequestType, translate } = this.props const { feedText } = this.state return (
@@ -168,11 +169,11 @@ export class SendFeedbackComponent extends Component { - 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 { +const mapStateToProps = (state: Map, 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 } } diff --git a/src/components/shareDialog/IShareDialogComponentProps.ts b/src/components/shareDialog/IShareDialogComponentProps.ts index 98f1fa4..c2fee4d 100644 --- a/src/components/shareDialog/IShareDialogComponentProps.ts +++ b/src/components/shareDialog/IShareDialogComponentProps.ts @@ -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 /** * Styles diff --git a/src/components/shareDialog/ShareDialogComponent.tsx b/src/components/shareDialog/ShareDialogComponent.tsx index e34f9b8..c969df1 100644 --- a/src/components/shareDialog/ShareDialogComponent.tsx +++ b/src/components/shareDialog/ShareDialogComponent.tsx @@ -105,9 +105,9 @@ export class ShareDialogComponent extends Component + url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`} + quote={post.get('body')} + hashtag={`#${post.getIn(['tags', 0], '')}`}> + url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`} + quote={post.get('body')} + hashtag={`#${post.getIn(['tags', 0], '')}`}> + url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`} + quote={post.get('body')} + hashtag={`#${post.getIn(['tags', 0], '')}`}> + url={`${location.origin}/${post.get('ownerUserId')}/posts/${post.get('id')}`} + quote={post.get('body')} + hashtag={`#${post.getIn(['tags', 0], '')}`}> ) :
- + Link has been copied to clipboard ... @@ -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')) } } diff --git a/src/components/userAvatar/UserAvatarComponent.tsx b/src/components/userAvatar/UserAvatarComponent.tsx index b8a89fe..f0de70b 100644 --- a/src/components/userAvatar/UserAvatarComponent.tsx +++ b/src/components/userAvatar/UserAvatarComponent.tsx @@ -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']) } } diff --git a/src/components/userBox/IUserBoxComponentProps.ts b/src/components/userBox/IUserBoxComponentProps.ts index a2454db..0b56426 100644 --- a/src/components/userBox/IUserBoxComponentProps.ts +++ b/src/components/userBox/IUserBoxComponentProps.ts @@ -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> /** * List of circles' id - * - * @type {string[]} - * @memberof IUserBoxComponentProps */ - userBelongCircles?: string[] + userBelongCircles?: List /** * 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 /** * 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 /** * 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,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) => any /** * Remove current user selected circles for referer user */ - removeSelectedCircles?: (userId: string, circleList: string[]) => any + removeSelectedCircles?: (userId: string, circleList: List) => 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 /** * Whether the select circles box for referer user is open diff --git a/src/components/userBox/UserBoxComponent.tsx b/src/components/userBox/UserBoxComponent.tsx index 859ec50..b0644c8 100644 --- a/src/components/userBox/UserBoxComponent.tsx +++ b/src/components/userBox/UserBoxComponent.tsx @@ -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 { - 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 { // 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 { 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 { 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( - + this.handleSelectCircle(event, isInputChecked, circleId)} + onChange={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId!)} checked={isBelong} /> ) }) - return parsedDate + return circleDomList } } /** * Check if the the selected circles changed */ - selectedCircleChange = (selectedCircles: string[]) => { + selectedCircleChange = (selectedCircles: ImuList) => { 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 @@ -331,7 +335,7 @@ export class UserBoxComponent extends Component
this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{ cursor: 'pointer' }}>
- {this.props.user.fullName} + {this.props.fullName}
@@ -344,7 +348,7 @@ export class UserBoxComponent extends Component {!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')))}
@@ -414,7 +418,7 @@ export class UserBoxComponent extends Component { return { createCircle: (name: string) => dispatch(circleActions.dbAddCircle(name)), - addUserToCircle: (circleIds: string[], user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)), + addUserToCircle: (circleIds: ImuList, 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, 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> = state.getIn(['circle', 'circleList'], {}) + const userBelongCircles: ImuList = 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 || '' } } diff --git a/src/components/userBoxList/IUserBoxListComponentProps.ts b/src/components/userBoxList/IUserBoxListComponentProps.ts index c6e9864..972837e 100644 --- a/src/components/userBoxList/IUserBoxListComponentProps.ts +++ b/src/components/userBoxList/IUserBoxListComponentProps.ts @@ -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 /** * User identifier diff --git a/src/components/userBoxList/UserBoxListComponent.tsx b/src/components/userBoxList/UserBoxListComponent.tsx index 565eb2d..2fcef75 100644 --- a/src/components/userBoxList/UserBoxListComponent.tsx +++ b/src/components/userBoxList/UserBoxListComponent.tsx @@ -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 { - 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 + userBoxList.push() } }) } + 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 } diff --git a/src/components/yourCircles/IYourCirclesComponentProps.ts b/src/components/yourCircles/IYourCirclesComponentProps.ts index a5aaa29..5b3d407 100644 --- a/src/components/yourCircles/IYourCirclesComponentProps.ts +++ b/src/components/yourCircles/IYourCirclesComponentProps.ts @@ -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> /** * User identifier - * - * @type {string} - * @memberof IYourCirclesComponentProps */ uid?: string } diff --git a/src/components/yourCircles/YourCirclesComponent.tsx b/src/components/yourCircles/YourCirclesComponent.tsx index 9d7f457..6675b55 100644 --- a/src/components/yourCircles/YourCirclesComponent.tsx +++ b/src/components/yourCircles/YourCirclesComponent.tsx @@ -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 { - parsedCircles.push() + circles.map((circle, key) => { + parsedCircles.push() }) } 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, ownProps: IYourCirclesComponentProps) => { + const uid = state.getIn(['authorize', 'uid']) + const circles: Map> = state.getIn(['circle', 'circleList'], {}) return { uid, circles diff --git a/src/constants/postActionType.ts b/src/constants/postActionType.ts index 4748e4c..81f0f64 100644 --- a/src/constants/postActionType.ts +++ b/src/constants/postActionType.ts @@ -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' diff --git a/src/containers/emailVerification/EmailVerificationComponent.tsx b/src/containers/emailVerification/EmailVerificationComponent.tsx index 3a5b89c..67b0cbb 100644 --- a/src/containers/emailVerification/EmailVerificationComponent.tsx +++ b/src/containers/emailVerification/EmailVerificationComponent.tsx @@ -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')) } } diff --git a/src/containers/home/HomeComponent.tsx b/src/containers/home/HomeComponent.tsx index a3c2ca8..339fef1 100644 --- a/src/containers/home/HomeComponent.tsx +++ b/src/containers/home/HomeComponent.tsx @@ -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 { * @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, 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 = 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']) } } diff --git a/src/containers/login/LoginComponent.tsx b/src/containers/login/LoginComponent.tsx index fe59c83..4f1e406 100644 --- a/src/containers/login/LoginComponent.tsx +++ b/src/containers/login/LoginComponent.tsx @@ -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(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(LoginComponent as any) as any)) as typeof LoginComponent +export default withRouter(connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(localize(LoginComponent, 'locale', CommonAPI.getStateSlice) as any) as any)) as typeof LoginComponent diff --git a/src/containers/master/MasterComponent.tsx b/src/containers/master/MasterComponent.tsx index 19743ac..be19b49 100644 --- a/src/containers/master/MasterComponent.tsx +++ b/src/containers/master/MasterComponent.tsx @@ -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 { - 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 + + + React Social Network + + {sendFeedbackStatus ? : ''}
@@ -137,7 +135,7 @@ export class MasterComponent extends Component
Loading ...
- + {progress.visible ? : ''} { * 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) => { + 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 } diff --git a/src/containers/people/PeopleComponent.tsx b/src/containers/people/PeopleComponent.tsx index 1859b93..b268f9b 100644 --- a/src/containers/people/PeopleComponent.tsx +++ b/src/containers/people/PeopleComponent.tsx @@ -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, 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']) } } diff --git a/src/containers/postPage/PostPageComponent.tsx b/src/containers/postPage/PostPageComponent.tsx index 3c245dc..c1b4387 100644 --- a/src/containers/postPage/PostPageComponent.tsx +++ b/src/containers/postPage/PostPageComponent.tsx @@ -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,ownProps: IPostPageComponentProps) => { const {userId,postId} = ownProps.match.params + const userInfo = state.getIn(['state', 'user', 'info', userId]) + let posts: Map> = 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 } } diff --git a/src/containers/profile/ProfileComponent.tsx b/src/containers/profile/ProfileComponent.tsx index c915f0e..a918dc8 100644 --- a/src/containers/profile/ProfileComponent.tsx +++ b/src/containers/profile/ProfileComponent.tsx @@ -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
-
- {this.props.posts && Object.keys(this.props.posts).length !== 0 + {posts ? (
{translate!('profile.headPostsLabel', {userName: this.props.name})} @@ -92,7 +94,7 @@ export class ProfileComponent extends Component
@@ -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, 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, diff --git a/src/containers/resetPassword/ResetPasswordComponent.tsx b/src/containers/resetPassword/ResetPasswordComponent.tsx index 7dcb4df..0eb6a12 100644 --- a/src/containers/resetPassword/ResetPasswordComponent.tsx +++ b/src/containers/resetPassword/ResetPasswordComponent.tsx @@ -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')), } } diff --git a/src/containers/setting/SettingComponent.tsx b/src/containers/setting/SettingComponent.tsx index 8c28387..1d540ec 100644 --- a/src/containers/setting/SettingComponent.tsx +++ b/src/containers/setting/SettingComponent.tsx @@ -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')) } } diff --git a/src/containers/signup/SignupComponent.tsx b/src/containers/signup/SignupComponent.tsx index 8847985..950607d 100644 --- a/src/containers/signup/SignupComponent.tsx +++ b/src/containers/signup/SignupComponent.tsx @@ -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')), } } diff --git a/src/containers/stream/IStreamComponentProps.ts b/src/containers/stream/IStreamComponentProps.ts index 0e53df9..e398a78 100644 --- a/src/containers/stream/IStreamComponentProps.ts +++ b/src/containers/stream/IStreamComponentProps.ts @@ -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> /** * Router match property diff --git a/src/containers/stream/StreamComponent.tsx b/src/containers/stream/StreamComponent.tsx index bb3565f..b857106 100644 --- a/src/containers/stream/StreamComponent.tsx +++ b/src/containers/stream/StreamComponent.tsx @@ -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 { - let { posts, match } = this.props + let { match } = this.props + let posts: Map> = this.props.posts let { tag } = match.params - if (posts === undefined || !(Object.keys(posts).length > 0)) { - + if (posts === undefined || !(posts.keySeq().count() > 0)) { + return ( - +

'Nothing has shared.'

- ) - } 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 = ImuList() + posts.forEach((post: Map) => { 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 = ( -
- +
+ {index > 1 || (!postBack.divided && index > 0) ?
: ''} - +
) - + 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, 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 diff --git a/src/hoc/asyncComponent/asyncComponent.tsx b/src/hoc/asyncComponent/asyncComponent.tsx deleted file mode 100644 index 3714bc6..0000000 --- a/src/hoc/asyncComponent/asyncComponent.tsx +++ /dev/null @@ -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 ? : null - } - } -} - -export default asyncComponent \ No newline at end of file diff --git a/src/routes/HomeRouter.tsx b/src/routes/HomeRouter.tsx index c9b24fb..2ff2ca0 100644 --- a/src/routes/HomeRouter.tsx +++ b/src/routes/HomeRouter.tsx @@ -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 { render () { const { enabled, match, data, translate } = this.props - const St = AsyncStream as any + const St = AsyncStream return ( enabled ? ( @@ -71,10 +73,10 @@ const mapDispatchToProps = (dispatch: any, ownProps: IRouterProps) => { /** * Map state to props */ -const mapStateToProps = (state: any, ownProps: IRouterProps) => { +const mapStateToProps = (state: Map, ownProps: IRouterProps) => { return { - translate: getTranslate(state.locale), - currentLanguage: getActiveLanguage(state.locale).code, + translate: getTranslate(state.get('locale')), + currentLanguage: getActiveLanguage(state.get('locale')).code, } } diff --git a/src/routes/MasterRouter.tsx b/src/routes/MasterRouter.tsx index f820200..69acf56 100644 --- a/src/routes/MasterRouter.tsx +++ b/src/routes/MasterRouter.tsx @@ -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, }) /** diff --git a/src/routes/PrivateRoute.tsx b/src/routes/PrivateRoute.tsx index 794a2a8..721db4a 100644 --- a/src/routes/PrivateRoute.tsx +++ b/src/routes/PrivateRoute.tsx @@ -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 { @@ -19,10 +20,10 @@ export class PrivateRoute extends Component { } } -const mapStateToProps = (state: any, nexProps: IRoute) => { - const { authorize } = state +const mapStateToProps = (state: Map, nexProps: IRoute) => { + return { - authed: authorize.authed + authed: state.getIn(['authorize', 'authed']) } } diff --git a/src/routes/PublicRoute.tsx b/src/routes/PublicRoute.tsx index 134f7c7..54240f1 100644 --- a/src/routes/PublicRoute.tsx +++ b/src/routes/PublicRoute.tsx @@ -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 { @@ -19,10 +20,10 @@ export class PublicRoute extends Component { } } -const mapStateToProps = (state: any, nexProps: IRoute) => { - const { authorize } = state +const mapStateToProps = (state: Map, nexProps: IRoute) => { + return { - authed: authorize.authed + authed: state.getIn(['authorize', 'authed', false]) } } diff --git a/src/socialEngine.ts b/src/socialEngine.ts index cdd6f1d..a4432a7 100644 --- a/src/socialEngine.ts +++ b/src/socialEngine.ts @@ -7,7 +7,7 @@ import CommonAPI from 'api/CommonAPI' /** * Developer tools */ -console.trace = CommonAPI.logger +window['console']['trace'] = CommonAPI.logger /** * Initialize container diff --git a/src/store/actions/circleActions.ts b/src/store/actions/circleActions.ts index 19d6543..70af405 100644 --- a/src/store/actions/circleActions.ts +++ b/src/store/actions/circleActions.ts @@ -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(SocialProv export let dbAddCircle = (circleName: string) => { return (dispatch: any, getState: Function) => { - let uid: string = getState().authorize.uid + const state: Map = 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 = 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 = 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, 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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) => { 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) => { 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) => { return { type: CircleActionType.ADD_FOLLOWING_USER, payload: { userTie } diff --git a/src/store/actions/commentActions.ts b/src/store/actions/commentActions.ts index 09776f8..31166ee 100644 --- a/src/store/actions/commentActions.ts +++ b/src/store/actions/commentActions.ts @@ -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 = 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 diff --git a/src/store/actions/globalActions.ts b/src/store/actions/globalActions.ts index 0da52fa..c24b14c 100644 --- a/src/store/actions/globalActions.ts +++ b/src/store/actions/globalActions.ts @@ -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(SocialProvide */ export let dbSendFeed = (newFeed: Feed) => { return (dispatch: any, getState: Function) => { - - let uid: string = getState().authorize.uid + const state: Map = 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 = 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 = getState() + const translate = getTranslate(state.get('locale')) return dispatch(showMessage(String(translate('common.successfulRequestMessage')))) } } diff --git a/src/store/actions/imageGalleryActions.ts b/src/store/actions/imageGalleryActions.ts index f228a95..f56e5ff 100644 --- a/src/store/actions/imageGalleryActions.ts +++ b/src/store/actions/imageGalleryActions.ts @@ -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 { return (dispatch: any, getState: Function) => { - let uid: string = getState().authorize.uid + const state: Map = 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 = 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 = getState() + let uid: string = state.getIn(['authorize', 'uid']) return imageGalleryService.deleteImage(uid,id) .then(() => { diff --git a/src/store/actions/notifyActions.ts b/src/store/actions/notifyActions.ts index 23dc5a2..b3394cc 100644 --- a/src/store/actions/notifyActions.ts +++ b/src/store/actions/notifyActions.ts @@ -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 { 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 = 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 = 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 = getState() + let uid: string = state.getIn(['authorize', 'uid']) + let notify: Map = 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) => { return { type: NotificationActionType.ADD_NOTIFY_LIST, diff --git a/src/store/actions/postActions.ts b/src/store/actions/postActions.ts index c7e88e7..0daad0d 100644 --- a/src/store/actions/postActions.ts +++ b/src/store/actions/postActions.ts @@ -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(SocialProviderTypes */ export let dbAddPost = (newPost: Post, callBack: Function) => { return (dispatch: any, getState: Function) => { - - let uid: string = getState().authorize.uid + const state: Map = 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 = 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, 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 = 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 = getState() + const stream: Map = 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 = 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) => { return { type: PostActionType.UPDATE_POST, payload: { post } } } +/** + * Update the comments of post + */ +export const updatePostComments = (comments: Map) => { + return { + type: PostActionType.UPDATE_POST, + payload: comments + } +} + +/** + * Update the votes of post + */ +export const updatePostVotes = (votes: Map) => { + return { + type: PostActionType.UPDATE_POST, + payload: votes + } +} + /** * Delete a post */ diff --git a/src/store/actions/serverRequestStatusType.ts b/src/store/actions/serverRequestStatusType.ts index bb6a672..9bd1c41 100644 --- a/src/store/actions/serverRequestStatusType.ts +++ b/src/store/actions/serverRequestStatusType.ts @@ -1,5 +1,6 @@ export enum ServerRequestStatusType { Sent = 'Sent', + NoAction = 'NoAction', OK = 'OK', Error = 'Error' } diff --git a/src/store/actions/userActions.ts b/src/store/actions/userActions.ts index 8b143e5..d967a41 100644 --- a/src/store/actions/userActions.ts +++ b/src/store/actions/userActions.ts @@ -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(SocialProviderTypes */ export const dbGetUserInfo = () => { return (dispatch: any, getState: Function) => { - let uid: string = getState().authorize.uid + const state: Map = 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 = 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 = 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 = getState() + const people: Map = 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 = 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) => { return { type: UserActionType.ADD_PEOPLE_INFO, payload: infoList diff --git a/src/store/actions/voteActions.ts b/src/store/actions/voteActions.ts index 881c39d..7bea044 100644 --- a/src/store/actions/voteActions.ts +++ b/src/store/actions/voteActions.ts @@ -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(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 = 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 = 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 = 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 = getState() + let uid: string = state.getIn(['authorize', 'uid']) + const post: Map = 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)) }) } diff --git a/src/store/configureStore.dev.ts b/src/store/configureStore.dev.ts index 29153a3..90ca101 100644 --- a/src/store/configureStore.dev.ts +++ b/src/store/configureStore.dev.ts @@ -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) => { + + 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 = redux.createStore(rootReducer, initialState, redux.compose( - redux.applyMiddleware(logger,thunk, routerMiddleware(history), sagaMiddleware), - DevTools.instrument() +const composeEnhancers = composeWithDevTools({ + // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize... + }) +let store: Store = createStore(rootReducer, fromJS(initialState), composeEnhancers( + applyMiddleware(logger,thunk, routerMiddleware(history), sagaMiddleware) )) export default {store, runSaga: sagaMiddleware.run, close: () => store.dispatch(END), history} diff --git a/src/store/configureStore.prod.ts b/src/store/configureStore.prod.ts index 71b519e..ce52160 100644 --- a/src/store/configureStore.prod.ts +++ b/src/store/configureStore.prod.ts @@ -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 = redux.createStore(rootReducer, initialState, redux.compose( +let store: redux.Store = redux.createStore(rootReducer, fromJS(initialState), redux.compose( redux.applyMiddleware(thunk, routerMiddleware(history), sagaMiddleware) )) diff --git a/src/store/reducers/authorize/authorizeReducer.ts b/src/store/reducers/authorize/authorizeReducer.ts index 3263ba9..4e68044 100644 --- a/src/store/reducers/authorize/authorizeReducer.ts +++ b/src/store/reducers/authorize/authorizeReducer.ts @@ -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 diff --git a/src/store/reducers/authorize/authorizeSelector.ts b/src/store/reducers/authorize/authorizeSelector.ts index d12125c..f94f741 100644 --- a/src/store/reducers/authorize/authorizeSelector.ts +++ b/src/store/reducers/authorize/authorizeSelector.ts @@ -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) => { + const uid = state.getIn(['authorize', 'uid']) + return state.getIn(['user', 'info', uid]) +} export const authorizeSelector = { getCurrentUser diff --git a/src/store/reducers/circles/CircleState.ts b/src/store/reducers/circles/CircleState.ts index 10adbd3..886f1de 100644 --- a/src/store/reducers/circles/CircleState.ts +++ b/src/store/reducers/circles/CircleState.ts @@ -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 = Map({}) /** * The list of users belong to users circle * * @memberof CircleState */ - userTieds: { [userId: string]: UserTie } = {} + userTieds: Map = Map({}) /** * The list of circle of current user */ - circleList: { [circleId: string]: Circle } + circleList: Map = 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 = Map({}) /** * Whether the select circles box for referer user is open diff --git a/src/store/reducers/circles/circleReducer.ts b/src/store/reducers/circles/circleReducer.ts index 25822f2..36d3f71 100644 --- a/src/store/reducers/circles/circleReducer.ts +++ b/src/store/reducers/circles/circleReducer.ts @@ -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 = payload.circle + return state + .setIn(['circleList', circle.get('id')], payload.circle) +} + +/** + * Update circle + */ +const updateCircle = (state: any , payload: any) => { + const circle: Map = 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 = 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> = 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 diff --git a/src/store/reducers/comments/CommentState.ts b/src/store/reducers/comments/CommentState.ts index 08dd4e2..155d096 100644 --- a/src/store/reducers/comments/CommentState.ts +++ b/src/store/reducers/comments/CommentState.ts @@ -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 = Map({}) /** * Whether comment editor is open */ - editorStatus: {[postId: string]: {[commentId: string]: boolean}} = {} + editorStatus: Map = Map({}) /** * If the comments are loaded {true} or not {false} - * - * @type {Boolean} - * @memberof CommentState */ loaded: Boolean = false } diff --git a/src/store/reducers/comments/commentReducer.ts b/src/store/reducers/comments/commentReducer.ts index aeaa2d4..9328ba2 100644 --- a/src/store/reducers/comments/commentReducer.ts +++ b/src/store/reducers/comments/commentReducer.ts @@ -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 diff --git a/src/store/reducers/global/GlobalState.ts b/src/store/reducers/global/GlobalState.ts index bab07e1..89262d0 100644 --- a/src/store/reducers/global/GlobalState.ts +++ b/src/store/reducers/global/GlobalState.ts @@ -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() + }) } diff --git a/src/store/reducers/global/globalReducer.ts b/src/store/reducers/global/globalReducer.ts index 75de554..facd8d5 100644 --- a/src/store/reducers/global/globalReducer.ts +++ b/src/store/reducers/global/globalReducer.ts @@ -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 diff --git a/src/store/reducers/imageGallery/ImageGalleryState.ts b/src/store/reducers/imageGallery/ImageGalleryState.ts index c04f46f..cab3069 100644 --- a/src/store/reducers/imageGallery/ImageGalleryState.ts +++ b/src/store/reducers/imageGallery/ImageGalleryState.ts @@ -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 = 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 = {} diff --git a/src/store/reducers/imageGallery/imageGalleryReducer.ts b/src/store/reducers/imageGallery/imageGalleryReducer.ts index 21bdc39..7dd2449 100644 --- a/src/store/reducers/imageGallery/imageGalleryReducer.ts +++ b/src/store/reducers/imageGallery/imageGalleryReducer.ts @@ -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) => { + 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 diff --git a/src/store/reducers/notifications/NotificationState.ts b/src/store/reducers/notifications/NotificationState.ts index 018faf8..032db1d 100644 --- a/src/store/reducers/notifications/NotificationState.ts +++ b/src/store/reducers/notifications/NotificationState.ts @@ -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> = Map({}) /** * If user notifications are loaded {true} or not {false} - * - * @type {Boolean} - * @memberof NotificationState */ loaded: Boolean = false } \ No newline at end of file diff --git a/src/store/reducers/notifications/notificationReducer.ts b/src/store/reducers/notifications/notificationReducer.ts index 24b88ea..66a2710 100644 --- a/src/store/reducers/notifications/notificationReducer.ts +++ b/src/store/reducers/notifications/notificationReducer.ts @@ -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 diff --git a/src/store/reducers/posts/PostState.ts b/src/store/reducers/posts/PostState.ts index a97927b..502f322 100644 --- a/src/store/reducers/posts/PostState.ts +++ b/src/store/reducers/posts/PostState.ts @@ -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 = + Map({hasMoreData: true, lastPageRequest: -1, lastPostId: ''}) /** * Profile posts data storage */ - profile?: {[userId: string]: {hasMoreData: boolean, lastPageRequest: number, lastPostId: string}} = - {} + profile?: Map = + Map({}) } diff --git a/src/store/reducers/posts/postReducer.ts b/src/store/reducers/posts/postReducer.ts index 0e3b692..8c91967 100644 --- a/src/store/reducers/posts/postReducer.ts +++ b/src/store/reducers/posts/postReducer.ts @@ -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 = 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 = 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 = 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 diff --git a/src/store/reducers/posts/postSelector.ts b/src/store/reducers/posts/postSelector.ts index 2c243e6..5a396f3 100644 --- a/src/store/reducers/posts/postSelector.ts +++ b/src/store/reducers/posts/postSelector.ts @@ -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, userId: string, postId: string) => { + return state.getIn(['post', 'userPosts', userId, postId]) } export const postSelector = { diff --git a/src/store/reducers/rootReducer.ts b/src/store/reducers/rootReducer.ts index 89f2e34..a2bc6a9 100644 --- a/src/store/reducers/rootReducer.ts +++ b/src/store/reducers/rootReducer.ts @@ -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, diff --git a/src/store/reducers/server/ServerState.ts b/src/store/reducers/server/ServerState.ts index bfbff02..cc7394c 100644 --- a/src/store/reducers/server/ServerState.ts +++ b/src/store/reducers/server/ServerState.ts @@ -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 = Map({}) } diff --git a/src/store/reducers/server/serverReducer.ts b/src/store/reducers/server/serverReducer.ts index d069cdd..f83f0e7 100644 --- a/src/store/reducers/server/serverReducer.ts +++ b/src/store/reducers/server/serverReducer.ts @@ -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 diff --git a/src/store/reducers/users/UserState.ts b/src/store/reducers/users/UserState.ts index b8cf11a..c40497d 100644 --- a/src/store/reducers/users/UserState.ts +++ b/src/store/reducers/users/UserState.ts @@ -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 = 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 = Map({hasMoreData: true, lastPageRequest: -1, lastUserId: ''}) } diff --git a/src/store/reducers/users/userReducer.ts b/src/store/reducers/users/userReducer.ts index bb61aad..503275e 100644 --- a/src/store/reducers/users/userReducer.ts +++ b/src/store/reducers/users/userReducer.ts @@ -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 diff --git a/src/store/reducers/votes/voteReducer.ts b/src/store/reducers/votes/voteReducer.ts index 8864424..522d0a6 100644 --- a/src/store/reducers/votes/voteReducer.ts +++ b/src/store/reducers/votes/voteReducer.ts @@ -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 diff --git a/src/store/sagas/commentSaga.ts b/src/store/sagas/commentSaga.ts index 6e67bc8..b84fafc 100644 --- a/src/store/sagas/commentSaga.ts +++ b/src/store/sagas/commentSaga.ts @@ -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 = 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') } } diff --git a/src/typings/react-loadable.d.ts b/src/typings/react-loadable.d.ts new file mode 100644 index 0000000..0eff9bf --- /dev/null +++ b/src/typings/react-loadable.d.ts @@ -0,0 +1 @@ +declare module 'react-loadable' \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 09eaa5a..bade5aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"