Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / drive / src / app / components / sections / SharedWithMe / SharedWithMe.tsx
blob5c7e996bce1672185906e62fb2e0a72f882a480d
1 import { useCallback, useMemo, useRef } from 'react';
3 import { c } from 'ttag';
5 import { ContactEmailsProvider, 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 {
11     type EncryptedLink,
12     type ExtendedInvitationDetails,
13     type SignatureIssues,
14     useBookmarksActions,
15     type useSharedWithMeView,
16     useThumbnailsDownload,
17 } from '../../../store';
18 import { useDocumentActions, useDriveDocsFeatureFlag } from '../../../store/_documents';
19 import { SortField } from '../../../store/_views/utils/useSorting';
20 import { sendErrorReport } from '../../../utils/errorHandling';
21 import FileBrowser, {
22     type BrowserItemId,
23     Cells,
24     type FileBrowserBaseItem,
25     GridHeader,
26     type ListViewHeaderItem,
27     useItemContextMenu,
28     useSelection,
29 } from '../../FileBrowser';
30 import { GridViewItem } from '../FileBrowser/GridViewItemLink';
31 import { AcceptOrRejectInviteCell, NameCell, SharedByCell, SharedOnCell } from '../FileBrowser/contentCells';
32 import headerItems from '../FileBrowser/headerCells';
33 import { translateSortField } from '../SortDropdown';
34 import { getSelectedSharedWithMeItems } from '../helpers';
35 import EmptySharedWithMe from './EmptySharedWithMe';
36 import { SharedWithMeContextMenu } from './SharedWithMeItemContextMenu';
38 export interface SharedWithMeItem extends FileBrowserBaseItem {
39     activeRevision?: EncryptedLink['activeRevision'];
40     cachedThumbnailUrl?: string;
41     hasThumbnail?: boolean;
42     isFile: boolean;
43     mimeType: string;
44     name: string;
45     signatureIssues?: SignatureIssues;
46     signatureAddress?: string;
47     size: number;
48     trashed: number | null;
49     rootShareId: string;
50     volumeId: string;
51     sharedOn?: number;
52     sharedBy?: string;
53     parentLinkId: string;
54     invitationDetails?: ExtendedInvitationDetails;
55     bookmarkDetails?: { token: string; createTime: number; urlPassword: string };
58 type Props = {
59     shareId: string;
60     sharedWithMeView: ReturnType<typeof useSharedWithMeView>;
63 const { CheckboxCell, ContextMenuCell } = Cells;
65 const largeScreenCells: React.FC<{ item: SharedWithMeItem }>[] = [
66     CheckboxCell,
67     NameCell,
68     SharedByCell,
69     ({ item }) => (item.isInvitation ? <AcceptOrRejectInviteCell item={item} /> : <SharedOnCell item={item} />),
70     ContextMenuCell,
72 const smallScreenCells: React.FC<{ item: SharedWithMeItem }>[] = [
73     CheckboxCell,
74     NameCell,
75     ({ item }) => (item.isInvitation ? <AcceptOrRejectInviteCell item={item} /> : null),
76     ContextMenuCell,
79 const headerItemsLargeScreen: ListViewHeaderItem[] = [
80     headerItems.checkbox,
81     headerItems.name,
82     headerItems.sharedBy,
83     headerItems.sharedOnDate,
84     headerItems.placeholder,
87 const headerItemsSmallScreen: ListViewHeaderItem[] = [headerItems.checkbox, headerItems.name, headerItems.placeholder];
88 type SharedWithMeSortFields = Extract<SortField, SortField.name | SortField.sharedBy | SortField.sharedOn>;
89 const SORT_FIELDS: SharedWithMeSortFields[] = [SortField.name, SortField.sharedBy, SortField.sharedOn];
91 const SharedWithMe = ({ sharedWithMeView }: Props) => {
92     const contextMenuAnchorRef = useRef<HTMLDivElement>(null);
94     const { navigateToLink } = useNavigate();
96     const browserItemContextMenu = useItemContextMenu();
97     const thumbnails = useThumbnailsDownload();
98     const selectionControls = useSelection();
99     const { viewportWidth } = useActiveBreakpoint();
100     const { openDocument } = useDocumentActions();
101     const { canUseDocs } = useDriveDocsFeatureFlag();
102     const { openBookmark } = useBookmarksActions();
103     const { incrementItemRenderedCounter } = useOnItemRenderedMetrics(
104         sharedWithMeView.layout,
105         sharedWithMeView.isLoading
106     );
107     const { layout, items, sortParams, setSorting, isLoading } = sharedWithMeView;
109     const selectedItemIds = selectionControls!.selectedItemIds;
110     const selectedBrowserItems = useMemo(
111         () => getSelectedSharedWithMeItems(items, selectedItemIds),
112         [items, selectedItemIds]
113     );
115     const handleClick = useCallback(
116         (id: BrowserItemId) => {
117             const item = items.find((item) => item.id === id);
118             if (!item || item.isInvitation) {
119                 return;
120             }
121             if (item.isBookmark && item.bookmarkDetails) {
122                 void openBookmark({
123                     token: item.bookmarkDetails.token,
124                     urlPassword: item.bookmarkDetails.urlPassword,
125                 });
126                 return;
127             }
128             document.getSelection()?.removeAllRanges();
130             if (isProtonDocument(item.mimeType)) {
131                 void canUseDocs(item.rootShareId)
132                     .then((canUse) => {
133                         if (!canUse) {
134                             return;
135                         }
137                         return openDocument({
138                             linkId: item.linkId,
139                             shareId: item.rootShareId,
140                             openBehavior: 'tab',
141                         });
142                     })
143                     .catch(sendErrorReport);
144                 return;
145             }
146             navigateToLink(item.rootShareId, item.linkId, item.isFile);
147         },
148         [navigateToLink, items]
149     );
151     const handleItemRender = useCallback(
152         (item: SharedWithMeItem) => {
153             incrementItemRenderedCounter();
154             if (item.hasThumbnail && item.activeRevision && !item.cachedThumbnailUrl) {
155                 thumbnails.addToDownloadQueue(item.rootShareId, item.linkId, item.activeRevision.id);
156             }
157         },
158         [thumbnails, incrementItemRenderedCounter]
159     );
161     /* eslint-disable react/display-name */
162     const GridHeaderComponent = useMemo(
163         () =>
164             ({ scrollAreaRef }: { scrollAreaRef: React.RefObject<HTMLDivElement> }) => {
165                 const activeSortingText = translateSortField(sortParams.sortField);
166                 return (
167                     <GridHeader
168                         isLoading={isLoading}
169                         sortFields={SORT_FIELDS}
170                         onSort={setSorting}
171                         sortField={sortParams.sortField}
172                         sortOrder={sortParams.sortOrder}
173                         itemCount={items.length}
174                         scrollAreaRef={scrollAreaRef}
175                         activeSortingText={activeSortingText}
176                     />
177                 );
178             },
179         [sortParams.sortField, sortParams.sortOrder, isLoading]
180     );
182     if (!items.length && !isLoading) {
183         return <EmptySharedWithMe />;
184     }
186     const Cells = viewportWidth['>=large'] ? largeScreenCells : smallScreenCells;
187     const headerItems = viewportWidth['>=large'] ? headerItemsLargeScreen : headerItemsSmallScreen;
189     return (
190         <ContactEmailsProvider>
191             <SharedWithMeContextMenu
192                 selectedBrowserItems={selectedBrowserItems}
193                 anchorRef={contextMenuAnchorRef}
194                 close={browserItemContextMenu.close}
195                 isOpen={browserItemContextMenu.isOpen}
196                 open={browserItemContextMenu.open}
197                 position={browserItemContextMenu.position}
198             />
199             <FileBrowser
200                 caption={c('Title').t`Shared`}
201                 items={items}
202                 headerItems={headerItems}
203                 layout={layout}
204                 loading={isLoading}
205                 sortParams={sortParams}
206                 Cells={Cells}
207                 GridHeaderComponent={GridHeaderComponent}
208                 GridViewItem={GridViewItem}
209                 contextMenuAnchorRef={contextMenuAnchorRef}
210                 onItemContextMenu={browserItemContextMenu.handleContextMenu}
211                 onItemOpen={handleClick}
212                 onItemRender={handleItemRender}
213                 onSort={setSorting}
214                 onScroll={browserItemContextMenu.close}
215             />
216         </ContactEmailsProvider>
217     );
220 export default SharedWithMe;