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';
12 type ExtendedInvitationDetails,
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';
24 type FileBrowserBaseItem,
26 type ListViewHeaderItem,
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;
45 signatureIssues?: SignatureIssues;
46 signatureAddress?: string;
48 trashed: number | null;
54 invitationDetails?: ExtendedInvitationDetails;
55 bookmarkDetails?: { token: string; createTime: number; urlPassword: string };
60 sharedWithMeView: ReturnType<typeof useSharedWithMeView>;
63 const { CheckboxCell, ContextMenuCell } = Cells;
65 const largeScreenCells: React.FC<{ item: SharedWithMeItem }>[] = [
69 ({ item }) => (item.isInvitation ? <AcceptOrRejectInviteCell item={item} /> : <SharedOnCell item={item} />),
72 const smallScreenCells: React.FC<{ item: SharedWithMeItem }>[] = [
75 ({ item }) => (item.isInvitation ? <AcceptOrRejectInviteCell item={item} /> : null),
79 const headerItemsLargeScreen: ListViewHeaderItem[] = [
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
107 const { layout, items, sortParams, setSorting, isLoading } = sharedWithMeView;
109 const selectedItemIds = selectionControls!.selectedItemIds;
110 const selectedBrowserItems = useMemo(
111 () => getSelectedSharedWithMeItems(items, selectedItemIds),
112 [items, selectedItemIds]
115 const handleClick = useCallback(
116 (id: BrowserItemId) => {
117 const item = items.find((item) => item.id === id);
118 if (!item || item.isInvitation) {
121 if (item.isBookmark && item.bookmarkDetails) {
123 token: item.bookmarkDetails.token,
124 urlPassword: item.bookmarkDetails.urlPassword,
128 document.getSelection()?.removeAllRanges();
130 if (isProtonDocument(item.mimeType)) {
131 void canUseDocs(item.rootShareId)
137 return openDocument({
139 shareId: item.rootShareId,
143 .catch(sendErrorReport);
146 navigateToLink(item.rootShareId, item.linkId, item.isFile);
148 [navigateToLink, items]
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);
158 [thumbnails, incrementItemRenderedCounter]
161 /* eslint-disable react/display-name */
162 const GridHeaderComponent = useMemo(
164 ({ scrollAreaRef }: { scrollAreaRef: React.RefObject<HTMLDivElement> }) => {
165 const activeSortingText = translateSortField(sortParams.sortField);
168 isLoading={isLoading}
169 sortFields={SORT_FIELDS}
171 sortField={sortParams.sortField}
172 sortOrder={sortParams.sortOrder}
173 itemCount={items.length}
174 scrollAreaRef={scrollAreaRef}
175 activeSortingText={activeSortingText}
179 [sortParams.sortField, sortParams.sortOrder, isLoading]
182 if (!items.length && !isLoading) {
183 return <EmptySharedWithMe />;
186 const Cells = viewportWidth['>=large'] ? largeScreenCells : smallScreenCells;
187 const headerItems = viewportWidth['>=large'] ? headerItemsLargeScreen : headerItemsSmallScreen;
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}
200 caption={c('Title').t`Shared`}
202 headerItems={headerItems}
205 sortParams={sortParams}
207 GridHeaderComponent={GridHeaderComponent}
208 GridViewItem={GridViewItem}
209 contextMenuAnchorRef={contextMenuAnchorRef}
210 onItemContextMenu={browserItemContextMenu.handleContextMenu}
211 onItemOpen={handleClick}
212 onItemRender={handleItemRender}
214 onScroll={browserItemContextMenu.close}
216 </ContactEmailsProvider>
220 export default SharedWithMe;