1 import { useRef, useState } from 'react';
3 import { c } from 'ttag';
5 import type { Cancellable } from '@proton/components';
6 import { useHandler, useNotifications } from '@proton/components';
7 import useIsMounted from '@proton/hooks/useIsMounted';
8 import { wait } from '@proton/shared/lib/helpers/promise';
10 import type { SavingDraftNotificationAction } from '../../components/notifications/SavingDraftNotification';
11 import SavingDraftNotification from '../../components/notifications/SavingDraftNotification';
12 import { useOnCompose } from '../../containers/ComposeProvider';
13 import type { MessageState } from '../../store/messages/messagesTypes';
14 import type { PromiseHandlers } from '../usePromise';
15 import { ComposeTypes } from './useCompose';
17 export interface UseCloseHandlerParameters {
18 modelMessage: MessageState;
19 syncedMessage: MessageState;
21 ensureMessageContent: () => void;
22 uploadInProgress?: boolean;
23 promiseUpload?: Promise<void>;
24 pendingAutoSave: PromiseHandlers<void>;
25 autoSave: ((message: MessageState) => Promise<void>) & Cancellable;
26 saveNow: (message: MessageState) => Promise<void>;
28 onDiscard: () => void;
29 onMessageAlreadySent: () => void;
30 hasNetworkError: boolean;
33 export const useCloseHandler = ({
46 }: UseCloseHandlerParameters) => {
47 const { createNotification, hideNotification } = useNotifications();
48 const isMounted = useIsMounted();
49 const onCompose = useOnCompose();
51 // Indicates that the composer is saving a draft
52 const [saving, setSavingUnsafe] = useState(false);
54 // Manual save mostly used when closing, save state is not relevant then
55 const setSaving = (value: boolean) => {
57 setSavingUnsafe(value);
61 const notficationRef = useRef<SavingDraftNotificationAction>();
63 const handleManualSaveAfterUploads = useHandler(async (notificationID: number) => {
65 await saveNow(modelMessage);
66 notficationRef.current?.saved();
69 hideNotification(notificationID);
74 const handleManualSave = useHandler(async () => {
75 // Message already sent
76 if (syncedMessage.draftFlags?.isSentDraft) {
77 onMessageAlreadySent();
81 const notificationID = createNotification({
83 <SavingDraftNotification
87 hideNotification(notificationID);
94 showCloseButton: false,
98 ensureMessageContent();
102 } catch (error: any) {
103 hideNotification(notificationID);
108 // Split handlers to have the updated version of the message
109 await handleManualSaveAfterUploads(notificationID);
112 const handleClose = useHandler(async () => {
114 // Has to be done before the onClose call
115 // It needs to refer to composer components which will be disposed otherwise
116 ensureMessageContent();
119 // Closing the composer instantly, all the save process will be in background
122 if (syncedMessage.draftFlags?.isSentDraft) {
124 text: c('Error').t`This message has already been sent`,
131 // If the composer was locked, either it could have
132 // - failed at loading
133 // - still being created
134 // - still being loaded
135 // In all of those situation we don't need to save something, we can safely skip all the rest
139 // Message requires to be saved in background
140 if (pendingAutoSave.isPending || uploadInProgress || hasNetworkError) {
142 await handleManualSave();
145 text: c('Error').t`Draft could not be saved. Try again.`,
148 onCompose({ type: ComposeTypes.existingDraft, existingDraft: modelMessage, fromUndo: true });
153 return { handleClose, handleManualSave, saving };