Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / applications / drive / src / app / components / sections / FileBrowser / contentCells.tsx
blob66f786d669d00807d994748572c6541d9c2ff39d
1 import { c } from 'ttag';
3 import { Avatar, Button } from '@proton/atoms';
4 import { FileIcon, Icon, TableCell, useActiveBreakpoint, useConfirmActionModal } from '@proton/components';
5 import { useContactEmails } from '@proton/mail/contactEmails/hooks';
6 import { getInitials } from '@proton/shared/lib/helpers/string';
7 import clsx from '@proton/utils/clsx';
9 import { useActiveShare } from '../../../hooks/drive/useActiveShare';
10 import { useDriveSharingFlags, useInvitationsActions } from '../../../store';
11 import { formatAccessCount } from '../../../utils/formatters';
12 import { Cells } from '../../FileBrowser';
13 import SignatureIcon from '../../SignatureIcon';
14 import type { DeviceItem } from '../Devices/Devices';
15 import type { DriveItem } from '../Drive/Drive';
16 import type { SharedLinkItem } from '../SharedLinks/SharedLinks';
17 import type { SharedWithMeItem } from '../SharedWithMe/SharedWithMe';
18 import type { TrashItem } from '../Trash/Trash';
19 import CopyLinkIcon from './CopyLinkIcon';
20 import ShareIcon from './ShareIcon';
21 import { getDeviceIconText, getLinkIconText } from './utils';
23 const { LocationCell: LocationCellBase, SizeCell: SizeCellBase, NameCell: NameCellBase, TimeCell } = Cells;
25 export const NameCell = ({ item }: { item: DriveItem | SharedLinkItem | SharedWithMeItem | TrashItem }) => {
26     const iconText = getLinkIconText({
27         linkName: item.name,
28         mimeType: item.mimeType,
29         isFile: item.isFile,
30     });
32     return (
33         <TableCell className="m-0 flex items-center flex-nowrap flex-1" data-testid="column-name">
34             {item.cachedThumbnailUrl ? (
35                 <img
36                     src={item.cachedThumbnailUrl}
37                     alt={iconText}
38                     className="file-browser-list-item--thumbnail shrink-0 mr-2"
39                 />
40             ) : (
41                 <FileIcon
42                     mimeType={item.isFile ? item.mimeType : 'Folder'}
43                     alt={iconText}
44                     className="file-browser-list-item--icon mr-2"
45                 />
46             )}
47             <SignatureIcon signatureIssues={item.signatureIssues} isFile={item.isFile} className="mr-2 shrink-0" />
48             <NameCellBase name={item.name} />
49         </TableCell>
50     );
53 export const DeviceNameCell = ({ item }: { item: DeviceItem }) => {
54     const iconText = getDeviceIconText(item.name);
56     return (
57         <TableCell
58             className="m-0 flex items-center flex-nowrap flex-1 filebrowser-list-device-name-cell"
59             data-testid="column-name"
60         >
61             <Icon name="tv" alt={iconText} className="mr-2" />
62             <NameCellBase name={item.name} />
63         </TableCell>
64     );
67 export const ModifiedCell = ({ item }: { item: DriveItem }) => {
68     return (
69         <TableCell className="flex items-center m-0 w-1/6" data-testid="column-modified">
70             <TimeCell time={item.fileModifyTime} />
71         </TableCell>
72     );
75 export const ModifiedCellDevice = ({ item }: { item: DeviceItem }) => {
76     return (
77         <TableCell className="flex items-center m-0 w-1/6" data-testid="column-modified">
78             <TimeCell time={item.modificationTime} />
79         </TableCell>
80     );
83 export function SizeCell({ item }: { item: DriveItem | TrashItem }) {
84     const { viewportWidth } = useActiveBreakpoint();
85     return (
86         <TableCell
87             className={clsx(['flex items-center m-0', viewportWidth['>=large'] ? 'w-1/10' : 'w-1/6'])}
88             data-testid="column-size"
89         >
90             {item.isFile ? <SizeCellBase size={item.size} /> : '-'}
91         </TableCell>
92     );
95 export const DeletedCell = ({ item }: { item: TrashItem }) => {
96     return (
97         <TableCell className="flex items-center m-0 w-1/4" data-testid="column-trashed">
98             <TimeCell time={item.trashed || item.fileModifyTime} />
99         </TableCell>
100     );
103 export const CreatedCell = ({ item }: { item: TrashItem | SharedLinkItem }) => {
104     const time = item.shareUrl?.createTime || item.sharedOn;
105     return (
106         <TableCell className="flex items-center m-0 w-1/6" data-testid="column-share-created">
107             {time && <TimeCell time={time} />}
108         </TableCell>
109     );
112 export const LocationCell = ({ item }: { item: TrashItem | SharedLinkItem }) => {
113     const { viewportWidth } = useActiveBreakpoint();
114     const shareId = item.rootShareId;
116     return (
117         <TableCell
118             className={`flex items-center ${clsx(['m-0', viewportWidth['>=large'] ? 'w-1/5' : 'w-1/4'])}`}
119             data-testid="column-location"
120         >
121             <LocationCellBase shareId={shareId} parentLinkId={item.parentLinkId} />
122         </TableCell>
123     );
126 export const AccessCountCell = ({ item }: { item: TrashItem }) => {
127     return (
128         <TableCell className="flex items-center m-0 w-1/6" data-testid="column-num-accesses">
129             {formatAccessCount(item.shareUrl?.numAccesses)}
130         </TableCell>
131     );
134 export const ExpirationCell = ({ item }: { item: TrashItem }) => {
135     const { viewportWidth } = useActiveBreakpoint();
137     const expiredPart = viewportWidth['>=large'] ? (
138         <span className="ml-1">{c('Label').t`(Expired)`}</span>
139     ) : (
140         <span>{c('Label').t`Expired`}</span>
141     );
143     let expiration;
144     if (item.shareUrl) {
145         expiration = item.shareUrl.expireTime ? (
146             <div className="flex flex-nowrap">
147                 {(viewportWidth['>=large'] || !item.shareUrl.isExpired) && <TimeCell time={item.shareUrl.expireTime} />}
148                 {item.shareUrl.isExpired ? expiredPart : null}
149             </div>
150         ) : (
151             c('Label').t`Never`
152         );
153     }
155     return (
156         <TableCell className="flex items-center m-0 w-1/5" data-testid="column-share-expires">
157             {expiration}
158         </TableCell>
159     );
162 export const ShareOptionsCell = ({ item }: { item: DriveItem }) => {
163     const { activeShareId } = useActiveShare();
164     const { isSharingInviteAvailable } = useDriveSharingFlags();
166     return (
167         <TableCell
168             className="m-0 file-browser-list--icon-column file-browser-list--context-menu-column flex items-center"
169             data-testid="column-share-options"
170         >
171             {isSharingInviteAvailable
172                 ? item.isShared &&
173                   item.showLinkSharingModal && (
174                       <ShareIcon
175                           shareId={activeShareId}
176                           linkId={item.linkId}
177                           trashed={item.trashed}
178                           showLinkSharingModal={item.showLinkSharingModal}
179                           isAdmin={item.isAdmin}
180                       />
181                   )
182                 : item.shareUrl && (
183                       <CopyLinkIcon
184                           shareId={activeShareId}
185                           linkId={item.linkId}
186                           isExpired={Boolean(item.shareUrl?.isExpired)}
187                           trashed={item.trashed}
188                       />
189                   )}
190         </TableCell>
191     );
194 export const SharedByCell = ({ item }: { item: SharedWithMeItem }) => {
195     const [contactEmails] = useContactEmails();
197     if (item.isBookmark) {
198         return (
199             <TableCell className="flex flex-nowrap items-center m-0 w-1/5" data-testid="column-shared-by">
200                 <>
201                     <Avatar
202                         color="weak"
203                         className="mr-2 min-w-custom max-w-custom max-h-custom"
204                         style={{
205                             '--min-w-custom': '1.75rem',
206                             '--max-w-custom': '1.75rem',
207                             '--max-h-custom': '1.75rem',
208                         }}
209                     >
210                         <Icon className="color-weak" name="globe" />
211                     </Avatar>
212                     <span className="text-ellipsis color-weak">{c('Info').t`Public link`}</span>
213                 </>
214             </TableCell>
215         );
216     }
217     const email = item.sharedBy;
218     const contactEmail = contactEmails?.find((contactEmail) => contactEmail.Email === email);
219     const displayName = email && contactEmails && contactEmail ? contactEmail.Name : email;
220     return (
221         <TableCell className="flex flex-nowrap items-center m-0 w-1/5" data-testid="column-shared-by">
222             {displayName && (
223                 <>
224                     <Avatar
225                         color="weak"
226                         className="mr-2 min-w-custom max-w-custom max-h-custom"
227                         style={{
228                             '--min-w-custom': '1.75rem',
229                             '--max-w-custom': '1.75rem',
230                             '--max-h-custom': '1.75rem',
231                         }}
232                     >
233                         {getInitials(displayName)}
234                     </Avatar>
235                     <span className="text-ellipsis">{displayName}</span>
236                 </>
237             )}
238         </TableCell>
239     );
242 export const SharedOnCell = ({ item }: { item: SharedWithMeItem }) => {
243     const time = item.bookmarkDetails?.createTime || item.sharedOn;
244     return (
245         <TableCell className="flex items-center m-0 w-1/6" data-testid="column-share-created">
246             {time && <TimeCell time={time} />}
247         </TableCell>
248     );
251 export const AcceptOrRejectInviteCell = ({ item }: { item: SharedWithMeItem }) => {
252     const { acceptInvitation, rejectInvitation } = useInvitationsActions();
253     const [confirmModal, showConfirmModal] = useConfirmActionModal();
254     const invitationDetails = item.invitationDetails;
255     return (
256         <>
257             <TableCell
258                 className="flex flex-nowrap items-center m-0 file-browser-list-item--accept-decline-cell"
259                 data-testid="column-share-accept-reject"
260             >
261                 {invitationDetails && (
262                     <div className="flex flex-nowrap">
263                         <Button
264                             loading={invitationDetails.isLocked}
265                             disabled={invitationDetails.isLocked}
266                             className="text-ellipsis"
267                             color="norm"
268                             shape="ghost"
269                             size="small"
270                             data-testid="share-accept-button"
271                             onClick={async (e) => {
272                                 e.stopPropagation();
273                                 await acceptInvitation(
274                                     new AbortController().signal,
275                                     invitationDetails.invitation.invitationId
276                                 );
277                             }}
278                         >
279                             <span className="file-browser-list-item--accept-decline-text">{c('Action').t`Accept`}</span>
280                         </Button>
281                         {!invitationDetails.isLocked && (
282                             <>
283                                 <Button
284                                     className="text-ellipsis file-browser-list-item--decline"
285                                     color="norm"
286                                     shape="ghost"
287                                     size="small"
288                                     data-testid="share-decline-button"
289                                     onClick={(e) => {
290                                         e.stopPropagation();
291                                         void rejectInvitation(new AbortController().signal, {
292                                             showConfirmModal,
293                                             invitationId: invitationDetails.invitation.invitationId,
294                                         });
295                                     }}
296                                 >
297                                     <span className="file-browser-list-item--accept-decline-text">{c('Action')
298                                         .t`Decline`}</span>
299                                 </Button>
300                             </>
301                         )}
302                     </div>
303                 )}
304             </TableCell>
305             {confirmModal}
306         </>
307     );