1 import { useCallback, useEffect, useMemo, useRef } from 'react';
3 import { useActiveBreakpoint } from '@proton/components';
4 import { getCanAdmin } from '@proton/shared/lib/drive/permissions';
5 import { isProtonDocument } from '@proton/shared/lib/helpers/mimetype';
7 import type { DriveFolder } from '../../../hooks/drive/useActiveShare';
8 import useDriveDragMove from '../../../hooks/drive/useDriveDragMove';
9 import useNavigate from '../../../hooks/drive/useNavigate';
10 import { useOnItemRenderedMetrics } from '../../../hooks/drive/useOnItemRenderedMetrics';
11 import type { EncryptedLink, LinkShareUrl, SignatureIssues, useFolderView } from '../../../store';
12 import { useThumbnailsDownload } from '../../../store';
13 import { useDocumentActions, useDriveDocsFeatureFlag } from '../../../store/_documents';
14 import { SortField } from '../../../store/_views/utils/useSorting';
15 import { sendErrorReport } from '../../../utils/errorHandling';
19 useContextMenuControls,
22 } from '../../FileBrowser';
23 import type { BrowserItemId, FileBrowserBaseItem, ListViewHeaderItem } from '../../FileBrowser/interface';
24 import { useLinkSharingModal } from '../../modals/ShareLinkModal/ShareLinkModal';
25 import useOpenPreview from '../../useOpenPreview';
26 import { GridViewItem } from '../FileBrowser/GridViewItemLink';
27 import { ModifiedCell, NameCell, ShareOptionsCell, SizeCell } from '../FileBrowser/contentCells';
28 import headerItems from '../FileBrowser/headerCells';
29 import { translateSortField } from '../SortDropdown';
30 import { getSelectedItems } from '../helpers';
31 import { DriveItemContextMenu } from './DriveContextMenu';
32 import EmptyDeviceRoot from './EmptyDeviceRoot';
33 import EmptyFolder from './EmptyFolder';
34 import { FolderContextMenu } from './FolderContextMenu';
36 export interface DriveItem extends FileBrowserBaseItem {
37 activeRevision?: EncryptedLink['activeRevision'];
38 cachedThumbnailUrl?: string;
39 hasThumbnail: boolean;
42 fileModifyTime: number;
44 shareUrl?: LinkShareUrl;
45 signatureIssues?: SignatureIssues;
46 signatureAddress?: string;
48 trashed: number | null;
52 showLinkSharingModal?: ReturnType<typeof useLinkSharingModal>[1];
56 activeFolder: DriveFolder;
57 folderView: ReturnType<typeof useFolderView>;
60 const { CheckboxCell, ContextMenuCell } = Cells;
62 const myFilesLargeScreenCells: React.FC<{ item: DriveItem }>[] = [
70 const myFilesSmallScreenCells = [CheckboxCell, NameCell, ContextMenuCell];
72 const headerItemsLargeScreen: ListViewHeaderItem[] = [
75 headerItems.modificationDate,
77 headerItems.placeholder,
78 headerItems.placeholder,
81 const headerItemsSmallScreen: ListViewHeaderItem[] = [headerItems.checkbox, headerItems.name, headerItems.placeholder];
83 type DriveSortFields = Extract<SortField, SortField.name | SortField.fileModifyTime | SortField.size>;
84 const SORT_FIELDS: DriveSortFields[] = [SortField.name, SortField.fileModifyTime, SortField.size];
86 function Drive({ activeFolder, folderView }: Props) {
87 const { shareId, linkId } = activeFolder;
88 const contextMenuAnchorRef = useRef<HTMLDivElement>(null);
90 const browserContextMenu = useContextMenuControls();
91 const browserItemContextMenu = useItemContextMenu();
92 const thumbnails = useThumbnailsDownload();
93 const { navigateToLink } = useNavigate();
94 const selectionControls = useSelection();
95 const { viewportWidth } = useActiveBreakpoint();
96 const { openDocument } = useDocumentActions();
97 const { canUseDocs } = useDriveDocsFeatureFlag();
98 const [linkSharingModal, showLinkSharingModal] = useLinkSharingModal();
99 const { incrementItemRenderedCounter } = useOnItemRenderedMetrics(folderView.layout, folderView.isLoading);
100 const { permissions, layout, folderName, items, sortParams, setSorting, isLoading } = folderView;
102 const isAdmin = useMemo(() => getCanAdmin(permissions), [permissions]);
104 const selectedItems = useMemo(
105 () => getSelectedItems(items, selectionControls!.selectedItemIds),
106 [items, selectionControls!.selectedItemIds]
109 const openPreview = useOpenPreview();
110 const browserItems: DriveItem[] = items.map((item) => ({
113 // TODO: Improve this, we should not pass showLinkSharingModal here
114 showLinkSharingModal: item.isShared ? showLinkSharingModal : undefined,
117 const { getDragMoveControls } = useDriveDragMove(shareId, browserItems, selectionControls!.clearSelections);
119 /* eslint-disable react/display-name */
120 const GridHeaderComponent = useMemo(
122 ({ scrollAreaRef }: { scrollAreaRef: React.RefObject<HTMLDivElement> }) => {
123 const activeSortingText = translateSortField(sortParams.sortField);
126 isLoading={isLoading}
127 sortFields={SORT_FIELDS}
129 sortField={sortParams.sortField}
130 sortOrder={sortParams.sortOrder}
131 itemCount={browserItems.length}
132 scrollAreaRef={scrollAreaRef}
133 activeSortingText={activeSortingText}
137 [sortParams.sortField, sortParams.sortOrder, isLoading]
140 const handleItemRender = useCallback(
141 (item: DriveItem) => {
142 incrementItemRenderedCounter();
144 if (item.hasThumbnail && item.activeRevision && !item.cachedThumbnailUrl) {
145 thumbnails.addToDownloadQueue(shareId, item.linkId, item.activeRevision.id);
148 [thumbnails, shareId, incrementItemRenderedCounter]
151 const handleClick = useCallback(
152 (id: BrowserItemId) => {
153 const item = browserItems.find((item) => item.id === id);
158 document.getSelection()?.removeAllRanges();
160 if (isProtonDocument(item.mimeType)) {
161 void canUseDocs(shareId)
167 return openDocument({
173 .catch(sendErrorReport);
178 openPreview(shareId, id);
181 navigateToLink(shareId, id, item.isFile);
183 [navigateToLink, shareId, browserItems]
186 const handleScroll = () => {
187 browserContextMenu.close();
188 browserItemContextMenu.close();
192 browserContextMenu.close();
193 browserItemContextMenu.close();
194 }, [shareId, linkId]);
196 if (!items.length && !isLoading) {
197 if (folderView.isActiveLinkReadOnly) {
198 return <EmptyDeviceRoot />;
201 return <EmptyFolder shareId={shareId} permissions={permissions} />;
204 const Cells = viewportWidth['>=large'] ? myFilesLargeScreenCells : myFilesSmallScreenCells;
205 const headerItems = viewportWidth['>=large'] ? headerItemsLargeScreen : headerItemsSmallScreen;
210 permissions={permissions}
211 isActiveLinkReadOnly={folderView.isActiveLinkReadOnly}
213 anchorRef={contextMenuAnchorRef}
214 close={browserContextMenu.close}
215 isOpen={browserContextMenu.isOpen}
216 open={browserContextMenu.open}
217 position={browserContextMenu.position}
219 <DriveItemContextMenu
220 permissions={permissions}
221 isActiveLinkReadOnly={folderView.isActiveLinkReadOnly}
223 selectedLinks={selectedItems}
224 anchorRef={contextMenuAnchorRef}
225 close={browserItemContextMenu.close}
226 isOpen={browserItemContextMenu.isOpen}
227 open={browserItemContextMenu.open}
228 position={browserItemContextMenu.position}
233 headerItems={headerItems}
237 sortParams={sortParams}
240 GridHeaderComponent={GridHeaderComponent}
241 GridViewItem={GridViewItem}
243 onItemOpen={handleClick}
244 contextMenuAnchorRef={contextMenuAnchorRef}
245 onItemContextMenu={browserItemContextMenu.handleContextMenu}
246 onItemRender={handleItemRender}
248 onScroll={handleScroll}
249 onViewContextMenu={browserContextMenu.handleContextMenu}
250 getDragMoveControls={folderView.isActiveLinkReadOnly ? undefined : getDragMoveControls}
257 export default Drive;