Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / applications / drive / src / app / components / TransferManager / Header.tsx
blobd23d7ecbaba824fe718101a8b3e8d1611d3c902b
1 import { useEffect, useMemo, useRef, useState } from 'react';
3 import { c, msgid } from 'ttag';
5 import { Button } from '@proton/atoms';
6 import { Icon, Tooltip } from '@proton/components';
7 import clsx from '@proton/utils/clsx';
9 import {
10     calculateProgress,
11     isTransferActive,
12     isTransferCanceled,
13     isTransferDone,
14     isTransferError,
15     isTransferManuallyPaused,
16     isTransferSkipped,
17 } from '../../utils/transfer';
18 import type { Download, TransfersStats, Upload } from './transfer';
20 interface Props {
21     downloads: Download[];
22     uploads: Upload[];
23     stats: TransfersStats;
24     minimized: boolean;
25     onToggleMinimize: () => void;
26     onClose: () => void;
29 const Header = ({ downloads, uploads, stats, onClose, onToggleMinimize, minimized = false }: Props) => {
30     const [uploadsInSession, setUploadsInSession] = useState<Upload[]>([]);
31     const [downloadsInSession, setDownloadsInSession] = useState<Download[]>([]);
33     const minimizeRef = useRef<HTMLButtonElement>(null);
34     const transfers = useMemo(() => [...downloads, ...uploads], [uploads, downloads]);
36     const activeUploads = useMemo(() => uploads.filter(isTransferActive), [uploads]);
37     const activeDownloads = useMemo(() => downloads.filter(isTransferActive), [downloads]);
39     const doneUploads = useMemo(() => uploads.filter(isTransferDone), [uploads]);
40     const doneDownloads = useMemo(() => downloads.filter(isTransferDone), [downloads]);
42     const pausedTransfers = useMemo(() => transfers.filter(isTransferManuallyPaused), [transfers]);
43     const failedTransfers = useMemo(() => transfers.filter(isTransferError), [transfers]);
44     const canceledTransfers = useMemo(() => transfers.filter(isTransferCanceled), [transfers]);
45     const skippedTransfers = useMemo(() => transfers.filter(isTransferSkipped), [transfers]);
47     const activeUploadsCount = activeUploads.length;
48     const activeDownloadsCount = activeDownloads.length;
50     useEffect(() => {
51         if (activeUploadsCount) {
52             setUploadsInSession((uploadsInSession) => [
53                 ...doneUploads.filter((done) => uploadsInSession.some(({ id }) => id === done.id)),
54                 ...activeUploads,
55             ]);
56         } else {
57             setUploadsInSession([]);
58         }
59     }, [activeUploads, doneUploads, activeUploadsCount]);
61     useEffect(() => {
62         if (activeDownloadsCount) {
63             setDownloadsInSession((downloadsInSession) => [
64                 ...doneDownloads.filter((done) => downloadsInSession.some(({ id }) => id === done.id)),
65                 ...activeDownloads,
66             ]);
67         } else {
68             setDownloadsInSession([]);
69         }
70     }, [activeDownloads, activeDownloadsCount]);
72     const getHeadingText = () => {
73         const headingElements: string[] = [];
75         const activeCount = activeUploadsCount + activeDownloadsCount;
76         const doneUploadsCount = doneUploads.length;
77         const doneDownloadsCount = doneDownloads.length;
78         const doneCount = doneUploadsCount + doneDownloadsCount;
80         const errorCount = failedTransfers.length;
81         const canceledCount = canceledTransfers.length;
82         const skippedCount = skippedTransfers.length;
83         const pausedCount = pausedTransfers.length;
85         if (!activeCount) {
86             if (doneUploadsCount && doneDownloadsCount) {
87                 headingElements.push(
88                     c('Info').ngettext(msgid`${doneCount} finished`, `${doneCount} finished`, doneCount)
89                 );
90             } else {
91                 if (doneUploadsCount) {
92                     headingElements.push(
93                         c('Info').ngettext(
94                             msgid`${doneUploadsCount} uploaded`,
95                             `${doneUploadsCount} uploaded`,
96                             doneUploadsCount
97                         )
98                     );
99                 }
100                 if (doneDownloadsCount) {
101                     headingElements.push(
102                         c('Info').ngettext(
103                             msgid`${doneDownloadsCount} downloaded`,
104                             `${doneDownloadsCount} downloaded`,
105                             doneDownloadsCount
106                         )
107                     );
108                 }
109             }
110         }
112         if (activeUploadsCount) {
113             const uploadProgress = calculateProgress(stats, uploadsInSession);
114             headingElements.push(
115                 c('Info').ngettext(
116                     msgid`${activeUploadsCount} uploading (${uploadProgress}%)`,
117                     `${activeUploadsCount} uploading (${uploadProgress}%)`,
118                     activeUploadsCount
119                 )
120             );
121         }
122         if (activeDownloadsCount) {
123             if (downloadsInSession.some(({ meta: { size } }) => size === undefined)) {
124                 headingElements.push(
125                     c('Info').ngettext(
126                         msgid`${activeDownloadsCount} downloading`,
127                         `${activeDownloadsCount} downloading`,
128                         activeDownloadsCount
129                     )
130                 );
131             } else {
132                 const downloadProgress = calculateProgress(stats, downloadsInSession);
133                 headingElements.push(
134                     c('Info').ngettext(
135                         msgid`${activeDownloadsCount} downloading (${downloadProgress}%)`,
136                         `${activeDownloadsCount} downloading (${downloadProgress}%)`,
137                         activeDownloadsCount
138                     )
139                 );
140             }
141         }
143         if (pausedCount) {
144             headingElements.push(
145                 c('Info').ngettext(msgid`${pausedCount} paused`, `${pausedCount} paused`, pausedCount)
146             );
147         }
149         if (canceledCount) {
150             headingElements.push(
151                 c('Info').ngettext(msgid`${canceledCount} canceled`, `${canceledCount} canceled`, canceledCount)
152             );
153         }
155         if (skippedCount) {
156             headingElements.push(
157                 // translator: Shown in the transfer manager header - ex. "3 skipped"
158                 c('Info').ngettext(msgid`${skippedCount} skipped`, `${skippedCount} skipped`, skippedCount)
159             );
160         }
162         if (errorCount) {
163             headingElements.push(c('Info').ngettext(msgid`${errorCount} failed`, `${errorCount} failed`, errorCount));
164         }
166         return headingElements.join(', ');
167     };
169     const minMaxTitle = minimized ? c('Action').t`Maximize transfers` : c('Action').t`Minimize transfers`;
170     const closeTitle = c('Action').t`Close transfers`;
172     return (
173         <div className="transfers-manager-heading ui-prominent flex items-center flex-nowrap px-2">
174             <div
175                 role="presentation"
176                 className="flex-1 p-2"
177                 aria-atomic="true"
178                 aria-live="polite"
179                 data-testid="drive-transfers-manager:header"
180                 onClick={minimized ? onToggleMinimize : undefined}
181             >
182                 {getHeadingText()}
183             </div>
184             <Tooltip title={minMaxTitle}>
185                 <Button
186                     icon
187                     ref={minimizeRef}
188                     type="button"
189                     shape="ghost"
190                     onClick={() => {
191                         onToggleMinimize();
192                         minimizeRef.current?.blur();
193                     }}
194                     aria-expanded={!minimized}
195                     aria-controls="transfer-manager"
196                 >
197                     <Icon className={clsx(['m-auto', minimized && 'rotateX-180'])} name="low-dash" />
198                     <span className="sr-only">{minMaxTitle}</span>
199                 </Button>
200             </Tooltip>
201             <Tooltip title={closeTitle}>
202                 <Button icon type="button" shape="ghost" data-testid="drive-transfers-manager:close" onClick={onClose}>
203                     <Icon className="m-auto" name="cross" alt={closeTitle} />
204                 </Button>
205             </Tooltip>
206         </div>
207     );
210 export default Header;