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
This commit is contained in:
Nuwan 2024-10-03 04:34:03 +05:30
parent eea46feb5d
commit 77359f7fc2
5 changed files with 246 additions and 75 deletions

View File

@ -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');

View File

@ -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: {

View File

@ -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;

View File

@ -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;

View File

@ -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,