[Improvement] Move get comments action to saga (#48)

This commit is contained in:
Qolzam
2018-03-24 22:06:15 +07:00
parent c88326533f
commit 4c40069c7d
17 changed files with 233 additions and 76 deletions

View File

@@ -98,5 +98,29 @@
"engines": {
"node": "8.9.4",
"npm": "5.6.0"
}
},
"directories": {
"doc": "docs"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Qolzam/react-social-network.git"
},
"keywords": [
"react",
"redux",
"saga",
"redux-saga",
"router",
"react-router",
"firebase",
"social",
"media",
"app",
"mobile"
],
"bugs": {
"url": "https://github.com/Qolzam/react-social-network/issues"
},
"homepage": "https://github.com/Qolzam/react-social-network#readme"
}

View File

@@ -23,6 +23,7 @@ import StringAPI from 'api/StringAPI'
import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestModel } from 'models/server'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import CommentAPI from 'api/CommentAPI'
/**
* Get service providers
@@ -43,18 +44,18 @@ export const dbAddComment = (ownerPostUserId: string, newComment: Comment, callB
let uid: string = state.authorize.uid
let comment: Comment = {
score : 0,
creationDate : moment().unix(),
userDisplayName : state.user.info[uid].fullName,
userAvatar : state.user.info[uid].avatar,
userId : uid,
score: 0,
creationDate: moment().unix(),
userDisplayName: state.user.info[uid].fullName,
userAvatar: state.user.info[uid].avatar,
userId: uid,
postId: newComment.postId,
text: newComment.text
}
return commentService.addComment(comment)
.then((commentKey: string) => {
dispatch(addComment({id: commentKey! ,...comment}))
dispatch(addComment({ id: commentKey!, ...comment }))
callBack()
dispatch(globalActions.hideTopLoading())
@@ -79,49 +80,10 @@ export const dbAddComment = (ownerPostUserId: string, newComment: Comment, callB
/**
* Get all comments from database
*/
export const dbGetComments = (ownerUserId: string, postId: string) => {
return (dispatch: any, getState: Function) => {
const state = getState()
let uid: string = getState().authorize.uid
if (uid) {
// Set server request status to {Sent}
const getCommentsRequest = createGetCommentsRequest(postId)
dispatch(serverActions.sendRequest(getCommentsRequest))
return commentService.getComments(postId, (comments: {[postId: string]: {[commentId: string]: Comment}}) => {
// Set server request status to {OK}
getCommentsRequest.status = ServerRequestStatusType.OK
dispatch(serverActions.sendRequest(getCommentsRequest))
/**
* Workout getting the number of post's comment and getting three last comments
*/
dispatch(addCommentList(comments))
let commentsCount: number
const post: Post = state.post.userPosts[ownerUserId][postId]
if (!post) {
return
}
const desiredComments = comments[postId]
if (desiredComments) {
commentsCount = Object.keys(desiredComments).length
let sortedObjects = desiredComments as any
// Sort posts with creation date
const commentKeys = Object.keys(sortedObjects)
if (commentKeys.length > 1) {
sortedObjects = _.fromPairs(_.toPairs(sortedObjects)
.sort((a: any, b: any) => parseInt(b[1].creationDate,10) - parseInt(a[1].creationDate,10)).slice(0, 3))
}
post.comments = sortedObjects
post.commentCounter = commentsCount
dispatch(postActions.updatePost(post))
}
})
}
export const dbFetchComments = (ownerUserId: string, postId: string) => {
return {
type: CommentActionType.DB_FETCH_COMMENTS,
payload: {postId, ownerUserId}
}
}
@@ -134,7 +96,7 @@ export const dbUpdateComment = (comment: Comment) => {
return commentService.updateComment(comment)
.then(() => {
dispatch(updateComment( comment))
dispatch(updateComment(comment))
dispatch(closeCommentEditor(comment))
dispatch(globalActions.hideTopLoading())
@@ -171,19 +133,6 @@ export const dbDeleteComment = (id?: string | null, postId?: string) => {
}
/**
* Create get comments server request model
*/
const createGetCommentsRequest = (postId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CommentGetComments, postId)
return new ServerRequestModel(
ServerRequestType.CommentGetComments,
requestId,
'',
ServerRequestStatusType.Sent
)
}
/* _____________ CRUD State _____________ */
/**
@@ -201,7 +150,7 @@ export const addComment = (comment: Comment) => {
/**
* Update comment
*/
export const updateComment = ( comment: Comment) => {
export const updateComment = (comment: Comment) => {
return {
type: CommentActionType.UPDATE_COMMENT,
@@ -213,7 +162,7 @@ export const updateComment = ( comment: Comment) => {
* Add comment list
* @param {[postId: string]: {[commentId: string] : Comment}} postComments an array of comments
*/
export const addCommentList = (postComments: {[postId: string]: {[commentId: string]: Comment}}) => {
export const addCommentList = (postComments: { [postId: string]: { [commentId: string]: Comment } }) => {
return {
type: CommentActionType.ADD_COMMENT_LIST,

View File

@@ -8,6 +8,7 @@ import * as postActions from './postActions'
import * as userActions from './userActions'
import * as voteActions from './voteActions'
import * as localeActions from './localeActions'
import * as serverActions from './serverActions'
export {
authorizeActions,
@@ -19,5 +20,6 @@ export {
postActions,
userActions,
voteActions,
localeActions
localeActions,
serverActions
}

33
src/api/CommentAPI.ts Normal file
View File

@@ -0,0 +1,33 @@
import StringAPI from 'api/StringAPI'
import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestModel } from 'models/server'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { comments } from 'models/comments/commentTypes'
import * as _ from 'lodash'
/**
* Create get comments server request model
*/
const createGetCommentsRequest = (postId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CommentGetComments, postId)
return new ServerRequestModel(
ServerRequestType.CommentGetComments,
requestId,
'',
ServerRequestStatusType.Sent
)
}
const sortCommentsByDate = (sortedObjects: comments) => {
const commentKeys = Object.keys(sortedObjects)
if (commentKeys.length > 1) {
return _.fromPairs(_.toPairs(sortedObjects)
.sort((a: any, b: any) => parseInt(b[1].creationDate, 10) - parseInt(a[1].creationDate, 10)).slice(0, 3))
}
}
export default {
createGetCommentsRequest,
sortCommentsByDate
}

View File

@@ -24,7 +24,15 @@ const getRandomColor = () => {
return color
}
const updateObject = (oldObject: any, updatedProperties: any) => {
return {
...oldObject,
...updatedProperties
}
}
export default {
logger,
getRandomColor
getRandomColor,
updateObject
}

View File

@@ -490,7 +490,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
},
goTo: (url: string) => dispatch(push(url)),
setHomeTitle: (title: string) => dispatch(globalActions.setHeaderTitle(title || '')),
getPostComments: (ownerUserId: string, postId: string) => dispatch(commentActions.dbGetComments(ownerUserId, postId))
getPostComments: (ownerUserId: string, postId: string) => dispatch(commentActions.dbFetchComments(ownerUserId, postId))
}
}

View File

@@ -7,4 +7,5 @@ export enum CommentActionType {
UPDATE_COMMENT = 'UPDATE_COMMENT',
CLOSE_COMMENT_EDITOR = 'CLOSE_COMMENT_EDITOR',
OPEN_COMMENT_EDITOR = 'OPEN_COMMENT_EDITOR',
DB_FETCH_COMMENTS = 'DB_FETCH_COMMENTS',
}

View File

@@ -1,6 +1,7 @@
import { User } from 'core/domain/users'
import { Comment } from 'core/domain/comments'
import { postComments } from 'models/comments/commentTypes'
/**
* Comment service interface
@@ -11,7 +12,7 @@ import { Comment } from 'core/domain/comments'
export interface ICommentService {
addComment: (comment: Comment) => Promise<string>
getComments: (postId: string, next: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void) => void
getComments: (postId: string, next: (resultComments: postComments) => void) => () => void
updateComment: (comment: Comment) => Promise<void>
deleteComment: (commentId: string) => Promise<void>

View File

@@ -6,6 +6,7 @@ import { SocialError } from 'core/domain/common'
import { ICommentService } from 'core/services/comments'
import { Comment } from 'core/domain/comments'
import { injectable } from 'inversify'
import { postComments } from 'models/comments/commentTypes'
/**
* Firbase comment service
@@ -40,10 +41,10 @@ export class CommentService implements ICommentService {
*
* @memberof CommentService
*/
public getComments: (postId: string, next: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void)
=> void = (postId, next) => {
public getComments: (postId: string, next: (resultComments: postComments) => void)
=> () => void = (postId, next) => {
let commentsRef = db.collection(`comments`).where('postId', '==', postId)
commentsRef.onSnapshot((snapshot) => {
const unsubscribe = commentsRef.onSnapshot((snapshot) => {
let parsedData: {[postId: string]: {[commentId: string]: Comment}} = {[postId]: {}}
snapshot.forEach((result) => {
parsedData[postId][result.id] = {
@@ -55,6 +56,7 @@ export class CommentService implements ICommentService {
next(parsedData)
}
})
return unsubscribe
}
/**

View File

@@ -44,6 +44,9 @@ import './styles/app.css'
* Execute startup functions
*/
import './socialEngine'
import rootSaga from 'sagas/rootSaga'
configureStore.runSaga(rootSaga)
const supportsHistory = 'pushState' in window.history
ReactDOM.render(

View File

@@ -0,0 +1,3 @@
import {Comment} from 'core/domain/comments'
export type postComments = {[postId: string]: {[commentId: string]: Comment}}
export type comments = {[commentId: string]: Comment}

View File

@@ -0,0 +1,5 @@
const getCurrentUser = (state: any) => (state.user.info && state.authorize.uid) ? state.user.info[state.authorize.uid] : null
export const authorizeSelector = {
getCurrentUser
}

View File

@@ -1,3 +1,6 @@
import { authorizeReducer } from './authorizeReducer'
export {authorizeReducer}
import {authorizeSelector} from './authorizeSelector'
export {
authorizeReducer,
authorizeSelector
}

View File

@@ -1,3 +1,7 @@
import { postReducer } from './postReducer'
import { postSelector } from './postSelector'
export {postReducer}
export {
postReducer,
postSelector
}

View File

@@ -0,0 +1,9 @@
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
}
export const postSelector = {
getPost
}

101
src/sagas/commentSaga.ts Normal file
View File

@@ -0,0 +1,101 @@
import { ICommentService } from 'core/services/comments'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
import { take, fork, select, put, call, cancelled, all } from 'redux-saga/effects'
import { CommentActionType } from 'constants/commentActionType'
import { authorizeSelector } from 'reducers/authorize/authorizeSelector'
import {serverActions, commentActions, postActions} from 'actions'
import CommentAPI from 'api/CommentAPI'
import { postComments } from 'models/comments/commentTypes'
import { eventChannel, Channel } from 'redux-saga'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { Post } from 'core/domain/posts'
import { postSelector } from 'reducers/posts/postSelector'
/**
* Get service providers
*/
const commentService: ICommentService = provider.get<ICommentService>(SocialProviderTypes.CommentService)
/***************************** Subroutines ************************************/
/**
* Creating channel event and subscribing get comments service
*/
function fetchCommentsChannel(postId: string) {
return eventChannel<postComments>((emmiter) => {
const unsubscribe = commentService.getComments(postId, (comments: postComments) => {
emmiter(comments)
})
return () => {
unsubscribe()
}
})
}
/**
* Set comments in store
*/
function* setComments(ownerId: string, postId: string, comments: postComments) {
/**
* Workout getting the number of post's comment and getting three last comments
*/
yield put(commentActions.addCommentList(comments))
let commentsCount: number
const post: Post = 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))
}
}
}
/**
* Fetch comments from the server
*/
function* dbFetchComments(ownerId: string, postId: string) {
const currentUser = yield select(authorizeSelector.getCurrentUser)
const getCommentsRequest = CommentAPI.createGetCommentsRequest(postId)
yield put(serverActions.sendRequest(getCommentsRequest))
const channelSubscription: Channel<postComments> = yield call(fetchCommentsChannel, postId)
let comments = yield take(channelSubscription)
getCommentsRequest.status = ServerRequestStatusType.OK
yield call(setComments, ownerId, postId, comments)
yield put(serverActions.sendRequest(getCommentsRequest))
try {
while (true) {
let comments = yield take(channelSubscription)
yield call(setComments, ownerId, postId, comments)
}
} finally {
console.trace('FiNALLY')
if (yield cancelled()) {
channelSubscription.close()
console.trace('comments cancelled')
}
}
}
/******************************************************************************/
/******************************* WATCHERS *************************************/
/******************************************************************************/
function* watchFetchComments() {
while (true) {
const {payload} = yield take(CommentActionType.DB_FETCH_COMMENTS)
const {ownerUserId, postId} = payload
yield fork(dbFetchComments, ownerUserId, postId)
}
}
export default function* commentSaga() {
yield all([
watchFetchComments()
])
}

9
src/sagas/rootSaga.ts Normal file
View File

@@ -0,0 +1,9 @@
import { all, fork } from 'redux-saga/effects'
import commentSaga from 'sagas/commentSaga'
export default function* root() {
yield all([
commentSaga()
])
}