Remove client-side isLoggedIn value
[ProtonMail-WebClient.git] / packages / mail / contacts / contactSlice.ts
blobe2d74c39a015f9ba7f15be2b94360941dbc73f42
1 import { type PayloadAction, type UnknownAction, createSlice, miniSerializeError } from '@reduxjs/toolkit';
2 import type { ThunkAction } from 'redux-thunk';
4 import { type ModelState, serverEvent } from '@proton/account';
5 import type { ProtonThunkArguments } from '@proton/redux-shared-store-types';
6 import {
7     type CacheType,
8     cacheHelper,
9     createPromiseMapStore,
10     getFetchedAt,
11     getFetchedEphemeral,
12 } from '@proton/redux-utilities';
13 import { getContact } from '@proton/shared/lib/api/contacts';
14 import { EVENT_ACTIONS } from '@proton/shared/lib/constants';
15 import { EVENT_ERRORS } from '@proton/shared/lib/errors';
16 import { hasBit } from '@proton/shared/lib/helpers/bitset';
17 import type { Contact } from '@proton/shared/lib/interfaces/contacts';
19 const name = 'contact';
21 export interface ContactState {
22     [name]: { [id: string]: ModelState<Contact> };
25 type SliceState = ContactState[typeof name];
26 type Model = SliceState;
28 export const selectContact = (state: ContactState) => state[name];
30 const initialState: Model = {};
31 const slice = createSlice({
32     name,
33     initialState,
34     reducers: {
35         pending: (state, action: PayloadAction<{ id: string }>) => {
36             const oldValue = state[action.payload.id];
37             if (oldValue && oldValue.error) {
38                 oldValue.error = undefined;
39             }
40         },
41         fulfilled: (state, action: PayloadAction<{ id: string; value: Contact }>) => {
42             state[action.payload.id] = {
43                 value: action.payload.value,
44                 error: undefined,
45                 meta: { fetchedAt: getFetchedAt(), fetchedEphemeral: getFetchedEphemeral() },
46             };
47         },
48         rejected: (state, action: PayloadAction<{ id: string; value: any }>) => {
49             state[action.payload.id] = {
50                 value: undefined,
51                 error: action.payload.value,
52                 meta: { fetchedAt: getFetchedAt(), fetchedEphemeral: undefined },
53             };
54         },
55     },
56     extraReducers: (builder) => {
57         builder.addCase(serverEvent, (state, action) => {
58             if (action.payload.Contacts) {
59                 for (const update of action.payload.Contacts) {
60                     if (update.Action === EVENT_ACTIONS.DELETE) {
61                         delete state[update.ID];
62                     }
63                     if (update.Action === EVENT_ACTIONS.UPDATE) {
64                         if (state[update.ID]) {
65                             state[update.ID].value = update.Contact as Contact;
66                         }
67                     }
68                 }
69             }
70             if (hasBit(action.payload.Refresh, EVENT_ERRORS.CONTACTS)) {
71                 return {};
72             }
73         });
74     },
75 });
77 const promiseStore = createPromiseMapStore<Contact>();
79 export const contactThunk = ({
80     contactID,
81     cache,
82 }: {
83     contactID: string;
84     cache?: CacheType;
85 }): ThunkAction<Promise<Contact>, ContactState, ProtonThunkArguments, UnknownAction> => {
86     return (dispatch, getState, extraArgument) => {
87         const select = () => {
88             return selectContact(getState())?.[contactID || ''];
89         };
90         const cb = async () => {
91             try {
92                 dispatch(slice.actions.pending({ id: contactID }));
93                 const contact = await extraArgument
94                     .api<{
95                         Contact: Contact;
96                     }>(getContact(contactID))
97                     .then(({ Contact }) => Contact);
98                 dispatch(slice.actions.fulfilled({ id: contactID, value: contact }));
99                 return contact;
100             } catch (error) {
101                 dispatch(slice.actions.rejected({ id: contactID, value: miniSerializeError(error) }));
102                 throw error;
103             }
104         };
105         return cacheHelper({ store: promiseStore, key: contactID, select, cb, cache });
106     };
109 export const contactReducer = { [name]: slice.reducer };