import { ChangeEvent, ReactElement, useCallback, useRef, useState } from 'react' import { useParams } from 'react-router-dom' import { Button, ButtonGroup, IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList, Portal, Spacer, Slider, SliderTrack, SliderFilledTrack, SliderThumb, } from '@chakra-ui/react' import { useSWRConfig } from 'swr' import cx from 'classnames' import FileAPI, { List, SortBy, SortOrder } from '@/client/api/file' import { ltEditorPermission, ltOwnerPermission } from '@/client/api/permission' import downloadFile from '@/helpers/download-file' import mapFileList from '@/helpers/map-file-list' import useFileListSearchParams from '@/hooks/use-file-list-params' import { IconAdd, IconFileCopy, IconMoreVert, IconDownload, IconEdit, IconArrowTopRight, IconGroup, IconDelete, IconUpload, IconRefresh, IconGridView, IconArrowDownward, IconArrowUpward, IconCheck, IconSelectCheckBox, IconCheckBoxOutlineBlank, IconLibraryAddCheck, IconExpandMore, IconClose, IconList, } from '@/lib' import { uploadAdded, UploadDecorator } from '@/store/entities/uploads' import { useAppDispatch, useAppSelector } from '@/store/hook' import { sortByUpdated, viewTypeToggled, selectionModeToggled, sortOrderToggled, } from '@/store/ui/files' import { copyModalDidOpen, createModalDidOpen, deleteModalDidOpen, iconScaleUpdated, moveModalDidOpen, renameModalDidOpen, selectionUpdated, sharingModalDidOpen, } from '@/store/ui/files' import { uploadsDrawerOpened } from '@/store/ui/uploads-drawer' import { FileViewType } from '@/types/file' const ICON_SCALE_SLIDER_STEP = 0.25 const ICON_SCALE_SLIDER_MIN = 1 const ICON_SCALE_SLIDER_MAX = ICON_SCALE_SLIDER_STEP * 9 export type FileToolbarProps = { list?: List } const FileToolbar = ({ list }: FileToolbarProps) => { const dispatch = useAppDispatch() const { mutate } = useSWRConfig() const { id, fileId } = useParams() const [isRefreshing, setIsRefreshing] = useState(false) const fileCount = useAppSelector( (state) => state.entities.files.list?.data.length, ) const selectionCount = useAppSelector( (state) => state.ui.files.selection.length, ) const singleFile = useAppSelector((state) => state.ui.files.selection.length === 1 ? list?.data.find((e) => e.id === state.ui.files.selection[0]) : null, ) const iconScale = useAppSelector((state) => state.ui.files.iconScale) const viewType = useAppSelector((state) => state.ui.files.viewType) const sortBy = useAppSelector((state) => state.ui.files.sortBy) const sortOrder = useAppSelector((state) => state.ui.files.sortOrder) const hasOwnerPermission = useAppSelector( (state) => list?.data.findIndex( (f) => state.ui.files.selection.findIndex( (s) => f.id === s && ltOwnerPermission(f.permission), ) !== -1, ) === -1, ) const hasEditorPermission = useAppSelector( (state) => list?.data.findIndex( (f) => state.ui.files.selection.findIndex( (s) => f.id === s && ltEditorPermission(f.permission), ) !== -1, ) === -1, ) const isSelectionMode = useAppSelector( (state) => state.ui.files.isSelectionMode, ) const fileUploadInput = useRef(null) const folderUploadInput = useRef(null) const fileListSearchParams = useFileListSearchParams() const { data: folder } = FileAPI.useGetById(fileId) const stackClassName = cx('flex', 'flex-row', 'gap-0.5') const handleFileChange = useCallback( async (event: ChangeEvent) => { const files = mapFileList(event.target.files) if (files.length === 0) { return } for (const file of files) { dispatch( uploadAdded( new UploadDecorator({ workspaceId: id!, parentId: fileId!, file, }).value, ), ) } dispatch(uploadsDrawerOpened()) if (fileUploadInput && fileUploadInput.current) { fileUploadInput.current.value = '' } if (folderUploadInput && folderUploadInput.current) { folderUploadInput.current.value = '' } }, [id, fileId, dispatch], ) const handleIconScaleChange = useCallback( (value: number) => { dispatch(iconScaleUpdated(value)) }, [dispatch], ) const handleRefresh = useCallback(async () => { setIsRefreshing(true) dispatch(selectionUpdated([])) await mutate(`/files/${fileId}/list?${fileListSearchParams}`) setIsRefreshing(false) }, [fileId, fileListSearchParams, mutate, dispatch]) const handleSortByChange = useCallback( (value: SortBy) => { dispatch(sortByUpdated(value)) }, [dispatch], ) const handleSortOrderToggle = useCallback(() => { dispatch(sortOrderToggled()) }, [dispatch]) const handleViewTypeToggle = useCallback(() => { dispatch(viewTypeToggled()) }, [dispatch]) const handleSelectAllClick = useCallback(() => { if (list?.data) { dispatch(selectionUpdated(list?.data.map((f) => f.id))) } }, [list?.data, dispatch]) const handleToggleSelection = useCallback(() => { dispatch(selectionUpdated([])) dispatch(selectionModeToggled()) }, [dispatch]) const getSortByIcon = useCallback( (value: SortBy): ReactElement => { if (value === sortBy) { return } else { return } }, [sortBy], ) const getSortOrderIcon = useCallback(() => { if (sortOrder === SortOrder.Asc) { return } else if (sortOrder === SortOrder.Desc) { return } }, [sortOrder]) const getViewTypeIcon = useCallback(() => { if (viewType === FileViewType.Grid) { return } else if (viewType === FileViewType.List) { return } }, [viewType]) return ( <>
} isDisabled={ !folder || ltEditorPermission(folder.permission) || !list } > Upload } onClick={() => fileUploadInput?.current?.click()} > Upload Files } onClick={() => folderUploadInput?.current?.click()} > Upload Folder
{selectionCount > 0 && hasOwnerPermission && ( )} {singleFile?.type === 'file' && ( )} {selectionCount > 0 && hasOwnerPermission && ( )} {selectionCount > 0 ? ( } variant="solid" aria-label="" isDisabled={!list} /> } isDisabled={selectionCount === 0 || !hasOwnerPermission} onClick={() => dispatch(sharingModalDidOpen())} > Sharing } isDisabled={singleFile?.type !== 'file'} onClick={() => { if (singleFile) { downloadFile(singleFile) } }} > Download } className={cx('text-red-500')} isDisabled={selectionCount === 0 || !hasOwnerPermission} onClick={() => dispatch(deleteModalDidOpen())} > Delete } isDisabled={selectionCount !== 1 || !hasEditorPermission} onClick={() => dispatch(renameModalDidOpen())} > Rename } isDisabled={selectionCount === 0 || !hasEditorPermission} onClick={() => dispatch(moveModalDidOpen())} > Move } isDisabled={selectionCount === 0 || !hasEditorPermission} onClick={() => dispatch(copyModalDidOpen())} > Copy {isSelectionMode ? ( <> } onClick={handleSelectAllClick} > Select All } onClick={() => dispatch(selectionUpdated([]))} > Unselect All ) : null} ) : null}
{fileCount ? ( : } isDisabled={!list} variant="solid" aria-label="" onClick={handleToggleSelection} /> ) : null} } isLoading={isRefreshing} isDisabled={!list} variant="solid" aria-label="" onClick={handleRefresh} />
} variant="solid" aria-label="" isDisabled={!list} /> handleSortByChange(SortBy.Name)} > Sort By Name handleSortByChange(SortBy.Kind)} > Sort By Kind handleSortByChange(SortBy.Size)} > Sort By Size handleSortByChange(SortBy.DateCreated)} > Sort By Date Created handleSortByChange(SortBy.DateModified)} > Sort By Date Modified
) } export default FileToolbar