[Improvement] Move get comments action to saga (#48)
This commit is contained in:
26
package.json
26
package.json
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
33
src/api/CommentAPI.ts
Normal 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
|
||||
}
|
||||
@@ -24,7 +24,15 @@ const getRandomColor = () => {
|
||||
return color
|
||||
}
|
||||
|
||||
const updateObject = (oldObject: any, updatedProperties: any) => {
|
||||
return {
|
||||
...oldObject,
|
||||
...updatedProperties
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
logger,
|
||||
getRandomColor
|
||||
getRandomColor,
|
||||
updateObject
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(
|
||||
|
||||
3
src/models/comments/commentTypes.ts
Normal file
3
src/models/comments/commentTypes.ts
Normal 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}
|
||||
5
src/reducers/authorize/authorizeSelector.ts
Normal file
5
src/reducers/authorize/authorizeSelector.ts
Normal 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
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import { authorizeReducer } from './authorizeReducer'
|
||||
|
||||
export {authorizeReducer}
|
||||
import {authorizeSelector} from './authorizeSelector'
|
||||
export {
|
||||
authorizeReducer,
|
||||
authorizeSelector
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
import { postReducer } from './postReducer'
|
||||
import { postSelector } from './postSelector'
|
||||
|
||||
export {postReducer}
|
||||
export {
|
||||
postReducer,
|
||||
postSelector
|
||||
}
|
||||
9
src/reducers/posts/postSelector.ts
Normal file
9
src/reducers/posts/postSelector.ts
Normal 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
101
src/sagas/commentSaga.ts
Normal 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
9
src/sagas/rootSaga.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { all, fork } from 'redux-saga/effects'
|
||||
import commentSaga from 'sagas/commentSaga'
|
||||
|
||||
export default function* root() {
|
||||
yield all([
|
||||
commentSaga()
|
||||
])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user