From 77359f7fc232de84db5dfe65ab4be9f20be42453 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Thu, 3 Oct 2024 04:34:03 +0530 Subject: [PATCH] friends page state use a separate redux slice for friends page add musician slice to prevent mess up with the other user objects fetched globally. people redux slice would serve as global store having user records that can be referenced elsewhere --- jam-ui/cypress/e2e/friends/friends-page.cy.js | 5 +- jam-ui/src/components/page/JKPeopleFilter.js | 11 +- jam-ui/src/store/features/musiciansSlice.js | 166 ++++++++++++++++++ jam-ui/src/store/features/peopleSlice.js | 137 ++++++++------- jam-ui/src/store/store.js | 2 + 5 files changed, 246 insertions(+), 75 deletions(-) create mode 100644 jam-ui/src/store/features/musiciansSlice.js diff --git a/jam-ui/cypress/e2e/friends/friends-page.cy.js b/jam-ui/cypress/e2e/friends/friends-page.cy.js index e9b6bcc9f..5985c2856 100644 --- a/jam-ui/cypress/e2e/friends/friends-page.cy.js +++ b/jam-ui/cypress/e2e/friends/friends-page.cy.js @@ -99,6 +99,10 @@ describe('Friends page with data', () => { cy.intercept('POST', /\S+\/filter\?offset=10/, { fixture: 'people_page2' }).as('getPeople_page2'); cy.intercept('POST', /\S+\/filter\?offset=20/, { fixture: 'people_page3' }).as('getPeople_page3'); cy.intercept('GET', /\S+\/profile/, { fixture: 'person' }); + cy.intercept('GET', /\S+\/my_notifications\S+/, { + statusCode: 200, + body: [] + }); }); describe('listing users', () => { @@ -116,7 +120,6 @@ describe('Friends page with data', () => { cy.wait('@getPeople_page2') cy.get('[data-testid=paginate-next-page]').click(); cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 20); - //cy.get('[data-testid=paginate-next-page]').should('not.exist'); cy.get('[data-testid=paginate-next-page]').click(); cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 30); cy.get('[data-testid=paginate-next-page]').should('not.exist'); diff --git a/jam-ui/src/components/page/JKPeopleFilter.js b/jam-ui/src/components/page/JKPeopleFilter.js index 3bf8f6ba7..f24769b30 100644 --- a/jam-ui/src/components/page/JKPeopleFilter.js +++ b/jam-ui/src/components/page/JKPeopleFilter.js @@ -14,7 +14,7 @@ import JKPeopleSwiper from './JKPeopleSwiper'; import { getGenres, getInstruments } from '../../helpers/rest'; import { useForm, Controller, useFormState } from 'react-hook-form'; import { useDispatch, useSelector } from 'react-redux'; -import { fetchPeople, resetState, loadPrefetched, preFetchPeople } from '../../store/features/peopleSlice'; +import { fetchPeople, resetState, loadPrefetched, preFetchPeople } from '../../store/features/musiciansSlice'; function JKPeopleFilter() { const { t } = useTranslation(); @@ -35,11 +35,10 @@ function JKPeopleFilter() { const peopleListRef = useRef(); - const people = useSelector(state => state.people.people); - //const hasOffset = useSelector(state => state.people.hasOffset); - const offset = useSelector(state => state.people.offset); - const loadingStatus = useSelector(state => state.people.status); - const prefetched = useSelector(state => state.people.prefetched) + const people = useSelector(state => state.musician.people); + const offset = useSelector(state => state.musician.offset); + const loadingStatus = useSelector(state => state.musician.status); + const prefetched = useSelector(state => state.musician.prefetched) const { register, handleSubmit, setValue, getValues, control } = useForm({ defaultValues: { diff --git a/jam-ui/src/store/features/musiciansSlice.js b/jam-ui/src/store/features/musiciansSlice.js new file mode 100644 index 000000000..796b35540 --- /dev/null +++ b/jam-ui/src/store/features/musiciansSlice.js @@ -0,0 +1,166 @@ +import { createSlice, createAsyncThunk } from "@reduxjs/toolkit" +import { getPeople, getPeopleByIds, getPersonById, acceptFriendRequest as accept } from '../../helpers/rest'; + +const initialState = { + people: [], + prefetched: [], + status: 'idel', + error: null, + hasOffset: false, + offset: 0 +} + +export const fetchPeople = createAsyncThunk( + 'musician/fetchPeople', + async (options, thunkAPI) => { + const response = await getPeople(options) + return response.json() + } +) + +export const preFetchPeople = createAsyncThunk( + 'musician/preFetchPeople', + async (options, thunkAPI) => { + const response = await getPeople(options) + return response.json() + } +) + +export const fetchPeopleByIds = createAsyncThunk( + 'musician/fetchPeopleByIds', + async (options, thunkAPI) => { + const response = await getPeopleByIds(options) + return response.json() + } +) + +export const fetchPerson = createAsyncThunk( + 'musician/fetchPerson', + async (options, thunkAPI) => { + const {userId} = options + const response = await getPersonById(userId) + return response.json() + } + ,{ + condition: (options, {getState, extra}) => { + const {people} = getState() + const {userId} = options + const person = people.people.find(person => person.id === userId) + if(person && person.website){ + //only proceed if full data set for user has not been fetched. person.website is not included in the initial data fetching (i.e: in friends listing ). + return false; + } + } + } +) + +export const acceptFriendRequest = createAsyncThunk( + 'musician/acceptFriendRequest', + async(options) => { + const { userId, ...rest } = options + const response = await accept(userId, rest) + return response.json() + } +) + +export const musicianSlice = createSlice({ + name: 'musician', + initialState, + reducers: { + add: (state, action) => { + state.people.push(action.payload) + }, + resetState: (state) => { + return { ...initialState } + }, + loadPrefetched: (state, action) => { + if(state.prefetched.length > 0){ + const records = [...state.people, ...state.prefetched]; + state.people = records; + } + state.prefetched = [] + } + }, + extraReducers: (builder) => { + builder + .addCase(fetchPeople.pending, (state, action) => { + state.status = 'loading' + }) + .addCase(fetchPeople.fulfilled, (state, action) => { + // const records = [...state.people, ...action.payload.musicians]; + // state.people = records + // state.hasOffset = !!action.payload.offset + // state.offset = action.payload.offset + // state.status = 'succeeded' + //--- + const records = new Set([...state.people, ...action.payload.musicians]); + const unique = []; + records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) + state.people = unique + state.hasOffset = !!action.payload.offset + state.offset = action.payload.offset + state.status = 'succeeded' + }) + .addCase(fetchPeople.rejected, (state, action) => { + state.status = 'failed' + state.error = action.error.message + }) + .addCase(preFetchPeople.pending, (state, action) => { + state.status = 'loading' + }) + .addCase(preFetchPeople.fulfilled, (state, action) => { + // const records = [...state.prefetched, ...action.payload.musicians]; + // state.prefetched = records + // state.hasOffset = !!action.payload.offset + // state.offset = action.payload.offset + // state.status = 'succeeded' + //--- + const records = new Set([...state.prefetched, ...action.payload.musicians]); + const unique = []; + records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) + state.people = unique + state.hasOffset = !!action.payload.offset + state.offset = action.payload.offset + state.status = 'succeeded' + }) + .addCase(preFetchPeople.rejected, (state, action) => { + state.error = action.error.message + state.status = 'failed' + }) + .addCase(acceptFriendRequest.fulfilled, (state, action) => { + }) + .addCase(fetchPerson.fulfilled, (state, action) => { + const person = state.people.find(person => person.id === action.payload.id) + if(person){ + const updated = { + ...person, + ...action.payload + } + const objIndex = state.people.findIndex((p => p.id === updated.id)); + state.people[objIndex] = updated + }else{ + state.people.push(action.payload) + } + }) + .addCase(fetchPeopleByIds.pending, (state, action) => { + state.status = 'loading' + }) + .addCase(fetchPeopleByIds.fulfilled, (state, action) => { + const records = new Set([...state.people, ...action.payload.musicians]); + const unique = []; + records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) + state.people = unique + state.status = 'succeeded' + }) + .addCase(fetchPeopleByIds.rejected, (state, action) => { + state.error = action.error.message + state.status = 'failed' + }) + } +}) + +export const selectPersonById = (state, userId) => state.people.find((person) => person.id === userId) + +export const { add, resetState, loadPrefetched } = musicianSlice.actions; + +export default musicianSlice.reducer; \ No newline at end of file diff --git a/jam-ui/src/store/features/peopleSlice.js b/jam-ui/src/store/features/peopleSlice.js index f73ca694f..dff91a503 100644 --- a/jam-ui/src/store/features/peopleSlice.js +++ b/jam-ui/src/store/features/peopleSlice.js @@ -1,30 +1,31 @@ import { createSlice, createAsyncThunk } from "@reduxjs/toolkit" -import { getPeople, getPeopleByIds, getPersonById, acceptFriendRequest as accept } from '../../helpers/rest'; +//import { getPeople, getPeopleByIds, getPersonById, acceptFriendRequest as accept } from '../../helpers/rest'; +import { getPeopleByIds, getPersonById, acceptFriendRequest as accept } from '../../helpers/rest'; const initialState = { people: [], - prefetched: [], + //prefetched: [], status: 'idel', error: null, hasOffset: false, offset: 0 } -export const fetchPeople = createAsyncThunk( - 'people/fetchPeople', - async (options, thunkAPI) => { - const response = await getPeople(options) - return response.json() - } -) +// export const fetchPeople = createAsyncThunk( +// 'people/fetchPeople', +// async (options, thunkAPI) => { +// const response = await getPeople(options) +// return response.json() +// } +// ) -export const preFetchPeople = createAsyncThunk( - 'people/preFetchPeople', - async (options, thunkAPI) => { - const response = await getPeople(options) - return response.json() - } -) +// export const preFetchPeople = createAsyncThunk( +// 'people/preFetchPeople', +// async (options, thunkAPI) => { +// const response = await getPeople(options) +// return response.json() +// } +// ) export const fetchPeopleByIds = createAsyncThunk( 'people/fetchPeopleByIds', @@ -73,60 +74,60 @@ export const peopleSlice = createSlice({ resetState: (state) => { return { ...initialState } }, - loadPrefetched: (state, action) => { - if(state.prefetched.length > 0){ - const records = [...state.people, ...state.prefetched]; - state.people = records; - } - state.prefetched = [] - } + // loadPrefetched: (state, action) => { + // if(state.prefetched.length > 0){ + // const records = [...state.people, ...state.prefetched]; + // state.people = records; + // } + // state.prefetched = [] + // } }, extraReducers: (builder) => { builder - .addCase(fetchPeople.pending, (state, action) => { - state.status = 'loading' - }) - .addCase(fetchPeople.fulfilled, (state, action) => { - // const records = [...state.people, ...action.payload.musicians]; - // state.people = records - // state.hasOffset = !!action.payload.offset - // state.offset = action.payload.offset - // state.status = 'succeeded' - //--- - const records = new Set([...state.people, ...action.payload.musicians]); - const unique = []; - records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) - state.people = unique - state.hasOffset = !!action.payload.offset - state.offset = action.payload.offset - state.status = 'succeeded' - }) - .addCase(fetchPeople.rejected, (state, action) => { - state.status = 'failed' - state.error = action.error.message - }) - .addCase(preFetchPeople.pending, (state, action) => { - state.status = 'loading' - }) - .addCase(preFetchPeople.fulfilled, (state, action) => { - // const records = [...state.prefetched, ...action.payload.musicians]; - // state.prefetched = records - // state.hasOffset = !!action.payload.offset - // state.offset = action.payload.offset - // state.status = 'succeeded' - //--- - const records = new Set([...state.prefetched, ...action.payload.musicians]); - const unique = []; - records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) - state.people = unique - state.hasOffset = !!action.payload.offset - state.offset = action.payload.offset - state.status = 'succeeded' - }) - .addCase(preFetchPeople.rejected, (state, action) => { - state.error = action.error.message - state.status = 'failed' - }) + // .addCase(fetchPeople.pending, (state, action) => { + // state.status = 'loading' + // }) + // .addCase(fetchPeople.fulfilled, (state, action) => { + // // const records = [...state.people, ...action.payload.musicians]; + // // state.people = records + // // state.hasOffset = !!action.payload.offset + // // state.offset = action.payload.offset + // // state.status = 'succeeded' + // //--- + // const records = new Set([...state.people, ...action.payload.musicians]); + // const unique = []; + // records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) + // state.people = unique + // state.hasOffset = !!action.payload.offset + // state.offset = action.payload.offset + // state.status = 'succeeded' + // }) + // .addCase(fetchPeople.rejected, (state, action) => { + // state.status = 'failed' + // state.error = action.error.message + // }) + // .addCase(preFetchPeople.pending, (state, action) => { + // state.status = 'loading' + // }) + // .addCase(preFetchPeople.fulfilled, (state, action) => { + // // const records = [...state.prefetched, ...action.payload.musicians]; + // // state.prefetched = records + // // state.hasOffset = !!action.payload.offset + // // state.offset = action.payload.offset + // // state.status = 'succeeded' + // //--- + // const records = new Set([...state.prefetched, ...action.payload.musicians]); + // const unique = []; + // records.map(x => unique.filter(p => p.id === x.id).length > 0 ? null : unique.push(x)) + // state.people = unique + // state.hasOffset = !!action.payload.offset + // state.offset = action.payload.offset + // state.status = 'succeeded' + // }) + // .addCase(preFetchPeople.rejected, (state, action) => { + // state.error = action.error.message + // state.status = 'failed' + // }) .addCase(acceptFriendRequest.fulfilled, (state, action) => { }) .addCase(fetchPerson.fulfilled, (state, action) => { @@ -161,6 +162,6 @@ export const peopleSlice = createSlice({ export const selectPersonById = (state, userId) => state.people.find((person) => person.id === userId) -export const { add, resetState, loadPrefetched } = peopleSlice.actions; +export const { add, resetState } = peopleSlice.actions; export default peopleSlice.reducer; \ No newline at end of file diff --git a/jam-ui/src/store/store.js b/jam-ui/src/store/store.js index fec3b7ad5..07bf6d9f9 100644 --- a/jam-ui/src/store/store.js +++ b/jam-ui/src/store/store.js @@ -2,6 +2,7 @@ import { configureStore } from "@reduxjs/toolkit" import textMessageReducer from "./features/textMessagesSlice" import lobbyChatMessagesReducer from "./features/lobbyChatMessagesSlice" import peopleReducer from "./features/peopleSlice" +import MusicianReducer from "./features/musiciansSlice" import onlineMusicianReducer from "./features/onlineMusiciansSlice" import sessionReducer from "./features/sessionsSlice" import notificationReducer from './features/notificationSlice' @@ -15,6 +16,7 @@ export default configureStore({ reducer: { textMessage: textMessageReducer, people: peopleReducer, + musician: MusicianReducer, notification: notificationReducer, session: sessionReducer, // this is the slice that holds the currently active sessions latency: latencyReducer,