import { Message } from '@proton/shared/lib/interfaces/mail/Message' ; import { RequireSome } from '@proton/shared/lib/interfaces/utils' ; import { useHandler , useFolders , useCache } from '@proton/components' ; import { MessageCountsModel , ConversationCountsModel } from '@proton/shared/lib/models' ; import { LabelCount } from '@proton/shared/lib/interfaces/Label' ; import { STATUS } from '@proton/shared/lib/models/cache' ; import { MAILBOX_LABEL_IDS } from '@proton/shared/lib/constants' ; import { useDispatch } from 'react-redux' ; import { useGetElementByID } from '../mailbox/useElements' ; import { Conversation } from '../../models/conversation' ; import { Element } from '../../models/element' ; import { isMessage as testIsMessage , hasLabel , getCurrentFolderIDs } from '../../helpers/elements' ; import { updateCounters } from '../../helpers/counter' ; import { LabelChanges , applyLabelChangesOnMessage , applyLabelChangesOnConversation , applyLabelChangesOnOneMessageOfAConversation , UnreadStatus , } from '../../helpers/labels' ; import { CacheEntry } from '../../models/tools' ; import { optimisticApplyLabels as optimisticApplyLabelsElementsAction } from '../../logic/elements/elementsActions' ; import { applyLabelsOnConversation , applyLabelsOnConversationMessages , } from '../../logic/conversations/conversationsActions' ; import { useGetConversation } from '../conversation/useConversation' ; import { optimisticApplyLabels as optimisticApplyLabelsMessageAction } from '../../logic/messages/optimistic/messagesOptimisticActions' ; const { SENT , DRAFTS } = MAILBOX_LABEL_IDS ; const computeRollbackLabelChanges = ( element , changes ) => { const rollbackChange = { } as LabelChanges ; Object . keys ( changes ) . forEach ( ( labelID ) => { if ( changes [ labelID ] && ! hasLabel ( element , labelID ) ) { rollbackChange [ labelID ] = false ; } if ( ! changes [ labelID ] && hasLabel ( element , labelID ) ) { rollbackChange [ labelID ] = true ; } } ) ; return rollbackChange ; } ; export const useOptimisticApplyLabels = ( ) => { const dispatch = useDispatch ( ) ; const getElementByID = useGetElementByID ( ) ; const [ folders = [ ] ] = useFolders ( ) ; const globalCache = useCache ( ) ; const getConversation = useGetConversation ( ) ; param elements param inputChanges param isMove param unreadStatuses param currentLabelID returns const optimisticApplyLabels = useHandler ( ( elements , inputChanges , isMove = false , unreadStatuses ? , currentLabelID ? ) => { const rollbackChanges = [ ] as { element : Element ; changes : LabelChanges ; } [ ] ; const updatedElements = [ ] as Element [ ] ; const elementsUnreadStatuses = [ ] as UnreadStatus [ ] ; const isMessage = testIsMessage ( elements [ 0 ] ) ; let { value : messageCounters } = globalCache . get ( MessageCountsModel . key ) as CacheEntry < LabelCount [ ] > ; let { value : conversationCounters } = globalCache . get ( ConversationCountsModel . key ) as CacheEntry < LabelCount [ ] > ; elements . forEach ( ( element ) => { const changes = { ... inputChanges } ; if ( isMove ) { const currentFolderIDs = ( [ SENT , DRAFTS ] as string [ ] ) . includes ( currentLabelID || '' ) ? [ currentLabelID as string ] : getCurrentFolderIDs ( element , folders ) ; const isMoveToCurrentFolder = currentFolderIDs . every ( ( folderID ) => changes [ folderID ] ) ; if ( isMoveToCurrentFolder ) { return ; } if ( Object . keys ( inputChanges ) . includes ( MAILBOX_LABEL_IDS . TRASH ) ) { if ( isMessage ) { const message = element as Message ; elementsUnreadStatuses . push ( { id : message . ID , unread : message . Unread } ) ; } else { const conversation = element as Conversation ; elementsUnreadStatuses . push ( { id : conversation . ID ? conversation . ID : '' , unread : conversation . NumUnread ? conversation . NumUnread : 0 , } ) ; } } currentFolderIDs . forEach ( ( folderID ) => { changes [ folderID ] = false ; } ) ; } rollbackChanges . push ( { element , changes : computeRollbackLabelChanges ( element , changes ) } ) ; if ( isMessage ) { const message = element as Message ; dispatch ( optimisticApplyLabelsMessageAction ( { ID : element . ID || '' , changes , unreadStatuses } ) ) ; const conversationResult = getConversation ( message . ConversationID ) ; if ( conversationResult && conversationResult . Conversation ) { const conversation = conversationResult . Conversation ; const { updatedConversation , conversationChanges } = applyLabelChangesOnOneMessageOfAConversation ( conversation , changes ) ; dispatch ( applyLabelsOnConversationMessages ( { ID : message . ConversationID , messageID : message . ID , changes , unreadStatuses , updatedConversation , conversationResult , } ) ) ; conversationCounters = updateCounters ( conversation , conversationCounters , conversationChanges ) ; } const messageElement = getElementByID ( message . ID ) ; if ( messageElement && messageElement . ID ) { updatedElements . push ( applyLabelChangesOnMessage ( messageElement as Message , changes , unreadStatuses ) ) ; } const conversationElement = getElementByID ( message . ConversationID ) ; if ( conversationElement && conversationElement . ID ) { const { updatedConversation } = applyLabelChangesOnOneMessageOfAConversation ( conversationElement , changes ) ; updatedElements . push ( updatedConversation ) ; } messageCounters = updateCounters ( message , messageCounters , changes ) ; } else { const conversation = element as RequireSome < Conversation , 'ID' > ; const conversationFromState = getConversation ( conversation . ID ) ; dispatch ( applyLabelsOnConversation ( { ID : conversation . ID , changes , unreadStatuses } ) ) ; const conversationElement = getElementByID ( conversation . ID ) ; if ( conversationElement && conversationElement . ID ) { updatedElements . push ( applyLabelChangesOnConversation ( conversationElement , changes , unreadStatuses ) ) ; } conversationFromState ?. Messages ?. forEach ( ( message ) => { dispatch ( optimisticApplyLabelsMessageAction ( { ID : message . ID , changes , unreadStatuses } ) ) ; } ) ; conversationCounters = updateCounters ( conversation , conversationCounters , changes ) ; } } ) ; if ( updatedElements . length ) { dispatch ( optimisticApplyLabelsElementsAction ( { elements : updatedElements , isMove } ) ) ; } globalCache . set ( MessageCountsModel . key , { value : messageCounters , status : STATUS . RESOLVED } ) ; globalCache . set ( ConversationCountsModel . key , { value : conversationCounters , status : STATUS . RESOLVED } ) ; return ( ) => { rollbackChanges . forEach ( ( rollbackChange ) => { optimisticApplyLabels ( [ rollbackChange . element ] , rollbackChange . changes , false , elementsUnreadStatuses ) ; } ) ; } ; } ) ; return optimisticApplyLabels ; } ;