Skip to content

Commit

Permalink
SW-5331 Used search to imporve find module projects performances (#2606)
Browse files Browse the repository at this point in the history
This fixes a slow down in the home screen, which is very visible especailly when switch organizations.
  • Loading branch information
tommylau523 committed May 13, 2024
1 parent b0524ec commit 754d895
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 38 deletions.
31 changes: 14 additions & 17 deletions src/providers/Participant/ParticipantProvider.tsx
@@ -1,8 +1,8 @@
import React, { useCallback, useEffect, useState } from 'react';

import { useLocalization, useOrganization } from 'src/providers/hooks';
import { requestListAllModules, requestListModules } from 'src/redux/features/modules/modulesAsyncThunks';
import { selectAllProjectModuleList, selectProjectModuleList } from 'src/redux/features/modules/modulesSelectors';
import { requestListModuleProjects, requestListModules } from 'src/redux/features/modules/modulesAsyncThunks';
import { selectModuleProjects, selectProjectModuleList } from 'src/redux/features/modules/modulesSelectors';
import { requestGetParticipant } from 'src/redux/features/participants/participantsAsyncThunks';
import { selectParticipant } from 'src/redux/features/participants/participantsSelectors';
import { selectProjects } from 'src/redux/features/projects/projectsSelectors';
Expand All @@ -29,14 +29,14 @@ const ParticipantProvider = ({ children }: Props) => {
const [activeModules, setActiveModules] = useState<Module[]>([]);

const [listModulesRequest, setListModulesRequest] = useState<string>('');
const [allModulesRequest, setAllModulesRequest] = useState<string>('');
const [listModuleProjectsRequest, setListModuleProjectsRequest] = useState<string>('');

const participant = useAppSelector(selectParticipant(currentParticipantProject?.participantId || -1));

const projectModuleList = useAppSelector(selectProjectModuleList(listModulesRequest));
const projects = useAppSelector(selectProjects);

const allProjectModuleList = useAppSelector(selectAllProjectModuleList(allModulesRequest));
const moduleProjectsList = useAppSelector(selectModuleProjects(listModuleProjectsRequest));

const _setCurrentParticipantProject = useCallback(
(projectId: string | number) => {
Expand Down Expand Up @@ -70,12 +70,11 @@ const ParticipantProvider = ({ children }: Props) => {
}, [projects]);

useEffect(() => {
if (participantProjects && participantProjects.length > 0) {
const projectIds = participantProjects.map((project) => project.id);
const request = dispatch(requestListAllModules(projectIds));
setAllModulesRequest(request.requestId);
if (selectedOrganization) {
const request = dispatch(requestListModuleProjects(selectedOrganization.id));
setListModuleProjectsRequest(request.requestId);
}
}, [participantProjects, dispatch]);
}, [selectedOrganization, dispatch]);

useEffect(() => {
if (!projectModuleList) {
Expand All @@ -90,21 +89,19 @@ const ParticipantProvider = ({ children }: Props) => {
}, [projectModuleList]);

useEffect(() => {
if (allProjectModuleList && allProjectModuleList.status === 'success' && allProjectModuleList.data) {
const allProjectModules = allProjectModuleList.data;
const nextModuleProjects = (participantProjects || []).filter(
(project) =>
allProjectModules.findIndex(({ id, modules }) => id === project.id && modules && modules.length > 0) !== -1
);
if (moduleProjectsList && moduleProjectsList.status === 'success' && moduleProjectsList.data) {
const nextModuleProjects = moduleProjectsList.data
.map((id) => participantProjects.find((project) => project.id === id))
.filter((project): project is Project => !!project);

setModuleProjects(nextModuleProjects);

// Assign the first project with modules as the current participant project
if (nextModuleProjects.length > 0 && !currentParticipantProject) {
setCurrentParticipantProject(nextModuleProjects[0]);
}
return;
}
}, [allProjectModuleList, currentParticipantProject, participantProjects]);
}, [moduleProjectsList, currentParticipantProject, participantProjects]);

useEffect(() => {
if (currentParticipantProject?.participantId) {
Expand Down
41 changes: 29 additions & 12 deletions src/redux/features/modules/modulesAsyncThunks.ts
@@ -1,7 +1,10 @@
import { createAsyncThunk } from '@reduxjs/toolkit';

import { SearchService } from 'src/services';
import ModuleService from 'src/services/ModuleService';
import strings from 'src/strings';
import { ModuleProjectSearchResult } from 'src/types/Module';
import { SearchRequestPayload } from 'src/types/Search';

export const requestGetModule = createAsyncThunk(
'modules/get',
Expand All @@ -26,18 +29,32 @@ export const requestListModules = createAsyncThunk('modules/list', async (projec
return rejectWithValue(strings.GENERIC_ERROR);
});

export const requestListAllModules = createAsyncThunk(
'modules/listAll',
async (projectIds: number[], { rejectWithValue }) => {
const results = await Promise.all(projectIds.map((projectId) => ModuleService.list(projectId)));

const requestSucceeded = results.reduce(
(prev, result) => prev && !!result && result.requestSucceeded && !!result.data,
true
);

if (requestSucceeded) {
return results.map((result, idx) => ({ id: projectIds[idx], modules: result.data?.modules }));
export const requestListModuleProjects = createAsyncThunk(
'modules/listProjects',
async (organizationId: number, { rejectWithValue }) => {
const searchParams: SearchRequestPayload = {
prefix: 'projects',
fields: ['id', 'participant.cohort.cohortModules.module_id'],
search: {
operation: 'field',
field: 'organization.id',
type: 'Exact',
values: [organizationId.toString()],
},
count: 20,
};

const response: ModuleProjectSearchResult[] | null = await SearchService.search(searchParams);

if (response) {
const moduleProjectIds = response
.filter(
(moduleProject) =>
moduleProject.participant?.cohort?.cohortModules &&
moduleProject.participant?.cohort?.cohortModules.length > 0
)
.map(({ id }) => Number(id));
return moduleProjectIds;
}

return rejectWithValue(strings.GENERIC_ERROR);
Expand Down
2 changes: 1 addition & 1 deletion src/redux/features/modules/modulesSelectors.ts
Expand Up @@ -4,4 +4,4 @@ export const selectModuleRequest = (requestId: string) => (state: RootState) =>

export const selectProjectModuleList = (requestId: string) => (state: RootState) => state.moduleList[requestId];

export const selectAllProjectModuleList = (requestId: string) => (state: RootState) => state.allModuleList[requestId];
export const selectModuleProjects = (requestId: string) => (state: RootState) => state.moduleProjects[requestId];
16 changes: 8 additions & 8 deletions src/redux/features/modules/modulesSlice.ts
Expand Up @@ -3,7 +3,7 @@ import { createSlice } from '@reduxjs/toolkit';
import { Module } from 'src/types/Module';

import { StatusT, buildReducers } from '../asyncUtils';
import { requestGetModule, requestListAllModules, requestListModules } from './modulesAsyncThunks';
import { requestGetModule, requestListModuleProjects, requestListModules } from './modulesAsyncThunks';

/**
* Get Module
Expand Down Expand Up @@ -34,23 +34,23 @@ export const moduleListSlice = createSlice({
});

/**
* List Modules
* List projects with modules in an organization
*/
const initialStateAllModuleList: { [key: string]: StatusT<{ id: number; modules: Module[] }[]> } = {};
const initialStateModuleProjects: { [key: string]: StatusT<number[]> } = {};

export const allModuleListSlice = createSlice({
name: 'allModuleListSlice',
initialState: initialStateAllModuleList,
export const moduleProjectsSlice = createSlice({
name: 'moduleProjectsSlice',
initialState: initialStateModuleProjects,
reducers: {},
extraReducers: (builder) => {
buildReducers(requestListAllModules)(builder);
buildReducers(requestListModuleProjects)(builder);
},
});

const moduleReducers = {
module: moduleSlice.reducer,
moduleList: moduleListSlice.reducer,
allModuleList: allModuleListSlice.reducer,
moduleProjects: moduleProjectsSlice.reducer,
};

export default moduleReducers;
11 changes: 11 additions & 0 deletions src/types/Module.ts
Expand Up @@ -24,6 +24,17 @@ export const getEventType = (input: ModuleEventType): string => {
}
};

export type ModuleProjectSearchResult = {
id: number;
participant?: {
cohort?: {
cohortModules?: {
module_id: string;
}[];
};
};
};

export const getEventStatus = (status: ModuleEventSessionStatus) => {
switch (status) {
case 'Not Started': {
Expand Down

0 comments on commit 754d895

Please sign in to comment.