Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / drive / src / app / components / modals / UpsellFloatingModal / UpsellFloatingModal.tsx
blob208bd99750e38c34303a923a4d68bcc37b1f6159
1 import { useEffect, useLayoutEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { Button, ButtonLike } from '@proton/atoms';
6 import type { ModalProps } from '@proton/components';
7 import { DriveLogo, Icon, Tooltip, useActiveBreakpoint, useModalTwoStatic } from '@proton/components';
8 import Dialog from '@proton/components/components/dialog/Dialog';
9 import { Portal } from '@proton/components/components/portal';
10 import usePrevious from '@proton/hooks/usePrevious';
11 import { modalTwoRootClassName } from '@proton/shared/lib/busy';
12 import { DRIVE_APP_NAME } from '@proton/shared/lib/constants';
13 import { DRIVE_SIGNUP } from '@proton/shared/lib/drive/urls';
14 import clsx from '@proton/utils/clsx';
16 import { useDownload, usePublicSessionUser } from '../../../store';
18 import './UpsellFloatingModal.scss';
20 interface ChildProps {
21     open: boolean;
22     onClose: () => void;
23     onExit: () => void;
26 const UpsellFloatingModalContent = ({ onClose }: Pick<ChildProps, 'onClose'>) => {
27     return (
28         <div className="upsell-floating-modal-container relative">
29             <Tooltip
30                 className="upsell-floating-modal-tooltip absolute right-0 top-0 mr-1"
31                 title={c('Action').t`Close`}
32                 onClick={onClose}
33             >
34                 <Button className="shrink-0" icon shape="ghost" data-testid="upsell-floating-modal:close">
35                     <Icon className="modal-close-icon" name="cross-big" alt={c('Action').t`Close`} />
36                 </Button>
37             </Tooltip>
38             <div className="py-3 px-4">
39                 <div className="flex flex-nowrap items-center gap-2">
40                     <DriveLogo variant="glyph-only" />
41                     <div className="flex-1">
42                         <h4 className="text-bold">{c('Info').t`Try ${DRIVE_APP_NAME}: Free forever`}</h4>
43                         <div className="flex flex-nowrap items-center gap-6">
44                             <p className="m-0 mt-1 flex-1 max-w-custom" style={{ '--max-w-custom': '12.5em' }}>
45                                 {c('Info').t`With ${DRIVE_APP_NAME} your data is protected by end-to-end encryption.`}
46                             </p>
47                             <ButtonLike as="a" href={DRIVE_SIGNUP} target="_blank" color="norm" shape="outline">{c(
48                                 'Action'
49                             ).t`Get Started`}</ButtonLike>
50                         </div>
51                     </div>
52                 </div>
53             </div>
54         </div>
55     );
58 enum ExitState {
59     idle,
60     exiting,
61     exited,
64 const UpsellFloatingModal = ({ open, onClose }: ChildProps & ModalProps) => {
65     const [exit, setExit] = useState(() => (open ? ExitState.idle : ExitState.exited));
66     const active = exit !== ExitState.exited;
67     const previousOpen = usePrevious(open);
68     const { hasDownloads } = useDownload();
70     // Hide Upsell if download is in progress
71     useEffect(() => {
72         if (hasDownloads) {
73             onClose();
74             setExit(ExitState.exited);
75         }
76     }, [onClose, hasDownloads]);
78     useLayoutEffect(() => {
79         if (!previousOpen && open) {
80             setExit(ExitState.idle);
81         } else if (previousOpen && !open) {
82             setExit(ExitState.exiting);
83         }
84     }, [previousOpen, open, active]);
86     if (!active) {
87         return null;
88     }
90     const exiting = exit === ExitState.exiting;
91     return (
92         <Portal>
93             <div
94                 className={clsx(
95                     modalTwoRootClassName,
96                     'upsell-floating-modal mr-4 mb-2 p-0',
97                     exiting && 'modal-two--out'
98                 )}
99                 onAnimationEnd={({ animationName }) => {
100                     if (exiting && animationName === 'anime-modal-two-out') {
101                         setExit(ExitState.exited);
102                     }
103                 }}
104                 data-testid="upsell-floating-modal"
105             >
106                 <Dialog className="modal-two-dialog upsell-floating-modal-dialog border border-primary rounded">
107                     <div className="modal-two-dialog-container">
108                         <UpsellFloatingModalContent onClose={onClose} />
109                     </div>
110                 </Dialog>
111             </div>
112         </Portal>
113     );
116 export default UpsellFloatingModal;
118 export const useUpsellFloatingModal = () => {
119     const user = usePublicSessionUser();
120     const [renderUpsellFloatingModal, showUpsellFloatingModal] = useModalTwoStatic(UpsellFloatingModal);
122     const { viewportWidth } = useActiveBreakpoint();
124     // If user is proton user or on mobile we disable upsell modal
125     const hideModal = viewportWidth['<=small'] || !!user;
127     useEffect(() => {
128         if (hideModal) {
129             return;
130         }
131         showUpsellFloatingModal({});
132     }, [hideModal, showUpsellFloatingModal]);
134     return [hideModal ? null : renderUpsellFloatingModal, showUpsellFloatingModal] as const;