Files
frontend-app-discussions/src/discussions/posts/data/slices.js
Awais Ansari 59b4366edd Perf: improved discussions MFE's components re-rendering and loading time (#485)
* chore: configure WDYR for react profiling

* perf: reduced post content re-rendering

* perf: post content view and it child optimization

* perf: add memoization in post editor

* perf: add memoization in postCommnetsView

* perf: improved endorsed comment view rendering

* perf: improved re-rendering in reply component

* fix: uncomment questionType commentsView

* fix: removed console errors in postContent area

* perf: reduced postType and postId dependancy

* perf: improved re-rendering in discussionHome

* perf: improved re-rendering of postsList and its child components

* perf: improved re-rendering of legacyTopic and learner sidebar

* fix: postFilterBar filter was not updating

* fix: resolve duplicate comment posts issue

* fix: memory leaking issue in comments view

* fix: duplicate topic posts in inContext sidebar

* perf: add lazy loading

* chore: remove WDYR configuration

* fix: alert banner padding

* chore: update package-lock file
2023-05-08 15:14:53 +05:00

278 lines
8.7 KiB
JavaScript

/* eslint-disable no-param-reassign,import/prefer-default-export */
import { createSlice } from '@reduxjs/toolkit';
import omitBy from 'lodash/omitBy';
import {
PostsStatusFilter, RequestStatus, ThreadOrdering, ThreadType,
} from '../../../data/constants';
const mergeThreadsInTopics = (dataFromState, dataFromPayload) => {
const mergedArray = [];
mergedArray.push(dataFromState);
mergedArray.push(dataFromPayload);
return mergedArray.reduce((acc, obj) => {
const keys = Object.keys(obj);
const values = Object.values(obj);
keys.forEach((key, index) => {
if (!acc[key]) { acc[key] = []; }
if (Array.isArray(acc[key])) {
const uniqueValues = [...new Set(acc[key].concat(values[index]))];
acc[key] = uniqueValues;
} else { acc[key].push(values[index]); }
return acc;
});
return acc;
}, {});
};
const threadsSlice = createSlice({
name: 'thread',
initialState: {
status: RequestStatus.IN_PROGRESS,
avatars: {
// Mapping users to avatars
},
threadsInTopic: {
// Mapping of topic ids to thread ids in them
},
threadsById: {
// Mapping of threads ids to threads in them
},
author: null,
pages: [],
threadDraft: null,
nextPage: null,
totalPages: null,
totalThreads: null,
textSearchRewrite: null,
postStatus: RequestStatus.SUCCESSFUL,
filters: {
status: PostsStatusFilter.ALL,
postType: ThreadType.ALL,
cohort: '',
search: '',
},
postEditorVisible: false,
redirectToThread: null,
sortedBy: ThreadOrdering.BY_LAST_ACTIVITY,
},
reducers: {
fetchLearnerThreadsRequest: (state, { payload }) => {
if (state.author !== payload.author) {
state.pages = [];
state.author = payload.author;
}
state.status = RequestStatus.IN_PROGRESS;
},
clearPostsPages: (state) => {
state.pages = [];
},
fetchThreadsRequest: (state) => {
state.status = RequestStatus.IN_PROGRESS;
},
fetchThreadsSuccess: (state, { payload }) => {
const {
author, page, ids, threadsById, isFilterChanged, threadsInTopic, avatars, pagination, textSearchRewrite,
} = payload;
if (state.author !== author) {
state.pages = [];
state.author = author;
}
if (!state.pages[page - 1]) {
state.pages[page - 1] = ids;
} else {
state.pages[page - 1] = [...new Set([...state.pages[page - 1], ...ids])];
}
state.status = RequestStatus.SUCCESSFUL;
state.threadsById = { ...state.threadsById, ...threadsById };
state.threadsInTopic = (isFilterChanged || page === 1)
? { ...threadsInTopic }
: mergeThreadsInTopics(state.threadsInTopic, threadsInTopic);
state.avatars = { ...state.avatars, ...avatars };
state.nextPage = (page < pagination.numPages) ? page + 1 : null;
state.totalPages = pagination.numPages;
state.totalThreads = pagination.count;
state.textSearchRewrite = textSearchRewrite;
},
fetchThreadsFailed: (state) => {
state.status = RequestStatus.FAILED;
},
fetchThreadsDenied: (state) => {
state.status = RequestStatus.DENIED;
},
fetchThreadRequest: (state) => {
state.status = RequestStatus.IN_PROGRESS;
},
fetchThreadSuccess: (state, { payload }) => {
state.status = RequestStatus.SUCCESSFUL;
state.threadsById = { ...state.threadsById, ...payload.threadsById };
state.avatars = { ...state.avatars, ...payload.avatars };
},
fetchThreadByDirectLinkSuccess: (state, { payload }) => {
state.status = RequestStatus.SUCCESSFUL;
state.pages[payload.page - 1] = payload.ids;
state.threadsInTopic = { ...payload.threadsInTopic, ...state.threadsInTopic };
state.threadsById = { ...payload.threadsById, ...state.threadsById };
state.avatars = { ...state.avatars, ...payload.avatars };
},
fetchThreadFailed: (state) => {
state.status = RequestStatus.FAILED;
},
fetchThreadDenied: (state) => {
state.status = RequestStatus.DENIED;
},
postThreadRequest: (state, { payload }) => {
state.postStatus = RequestStatus.IN_PROGRESS;
state.threadDraft = payload;
},
postThreadSuccess: (state, { payload }) => {
state.postStatus = RequestStatus.SUCCESSFUL;
state.threadsById[payload.id] = payload;
state.threadsInTopic[payload.topicId] = [
...(state.threadsInTopic[payload.topicId] || []),
payload.id,
];
// Temporarily add it to the top of the list so it's visible
state.pages[0] = [payload.id].concat(state.pages[0] || []);
state.avatars = { ...state.avatars, ...payload.avatars };
state.redirectToThread = { topicId: payload.topicId, threadId: payload.id };
state.threadDraft = null;
},
postThreadFailed: (state) => {
state.postStatus = RequestStatus.FAILED;
},
postThreadDenied: (state) => {
state.postStatus = RequestStatus.DENIED;
},
updateThreadRequest: (state, { payload }) => {
state.postStatus = RequestStatus.IN_PROGRESS;
state.threadDraft = payload;
},
updateThreadAsRead: (state, { payload }) => {
state.threadsById[payload.threadId] = { ...state.threadsById[payload.threadId], read: true };
},
updateThreadSuccess: (state, { payload }) => {
state.postStatus = RequestStatus.SUCCESSFUL;
state.threadsById[payload.id] = {
...state.threadsById[payload.id],
...payload,
abuseFlaggedCount: state.threadsById[payload.id].abuseFlaggedCount || false,
};
state.avatars = { ...state.avatars, ...payload.avatars };
state.threadDraft = null;
},
updateThreadFailed: (state) => {
state.postStatus = RequestStatus.FAILED;
state.totalThreads = 0;
},
updateThreadDenied: (state) => {
state.postStatus = RequestStatus.DENIED;
state.totalThreads = 0;
},
deleteThreadRequest: (state) => {
state.postStatus = RequestStatus.IN_PROGRESS;
},
deleteThreadSuccess: (state, { payload }) => {
const { threadId } = payload;
const { topicId } = state.threadsById[threadId];
state.postStatus = RequestStatus.SUCCESSFUL;
state.threadsInTopic[topicId] = state.threadsInTopic[topicId].filter(item => item !== threadId);
state.pages = state.pages.map(page => page?.filter(item => item !== threadId));
state.threadsById = omitBy(state.threadsById, (thread) => thread.id === threadId);
},
deleteThreadFailed: (state) => {
state.postStatus = RequestStatus.FAILED;
},
deleteThreadDenied: (state) => {
state.postStatus = RequestStatus.DENIED;
},
setSortedBy: (state, { payload }) => {
state.sortedBy = payload;
state.pages = [];
},
setStatusFilter: (state, { payload }) => {
state.filters.status = payload;
state.pages = [];
},
setPostsTypeFilter: (state, { payload }) => {
state.filters.postType = payload;
state.pages = [];
},
setCohortFilter: (state, { payload }) => {
state.filters.cohort = payload;
state.pages = [];
},
setSearchQuery: (state, { payload }) => {
state.filters.search = payload;
// Search doesn't work with following
state.filters.status = state.filters.status === PostsStatusFilter.FOLLOWING
? PostsStatusFilter.ALL
: state.filters.status;
state.pages = [];
},
showPostEditor: (state) => {
state.postEditorVisible = true;
state.redirectToThread = null;
},
hidePostEditor: (state) => {
state.postEditorVisible = false;
},
clearRedirect: (state) => {
state.redirectToThread = null;
},
clearFilter: (state) => {
state.filters = {
status: PostsStatusFilter.ALL,
postType: ThreadType.ALL,
cohort: '',
search: '',
};
state.pages = [];
},
clearSort: (state) => {
state.sortedBy = ThreadOrdering.BY_LAST_ACTIVITY;
state.pages = [];
},
},
});
export const {
deleteThreadDenied,
deleteThreadFailed,
deleteThreadRequest,
deleteThreadSuccess,
fetchLearnerThreadsRequest,
fetchThreadDenied,
fetchThreadFailed,
fetchThreadRequest,
fetchThreadsDenied,
fetchThreadsFailed,
fetchThreadsRequest,
fetchThreadsSuccess,
fetchThreadSuccess,
fetchThreadByDirectLinkSuccess,
postThreadDenied,
postThreadFailed,
postThreadRequest,
postThreadSuccess,
updateThreadDenied,
updateThreadFailed,
updateThreadRequest,
updateThreadAsRead,
updateThreadSuccess,
setPostsTypeFilter,
setCohortFilter,
setSortedBy,
setStatusFilter,
setSearchQuery,
showPostEditor,
hidePostEditor,
clearRedirect,
clearPostsPages,
clearFilter,
clearSort,
} = threadsSlice.actions;
export const threadsReducer = threadsSlice.reducer;