import {createSlice, createAsyncThunk} from "@reduxjs/toolkit"; import {Contact} from "../../typeDefinitions"; import FirebaseQuery from "../../firebase/firebaseQuery"; import {DocumentData, QueryConstraint, QueryDocumentSnapshot, QuerySnapshot} from "firebase/firestore"; type InitialContactState = { status: string; lastContact: QueryDocumentSnapshot | undefined; values: { [key: string]: { [key: string]: unknown; }; }; }; const initialState: InitialContactState = { status: "", lastContact: undefined, values: {}, }; export const fetchContacts = createAsyncThunk( "contacts/fetchContacts", async ({ formattedConstraints }: {formattedConstraints?: QueryConstraint | QueryConstraint[] }, { rejectWithValue}) => { const firebaseQuery = new FirebaseQuery(); try { const documents = await firebaseQuery.getDocsWhere("contacts", formattedConstraints, true) as QuerySnapshot; return documents } catch (error) { return rejectWithValue(error); } } ); export const addContact = createAsyncThunk( "contacts/addContact", async ({ contactForm, userId }: { contactForm: Contact; userId: string }, { rejectWithValue }) => { const firebaseQuery = new FirebaseQuery(); try { await firebaseQuery.add(["contacts"], {...contactForm, uid: userId}) return contactForm } catch (error) { return rejectWithValue(error); } } ); export const updateContact = createAsyncThunk( "contacts/updateContact", async ({contactId, attributes }: { contactId: string, attributes: any }, { rejectWithValue }) => { const firebaseQuery = new FirebaseQuery(); try { await firebaseQuery.update(["contacts", contactId], attributes); return {contactId, attributes} } catch (error) { return rejectWithValue(error); } } ); export const deleteContact = createAsyncThunk( "contacts/deleteContact", async ({ contactId }: { contactId: string }, { rejectWithValue }) => { const firebaseQuery = new FirebaseQuery(); try { await firebaseQuery.delete(["contacts", contactId]) return contactId } catch (error) { return rejectWithValue(error); } } ); export const contactsSlice = createSlice({ name: "contacts", initialState, reducers: { setContacts: (state, action) => { state.values = action.payload }, addContacts: (state, action) => { state.values = {...state.values, ...action.payload} }, setLastContact: (state, action) => { state.lastContact = action.payload } }, extraReducers(builder){ builder .addCase(fetchContacts.pending, (state) => { state.status = "loading" }) .addCase(fetchContacts.fulfilled, (state, action) => { state.status = "success"; const newContacts = Object.fromEntries( action.payload.docs.map(doc => [doc.id, { ...doc.data(), docPath: doc.ref }]) ); return { ...state, values: { ...state.values, ...newContacts }, lastContact: action.payload.docs[action.payload.docs.length - 1] }; }) .addCase(fetchContacts.rejected, (state) => { state.status = "rejected" }) .addCase(addContact.fulfilled, (state, action) => { const newContact = action.payload const newContacts = {...state.values} newContacts[action.payload.id] = newContact }) .addCase(updateContact.fulfilled, (state, action) => { state.values[action.payload.contactId] = { ...state.values[action.payload.contactId], ...action.payload.attributes }; }) .addCase(deleteContact.fulfilled, (state, action) => { const contactIdToDelete = action.payload; const newContacts = { ...state, values: { ...state.values } }; delete newContacts.values[contactIdToDelete]; return newContacts; }) } }) export const {setContacts, setLastContact, addContacts} = contactsSlice.actions; export default contactsSlice.reducer;