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;
31 fileModifyTime: number;
33 shareUrl?: LinkShareUrl;
34 signatureIssues?: any;
36 trashed: number | null;
44 sharedLinksView: ReturnType<typeof useSharedLinksView>;
47 const { CheckboxCell, ContextMenuCell } = Cells;
49 const largeScreenCells: React.FC<{ item: SharedLinkItem }>[] = [
58 const smallScreenCells = [CheckboxCell, NameCell, LocationCell, ExpirationCell, ContextMenuCell];
60 const headerItemsLargeScreen: ListViewHeaderItem[] = [
64 headerItems.creationDate,
65 headerItems.accessCount,
66 headerItems.expirationDate,
67 headerItems.placeholder,
70 const headerItemsSmallScreen: ListViewHeaderItem[] = [
74 headerItems.expirationDate,
75 headerItems.placeholder,
77 type SharedLinksSortFields = Extract<
79 SortField.name | SortField.linkCreateTime | SortField.linkExpireTime | SortField.numAccesses
81 const SORT_FIELDS: SharedLinksSortFields[] = [
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
102 const { layout, items, sortParams, setSorting, isLoading } = sharedLinksView;
104 const selectedItems = useMemo(
105 () => getSelectedItems(items, selectionControls!.selectedItemIds),
106 [items, selectionControls!.selectedItemIds]
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);
118 document.getSelection()?.removeAllRanges();
120 if (isProtonDocument(item.mimeType)) {
121 void canUseDocs(item.rootShareId)
127 return openDocument({
129 shareId: item.rootShareId,
133 .catch(sendErrorReport);
136 navigateToLink(item.rootShareId, item.linkId, item.isFile);
138 [navigateToLink, browserItems]
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);
148 [thumbnails, incrementItemRenderedCounter]
151 /* eslint-disable react/display-name */
152 const GridHeaderComponent = useMemo(
154 ({ scrollAreaRef }: { scrollAreaRef: React.RefObject<HTMLDivElement> }) => {
155 const activeSortingText = translateSortField(sortParams.sortField);
158 isLoading={isLoading}
159 sortFields={SORT_FIELDS}
161 sortField={sortParams.sortField}
162 sortOrder={sortParams.sortOrder}
163 itemCount={browserItems.length}
164 scrollAreaRef={scrollAreaRef}
165 activeSortingText={activeSortingText}
169 [sortParams.sortField, sortParams.sortOrder, isLoading]
172 if (!items.length && !isLoading) {
173 return <EmptyShared shareId={shareId} />;
176 const Cells = viewportWidth['>=large'] ? largeScreenCells : smallScreenCells;
177 const headerItems = viewportWidth['>=large'] ? headerItemsLargeScreen : headerItemsSmallScreen;
181 <SharedLinksItemContextMenu
182 selectedLinks={selectedItems}
183 anchorRef={contextMenuAnchorRef}
184 close={browserItemContextMenu.close}
185 isOpen={browserItemContextMenu.isOpen}
186 open={browserItemContextMenu.open}
187 position={browserItemContextMenu.position}
190 caption={c('Title').t`Shared`}
192 headerItems={headerItems}
195 sortParams={sortParams}
197 GridHeaderComponent={GridHeaderComponent}
198 GridViewItem={GridViewItem}
199 contextMenuAnchorRef={contextMenuAnchorRef}
200 onItemContextMenu={browserItemContextMenu.handleContextMenu}
201 onItemOpen={handleClick}
202 onItemRender={handleItemRender}
204 onScroll={browserItemContextMenu.close}
210 export default SharedLinks;