1 import { useCallback, useMemo, useRef } from 'react';
3 import { c } from 'ttag';
5 import { useActiveBreakpoint } from '@proton/components';
6 import { SHARE_MEMBER_PERMISSIONS } from '@proton/shared/lib/drive/permissions';
8 import useDriveDragMove from '../../../hooks/drive/useDriveDragMove';
9 import useNavigate from '../../../hooks/drive/useNavigate';
10 import type { EncryptedLink, LinkShareUrl, useSearchView } from '../../../store';
11 import { useThumbnailsDownload } from '../../../store';
12 import { SortField } from '../../../store/_views/utils/useSorting';
13 import FileBrowser, { Cells, GridHeader, useItemContextMenu, useSelection } from '../../FileBrowser';
14 import type { BrowserItemId, FileBrowserBaseItem, ListViewHeaderItem } from '../../FileBrowser/interface';
15 import { GridViewItem } from '../FileBrowser/GridViewItemLink';
16 import { LocationCell, ModifiedCell, NameCell, SizeCell } from '../FileBrowser/contentCells';
17 import headerItems from '../FileBrowser/headerCells';
18 import { translateSortField } from '../SortDropdown';
19 import { getSelectedItems } from '../helpers';
20 import { NoSearchResultsView } from './NoSearchResultsView';
21 import { SearchItemContextMenu } from './SearchItemContextMenu';
23 interface SearchItem extends FileBrowserBaseItem {
24 activeRevision?: EncryptedLink['activeRevision'];
25 cachedThumbnailUrl?: string;
26 hasThumbnail: boolean;
29 fileModifyTime: number;
31 shareUrl?: LinkShareUrl;
32 signatureIssues?: any;
34 trashed: number | null;
43 searchView: ReturnType<typeof useSearchView>;
46 const { CheckboxCell, ContextMenuCell } = Cells;
48 const largeScreenCells: React.FC<{ item: SearchItem }>[] = [
56 const smallScreenCells = [CheckboxCell, NameCell, ContextMenuCell];
58 const headerItemsLargeScreen: ListViewHeaderItem[] = [
62 headerItems.modificationDate,
64 headerItems.placeholder,
67 const headerItemsSmallScreen: ListViewHeaderItem[] = [headerItems.checkbox, headerItems.name, headerItems.placeholder];
69 type SearchSortFields = Extract<SortField, SortField.name | SortField.fileModifyTime | SortField.size>;
70 const SORT_FIELDS: SearchSortFields[] = [SortField.name, SortField.size, SortField.fileModifyTime];
72 export const Search = ({ shareId, searchView }: Props) => {
73 const contextMenuAnchorRef = useRef<HTMLDivElement>(null);
75 const browserItemContextMenu = useItemContextMenu();
76 const thumbnails = useThumbnailsDownload();
77 const { navigateToLink } = useNavigate();
78 const selectionControls = useSelection();
79 const { viewportWidth } = useActiveBreakpoint();
81 const { layout, items, sortParams, setSorting, isLoading } = searchView;
83 const selectedItems = useMemo(
84 () => getSelectedItems(items, selectionControls!.selectedItemIds),
85 [items, selectionControls!.selectedItemIds]
88 // We consider that search is limited to items you own for now
89 const browserItems: SearchItem[] = items.map((item) => ({ ...item, id: item.linkId, isAdmin: true }));
90 const { getDragMoveControls } = useDriveDragMove(shareId, browserItems, selectionControls!.clearSelections);
92 /* eslint-disable react/display-name */
93 const GridHeaderComponent = useMemo(
95 ({ scrollAreaRef }: { scrollAreaRef: React.RefObject<HTMLDivElement> }) => {
96 const activeSortingText = translateSortField(sortParams.sortField);
100 sortFields={SORT_FIELDS}
102 sortField={sortParams.sortField}
103 sortOrder={sortParams.sortOrder}
104 itemCount={browserItems.length}
105 scrollAreaRef={scrollAreaRef}
106 activeSortingText={activeSortingText}
110 [sortParams.sortField, sortParams.sortOrder, isLoading]
113 const handleItemRender = (item: SearchItem) => {
114 if (item.hasThumbnail && item.activeRevision && !item.cachedThumbnailUrl) {
115 thumbnails.addToDownloadQueue(item.rootShareId, item.id, item.activeRevision.id);
119 const handleClick = useCallback(
120 (id: BrowserItemId) => {
121 const item = browserItems.find((item) => item.id === id);
126 document.getSelection()?.removeAllRanges();
127 navigateToLink(shareId, item.linkId, item.isFile);
129 [navigateToLink, shareId, browserItems]
132 const Cells = viewportWidth['>=large'] ? largeScreenCells : smallScreenCells;
133 const headerItems = viewportWidth['>=large'] ? headerItemsLargeScreen : headerItemsSmallScreen;
135 if (!items.length && !isLoading) {
136 return <NoSearchResultsView />;
140 <SearchItemContextMenu
141 permissions={SHARE_MEMBER_PERMISSIONS.OWNER} // TODO: Permissions is not supported on search for now
143 selectedLinks={selectedItems}
144 anchorRef={contextMenuAnchorRef}
145 close={browserItemContextMenu.close}
146 isOpen={browserItemContextMenu.isOpen}
147 open={browserItemContextMenu.open}
148 position={browserItemContextMenu.position}
152 caption={c('Title').t`Search results`}
153 headerItems={headerItems}
157 sortParams={sortParams}
160 GridHeaderComponent={GridHeaderComponent}
161 GridViewItem={GridViewItem}
163 onItemOpen={handleClick}
164 contextMenuAnchorRef={contextMenuAnchorRef}
165 onItemContextMenu={browserItemContextMenu.handleContextMenu}
166 onItemRender={handleItemRender}
168 onScroll={browserItemContextMenu.close}
169 getDragMoveControls={getDragMoveControls}