Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / components / containers / autoReply / AutoReplySection.tsx
blob84fddefd8a573737030db9a2df64d47fdf5627b1
1 import { useRef, useState } from 'react';
3 import { c } from 'ttag';
5 import { useUser } from '@proton/account/user/hooks';
6 import { Button } from '@proton/atoms';
7 import Editor from '@proton/components/components/editor/Editor';
8 import { useToolbar } from '@proton/components/components/editor/hooks/useToolbar';
9 import type { EditorActions } from '@proton/components/components/editor/interface';
10 import Toggle from '@proton/components/components/toggle/Toggle';
11 import SettingsLayout from '@proton/components/containers/account/SettingsLayout';
12 import SettingsLayoutLeft from '@proton/components/containers/account/SettingsLayoutLeft';
13 import SettingsLayoutRight from '@proton/components/containers/account/SettingsLayoutRight';
14 import SettingsParagraph from '@proton/components/containers/account/SettingsParagraph';
15 import SettingsSectionWide from '@proton/components/containers/account/SettingsSectionWide';
16 import useApi from '@proton/components/hooks/useApi';
17 import useErrorHandler from '@proton/components/hooks/useErrorHandler';
18 import useEventManager from '@proton/components/hooks/useEventManager';
19 import useNotifications from '@proton/components/hooks/useNotifications';
20 import { useLoading } from '@proton/hooks';
21 import { useMailSettings } from '@proton/mail/mailSettings/hooks';
22 import { PLANS, PLAN_NAMES } from '@proton/payments';
23 import { updateAutoresponder } from '@proton/shared/lib/api/mailSettings';
24 import {
25     APP_UPSELL_REF_PATH,
26     AutoReplyDuration,
27     BRAND_NAME,
28     MAIL_UPSELL_PATHS,
29     UPSELL_COMPONENT,
30 } from '@proton/shared/lib/constants';
31 import { getUpsellRef } from '@proton/shared/lib/helpers/upsell';
32 import { DEFAULT_MAILSETTINGS } from '@proton/shared/lib/mail/mailSettings';
33 import { removeImagesFromContent } from '@proton/shared/lib/sanitize/purify';
35 import { useHotkeys } from '../../hooks/useHotkeys';
36 import UpgradeBanner from '../account/UpgradeBanner';
37 import AutoReplyFormDaily from './AutoReplyForm/AutoReplyFormDaily';
38 import AutoReplyFormFixed from './AutoReplyForm/AutoReplyFormFixed';
39 import AutoReplyFormMonthly from './AutoReplyForm/AutoReplyFormMonthly';
40 import AutoReplyFormPermanent from './AutoReplyForm/AutoReplyFormPermanent';
41 import AutoReplyFormWeekly from './AutoReplyForm/AutoReplyFormWeekly';
42 import DurationField from './AutoReplyForm/fields/DurationField';
43 import useAutoReplyForm, { getDefaultAutoResponder } from './AutoReplyForm/useAutoReplyForm';
45 const AUTO_REPLY_MAX_LENGTH = 4096;
47 export const AutoReplySection = () => {
48     const errorHandler = useErrorHandler();
49     const [{ hasPaidMail }] = useUser();
50     const [mailSettings] = useMailSettings();
51     const { Shortcuts } = mailSettings || DEFAULT_MAILSETTINGS;
52     const AutoResponder = mailSettings?.AutoResponder || getDefaultAutoResponder();
53     const api = useApi();
54     const { call } = useEventManager();
55     const [enablingLoading, withEnablingLoading] = useLoading();
56     const [updatingLoading, withUpdatingLoading] = useLoading();
57     const [isEnabled, setIsEnabled] = useState(AutoResponder.IsEnabled);
58     const { createNotification } = useNotifications();
59     const { model, updateModel, toAutoResponder } = useAutoReplyForm(AutoResponder);
61     const editorActionsRef = useRef<EditorActions>();
62     const composerRef = useRef<HTMLDivElement>(null);
64     const messageLimitReached = model.message.length > AUTO_REPLY_MAX_LENGTH;
66     const handleToggle = async (enable: boolean) => {
67         if (!hasPaidMail) {
68             const error: any = new Error(
69                 c('Error').t`Automatic replies is a paid feature. Please upgrade to a paid account to use this feature.`
70             );
71             error.trace = false;
72             throw error;
73         }
75         setIsEnabled(enable);
77         const isDisablingExistingAutoResponder =
78             !enable && mailSettings?.AutoResponder && mailSettings?.AutoResponder.IsEnabled;
80         if (enable || !isDisablingExistingAutoResponder) {
81             return;
82         }
84         await api({
85             ...updateAutoresponder({ ...AutoResponder, IsEnabled: enable }),
86             silence: true,
87         });
88         await call();
89         createNotification({
90             text: c('Success').t`Auto-reply disabled`,
91         });
92     };
94     const handleSubmit = async () => {
95         // Remove images from the composer in autoreply
96         const { message, containsImages } = removeImagesFromContent(model.message);
97         if (containsImages) {
98             createNotification({
99                 type: 'warning',
100                 text: c('Info').t`Images have been removed because they are not allowed in auto-reply`,
101             });
103             updateModel('message').bind(message);
104             // update the composer to remove the image from it
105             if (editorActionsRef.current) {
106                 editorActionsRef.current.setContent(model.message);
107             }
108         }
110         await api(updateAutoresponder(toAutoResponder({ ...model, message })));
111         await call();
112         createNotification({ text: c('Success').t`Auto-reply updated` });
113     };
115     const handleEditorReady = (actions: EditorActions) => {
116         editorActionsRef.current = actions;
117         actions.setContent(model.message);
118     };
120     const formRenderer = (duration: AutoReplyDuration) => {
121         switch (duration) {
122             case AutoReplyDuration.FIXED:
123                 return <AutoReplyFormFixed model={model} updateModel={updateModel} />;
124             case AutoReplyDuration.DAILY:
125                 return <AutoReplyFormDaily model={model} updateModel={updateModel} />;
126             case AutoReplyDuration.MONTHLY:
127                 return <AutoReplyFormMonthly model={model} updateModel={updateModel} />;
128             case AutoReplyDuration.WEEKLY:
129                 return <AutoReplyFormWeekly model={model} updateModel={updateModel} />;
130             case AutoReplyDuration.PERMANENT:
131                 return <AutoReplyFormPermanent />;
132             default:
133                 return null;
134         }
135     };
137     useHotkeys(composerRef, [
138         [
139             ['Meta', 'Enter'],
140             async () => {
141                 if (Shortcuts) {
142                     await withUpdatingLoading(handleSubmit());
143                 }
144             },
145         ],
146     ]);
148     const { openEmojiPickerRef, toolbarConfig, setToolbarConfig, modalLink, modalImage, modalDefaultFont } = useToolbar(
149         {}
150     );
152     const plus = PLAN_NAMES[PLANS.MAIL];
153     const bundle = PLAN_NAMES[PLANS.BUNDLE];
155     const upsellRef = getUpsellRef({
156         app: APP_UPSELL_REF_PATH.MAIL_UPSELL_REF_PATH,
157         component: UPSELL_COMPONENT.BANNER,
158         feature: MAIL_UPSELL_PATHS.AUTO_REPLY,
159     });
161     return (
162         <SettingsSectionWide className="overflow-hidden">
163             <SettingsParagraph className="mt-0 mb-4">
164                 {c('new_plans: info')
165                     .t`Set automatic replies to inform senders you are out of the office or unable to respond.`}
166             </SettingsParagraph>
168             <SettingsLayout>
169                 <SettingsLayoutLeft>
170                     <label htmlFor="autoReplyToggle" className="text-semibold">
171                         {c('Label').t`Auto-reply`}
172                     </label>
173                 </SettingsLayoutLeft>
174                 <SettingsLayoutRight isToggleContainer>
175                     <Toggle
176                         id="autoReplyToggle"
177                         loading={enablingLoading}
178                         checked={isEnabled}
179                         onChange={({ target: { checked } }) =>
180                             withEnablingLoading(handleToggle(checked).catch(errorHandler))
181                         }
182                     />
183                 </SettingsLayoutRight>
184             </SettingsLayout>
186             {hasPaidMail ? (
187                 isEnabled && (
188                     <form
189                         onSubmit={async (e) => {
190                             e.preventDefault();
191                             await withUpdatingLoading(handleSubmit());
192                         }}
193                     >
194                         <DurationField value={model.duration} onChange={updateModel('duration')} />
196                         {formRenderer(model.duration)}
198                         <SettingsLayout>
199                             <SettingsLayoutLeft>
200                                 <label className="text-semibold" onClick={() => editorActionsRef.current?.focus()}>
201                                     {c('Label').t`Message`}
202                                 </label>
203                             </SettingsLayoutLeft>
204                             <SettingsLayoutRight>
205                                 <div ref={composerRef} tabIndex={-1} className="w-full">
206                                     <Editor
207                                         metadata={{ supportImages: false }}
208                                         onReady={handleEditorReady}
209                                         onChange={updateModel('message')}
210                                         simple
211                                         openEmojiPickerRef={openEmojiPickerRef}
212                                         toolbarConfig={toolbarConfig}
213                                         setToolbarConfig={setToolbarConfig}
214                                         modalLink={modalLink}
215                                         modalImage={modalImage}
216                                         modalDefaultFont={modalDefaultFont}
217                                         mailSettings={mailSettings}
218                                     />
219                                 </div>
220                                 {messageLimitReached && (
221                                     <p className="mt-1 mb-0 color-danger">{c('Error')
222                                         .t`Auto-reply message exceeds the allowed length. Please shorten it to fit within the limit.`}</p>
223                                 )}
225                                 <Button
226                                     color="norm"
227                                     type="submit"
228                                     disabled={updatingLoading || messageLimitReached}
229                                     loading={updatingLoading}
230                                     className="mt-4"
231                                 >
232                                     {c('Action').t`Save`}
233                                 </Button>
234                             </SettingsLayoutRight>
235                         </SettingsLayout>
236                     </form>
237                 )
238             ) : (
239                 <UpgradeBanner className="mt-8" upsellPath={upsellRef}>
240                     {c('new_plans: upgrade').t`Included with ${plus}, ${bundle}, and ${BRAND_NAME} for Business.`}
241                 </UpgradeBanner>
242             )}
243         </SettingsSectionWide>
244     );