Merge branch 'INDA-330-pii-update' into 'main'
[ProtonMail-WebClient.git] / applications / drive / src / app / components / sections / SharedLinks / SharedLinks.tsx
blobfbab21f9c19b570191002b05250d6028dd1d5e08
1 import { useCallback, useMemo, useRef } from 'react';
3 import { c } from 'ttag';
5 import { useActiveBreakpoint } from '@proton/components';
6 import { isProtonDocument } from '@proton/shared/lib/helpers/mimetype';
8 import useNavigate from '../../../hooks/drive/useNavigate';
9 import { useOnItemRenderedMetrics } from '../../../hooks/drive/useOnItemRenderedMetrics';
10 import type { EncryptedLink, LinkShareUrl, useSharedLinksView } from '../../../store';
11 import { useThumbnailsDownload } from '../../../store';
12 import { useDocumentActions, useDriveDocsFeatureFlag } from '../../../store/_documents';
13 import { SortField } from '../../../store/_views/utils/useSorting';
14 import { sendErrorReport } from '../../../utils/errorHandling';
15 import type { BrowserItemId, FileBrowserBaseItem, ListViewHeaderItem } from '../../FileBrowser';
16 import FileBrowser, { Cells, GridHeader, useItemContextMenu, useSelection } from '../../FileBrowser';
17 import { GridViewItem } from '../FileBrowser/GridViewItemLink';
18 import { AccessCountCell, CreatedCell, ExpirationCell, LocationCell, NameCell } from '../FileBrowser/contentCells';
19 import headerItems from '../FileBrowser/headerCells';
20 import { translateSortField } from '../SortDropdown';
21 import { getSelectedItems } from '../helpers';
22 import EmptyShared from './EmptyShared';
23 import { SharedLinksItemContextMenu } from './SharedLinksItemContextMenu';
25 export interface SharedLinkItem extends FileBrowserBaseItem {
26     activeRevision?: EncryptedLink['activeRevision'];
27     cachedThumbnailUrl?: string;
28     hasThumbnail: boolean;
29     isFile: boolean;
30     mimeType: string;
31     fileModifyTime: number;
32     name: string;
33     shareUrl?: LinkShareUrl;
34     signatureIssues?: any;
35     size: number;
36     trashed: number | null;
37     parentLinkId: string;
38     rootShareId: string;
39     sharedOn?: number;
42 type Props = {
43     shareId: string;
44     sharedLinksView: ReturnType<typeof useSharedLinksView>;
47 const { CheckboxCell, ContextMenuCell } = Cells;
49 const largeScreenCells: React.FC<{ item: SharedLinkItem }>[] = [
50     CheckboxCell,
51     NameCell,
52     LocationCell,
53     CreatedCell,
54     AccessCountCell,
55     ExpirationCell,
56     ContextMenuCell,
58 const smallScreenCells = [CheckboxCell, NameCell, LocationCell, ExpirationCell, ContextMenuCell];
60 const headerItemsLargeScreen: ListViewHeaderItem[] = [
61     headerItems.checkbox,
62     headerItems.name,
63     headerItems.location,
64     headerItems.creationDate,
65     headerItems.accessCount,
66     headerItems.expirationDate,
67     headerItems.placeholder,
70 const headerItemsSmallScreen: ListViewHeaderItem[] = [
71     headerItems.checkbox,
72     headerItems.name,
73     headerItems.location,
74     headerItems.expirationDate,
75     headerItems.placeholder,
77 type SharedLinksSortFields = Extract<
78     SortField,
79     SortField.name | SortField.linkCreateTime | SortField.linkExpireTime | SortField.numAccesses
81 const SORT_FIELDS: SharedLinksSortFields[] = [
82     SortField.name,
83     SortField.linkCreateTime,
84     SortField.linkExpireTime,
85     SortField.numAccesses,
88 const SharedLinks = ({ shareId, sharedLinksView }: Props) => {
89     const contextMenuAnchorRef = useRef<HTMLDivElement>(null);
91     const { navigateToLink } = useNavigate();
92     const browserItemContextMenu = useItemContextMenu();
93     const thumbnails = useThumbnailsDownload();
94     const selectionControls = useSelection();
95     const { viewportWidth } = useActiveBreakpoint();
96     const { openDocument } = useDocumentActions();
97     const { canUseDocs } = useDriveDocsFeatureFlag();
98     const { incrementItemRenderedCounter } = useOnItemRenderedMetrics(
99         sharedLinksView.layout,
100         sharedLinksView.isLoading
101     );
102     const { layout, items, sortParams, setSorting, isLoading } = sharedLinksView;
104     const selectedItems = useMemo(
105         () => getSelectedItems(items, selectionControls!.selectedItemIds),
106         [items, selectionControls!.selectedItemIds]
107     );
109     const browserItems: SharedLinkItem[] = items.map((item) => ({ ...item, id: item.linkId }));
111     const handleClick = useCallback(
112         (id: BrowserItemId) => {
113             const item = browserItems.find((item) => item.id === id);
115             if (!item) {
116                 return;
117             }
118             document.getSelection()?.removeAllRanges();
120             if (isProtonDocument(item.mimeType)) {
121                 void canUseDocs(item.rootShareId)
122                     .then((canUse) => {
123                         if (!canUse) {
124                             return;
125                         }
127                         return openDocument({
128                             linkId: item.linkId,
129                             shareId: item.rootShareId,
130                             openBehavior: 'tab',
131                         });
132                     })
133                     .catch(sendErrorReport);
134                 return;
135             }
136             navigateToLink(item.rootShareId, item.linkId, item.isFile);
137         },
138         [navigateToLink, browserItems]
139     );
141     const handleItemRender = useCallback(
142         (item: SharedLinkItem) => {
143             incrementItemRenderedCounter();
144             if (item.hasThumbnail && item.activeRevision && !item.cachedThumbnailUrl) {
145                 thumbnails.addToDownloadQueue(item.rootShareId, item.linkId, item.activeRevision.id);
146             }
147         },
148         [thumbnails, incrementItemRenderedCounter]
149     );
151     /* eslint-disable react/display-name */
152     const GridHeaderComponent = useMemo(
153         () =>
154             ({ scrollAreaRef }: { scrollAreaRef: React.RefObject<HTMLDivElement> }) => {
155                 const activeSortingText = translateSortField(sortParams.sortField);
156                 return (
157                     <GridHeader
158                         isLoading={isLoading}
159                         sortFields={SORT_FIELDS}
160                         onSort={setSorting}
161                         sortField={sortParams.sortField}
162                         sortOrder={sortParams.sortOrder}
163                         itemCount={browserItems.length}
164                         scrollAreaRef={scrollAreaRef}
165                         activeSortingText={activeSortingText}
166                     />
167                 );
168             },
169         [sortParams.sortField, sortParams.sortOrder, isLoading]
170     );
172     if (!items.length && !isLoading) {
173         return <EmptyShared shareId={shareId} />;
174     }
176     const Cells = viewportWidth['>=large'] ? largeScreenCells : smallScreenCells;
177     const headerItems = viewportWidth['>=large'] ? headerItemsLargeScreen : headerItemsSmallScreen;
179     return (
180         <>
181             <SharedLinksItemContextMenu
182                 selectedLinks={selectedItems}
183                 anchorRef={contextMenuAnchorRef}
184                 close={browserItemContextMenu.close}
185                 isOpen={browserItemContextMenu.isOpen}
186                 open={browserItemContextMenu.open}
187                 position={browserItemContextMenu.position}
188             />
189             <FileBrowser
190                 caption={c('Title').t`Shared`}
191                 items={browserItems}
192                 headerItems={headerItems}
193                 layout={layout}
194                 loading={isLoading}
195                 sortParams={sortParams}
196                 Cells={Cells}
197                 GridHeaderComponent={GridHeaderComponent}
198                 GridViewItem={GridViewItem}
199                 contextMenuAnchorRef={contextMenuAnchorRef}
200                 onItemContextMenu={browserItemContextMenu.handleContextMenu}
201                 onItemOpen={handleClick}
202                 onItemRender={handleItemRender}
203                 onSort={setSorting}
204                 onScroll={browserItemContextMenu.close}
205             />
206         </>
207     );
210 export default SharedLinks;