import { Draft } from 'immer' ; import { PayloadAction } from '@reduxjs/toolkit' ; import { Message } from '@proton/shared/lib/interfaces/mail/Message' ; import { ConversationErrors , ConversationEvent , ConversationParams , ConversationResult , ConversationsState , ConversationState , } from './conversationsTypes' ; import { allConversations , conversationByID } from './conversationsSelectors' ; import { RootState } from '../store' ; import { Conversation } from '../../models/conversation' ; import { isNetworkError , isNotExistError } from '../../helpers/errors' ; import { applyLabelChangesOnConversation , applyLabelChangesOnMessage , LabelChanges , UnreadStatus , } from '../../helpers/labels' ; import { applyMarkAsChangesOnConversation , MarkAsChanges } from '../../hooks/optimistic/useOptimisticMarkAs' ; import { mergeConversations } from '../../helpers/conversation' ; import { parseLabelIDsInEvent } from '../../helpers/elements' ; import { applyMarkAsChangesOnMessage } from '../../helpers/message/messages' ; export const globalReset = ( state ) => { Object . keys ( state ) . forEach ( ( key ) => delete state [ key ] ) ; } ; const getConversation = ( state , ID ) => conversationByID ( { conversations : state } as RootState , { ID } ) ; const getAllConversation = ( state ) => allConversations ( { conversations : state } as RootState ) ; export const initialize = ( state , action ) => { state [ action . payload . Conversation . ID ] = action . payload as any ; } ; export const loadFulfilled = ( state , action ) => { const { Conversation , Messages } = action . payload ; const { conversationID } = action . meta . arg ; const conversationState = getConversation ( state , conversationID ) ; if ( conversationState ) { conversationState . Conversation = Conversation ; conversationState . Messages = Messages ; conversationState . loadRetry = conversationState . loadRetry ? conversationState . loadRetry + 1 : 1 ; conversationState . errors = { network : [ ] , unknown : [ ] } ; } else { state [ Conversation . ID ] = { Conversation , Messages , loadRetry : 1 , errors : { network : [ ] , unknown : [ ] } , } ; } } ; export const loadRejected = ( state , action ) => { const error = action . payload ; const errors = { } ; if ( isNetworkError ( error ) ) { errors . network = [ error ] ; } else if ( isNotExistError ( error ) ) { errors . notExist = [ error ] ; } else { errors . unknown = [ error ] ; } const { conversationID } = action . meta . arg ; const conversationState = getConversation ( state , conversationID ) ; if ( conversationState ) { conversationState . errors = errors ; conversationState . loadRetry = conversationState . loadRetry ? conversationState . loadRetry + 1 : 1 ; } } ; export const retryLoading = ( state , { payload : { ID } } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . loadRetry = 0 ; conversationState . errors = { } ; } } ; export const applyLabelsOnConversationMessages = ( state , { payload : { ID , messageID , changes , unreadStatuses , updatedConversation , conversationResult } , } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . Conversation = updatedConversation ; conversationState . Messages = conversationResult . Messages ?. map ( ( messageFromConversation ) => { if ( messageFromConversation . ID === messageID ) { return applyLabelChangesOnMessage ( messageFromConversation , changes , unreadStatuses ) ; } return messageFromConversation ; } ) ; } } ; export const applyLabelsOnConversation = ( state , { payload : { ID , changes , unreadStatuses } , } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . Conversation = applyLabelChangesOnConversation ( conversationState . Conversation , changes , unreadStatuses ) ; } } ; export const optimisticDelete = ( state , { payload : ID } ) => { delete state [ ID ] ; } ; export const optimisticDeleteConversationMessages = ( state , { payload : { ID , messages } } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . Messages = messages ; } } ; export const optimisticRestore = ( state , { payload : conversations } ) => { conversations . forEach ( ( conversation ) => { state [ conversation . Conversation . ID ] = conversation ; } ) ; } ; export const optimisticMarkAsConversationMessages = ( state , { payload : { ID , messageID , updatedConversation , changes } , } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . Conversation = updatedConversation ; conversationState . Messages = conversationState . Messages ?. map ( ( conversationMessage ) => { if ( conversationMessage . ID === messageID ) { return applyMarkAsChangesOnMessage ( conversationMessage , changes ) ; } return conversationMessage ; } ) ; } } ; export const optimisticMarkAsConversation = ( state , { payload : { ID , labelID , changes } } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . Conversation = applyMarkAsChangesOnConversation ( conversationState . Conversation , labelID , changes ) ; } } ; export const deleteConversation = ( state , { payload : ID } ) => { delete state [ ID ] ; } ; export const updateConversation = ( state , { payload : { ID , updates } } ) => { const currentConversation = state [ ID ] ; const updatedConversation = mergeConversations ( currentConversation , updates ) ; state [ ID ] = updatedConversation ; } ; export const eventMessagesUpdates = ( state , action ) => { const { toCreate , toUpdate , toDelete } = action . payload ; [ ... toCreate , ... toUpdate ] . forEach ( ( messageEvent ) => { const conversationState = getConversation ( state , messageEvent . ConversationID ) ; if ( conversationState ) { conversationState . loadRetry = 0 ; conversationState . errors = { } ; const isUpdate = conversationState . Messages ?. some ( ( message ) => message . ID === messageEvent . ID ) ; let updatedMessages ; if ( isUpdate && conversationState . Messages ) { updatedMessages = conversationState . Messages . map ( ( message ) => { if ( message . ID === messageEvent . ID ) { return parseLabelIDsInEvent ( message , messageEvent ) ; } return message ; } ) ; } else { updatedMessages = [ ... ( conversationState . Messages || [ ] ) , messageEvent ] ; } conversationState . Messages = updatedMessages ; } } ) ; if ( Object . keys ( toDelete ) . length > 0 ) { const allConversations = getAllConversation ( state ) ; allConversations . forEach ( ( conversationState ) => { if ( conversationState ?. Conversation . ID && conversationState . Messages ) { const updatedMessages = conversationState . Messages . filter ( ( { ID } ) => ! toDelete [ ID ] ) ; if ( conversationState . Messages . length !== updatedMessages . length ) { conversationState . Messages = updatedMessages ; } } } ) ; } } ; export const eventConversationUpdate = ( state , { payload : { ID , updatedConversation } } ) => { const conversationState = getConversation ( state , ID ) ; if ( conversationState ) { conversationState . Conversation = updatedConversation ; } } ;