import React, { useEffect, useState } from 'react'; import tw from 'twin.macro'; import { Button } from '@/components/elements/button/index'; import Fade from '@/components/elements/Fade'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import useFlash from '@/plugins/useFlash'; import compressFiles from '@/api/server/files/compressFiles'; import { ServerContext } from '@/state/server'; import deleteFiles from '@/api/server/files/deleteFiles'; import RenameFileModal from '@/components/server/files/RenameFileModal'; import Portal from '@/components/elements/Portal'; import { Dialog } from '@/components/elements/dialog'; const MassActionsBar = () => { const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const { mutate } = useFileManagerSwr(); const { clearFlashes, clearAndAddHttpError } = useFlash(); const [ loading, setLoading ] = useState(false); const [ loadingMessage, setLoadingMessage ] = useState(''); const [ showConfirm, setShowConfirm ] = useState(false); const [ showMove, setShowMove ] = useState(false); const directory = ServerContext.useStoreState(state => state.files.directory); const selectedFiles = ServerContext.useStoreState(state => state.files.selectedFiles); const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles); useEffect(() => { if (!loading) setLoadingMessage(''); }, [ loading ]); const onClickCompress = () => { setLoading(true); clearFlashes('files'); setLoadingMessage('Archiving files...'); compressFiles(uuid, directory, selectedFiles) .then(() => mutate()) .then(() => setSelectedFiles([])) .catch(error => clearAndAddHttpError({ key: 'files', error })) .then(() => setLoading(false)); }; const onClickConfirmDeletion = () => { setLoading(true); setShowConfirm(false); clearFlashes('files'); setLoadingMessage('Deleting files...'); deleteFiles(uuid, directory, selectedFiles) .then(() => { mutate(files => files.filter(f => selectedFiles.indexOf(f.name) < 0), false); setSelectedFiles([]); }) .catch(error => { mutate(); clearAndAddHttpError({ key: 'files', error }); }) .then(() => setLoading(false)); }; return ( <> <div css={tw`pointer-events-none fixed bottom-0 z-20 left-0 right-0 flex justify-center`}> <SpinnerOverlay visible={loading} size={'large'} fixed> {loadingMessage} </SpinnerOverlay> <Dialog.Confirm title={'Delete Files'} open={showConfirm} confirm={'Delete'} onClose={() => setShowConfirm(false)} onConfirmed={onClickConfirmDeletion} > <p className={'mb-2'}> Are you sure you want to delete <span className={'font-semibold text-gray-50'}>{selectedFiles.length} files</span>? This is a permanent action and the files cannot be recovered. </p> {selectedFiles.slice(0, 15).map(file => ( <li key={file}>{file}</li>)) } {selectedFiles.length > 15 && <li>and {selectedFiles.length - 15} others</li> } </Dialog.Confirm> {showMove && <RenameFileModal files={selectedFiles} visible appear useMoveTerminology onDismissed={() => setShowMove(false)} /> } <Portal> <div className={'fixed bottom-0 mb-6 flex justify-center w-full z-50'}> <Fade timeout={75} in={selectedFiles.length > 0} unmountOnExit> <div css={tw`flex items-center space-x-4 pointer-events-auto rounded p-4 bg-black/50`}> <Button onClick={() => setShowMove(true)}>Move</Button> <Button onClick={onClickCompress}>Archive</Button> <Button.Danger variant={Button.Variants.Secondary} onClick={() => setShowConfirm(true)}> Delete </Button.Danger> </div> </Fade> </div> </Portal> </div> </> ); }; export default MassActionsBar;