Skip to content

Commit 80ecd58

Browse files
committed
Mass actions for moving files, mostly working?
1 parent 121f163 commit 80ecd58

File tree

6 files changed

+66
-41
lines changed

6 files changed

+66
-41
lines changed

resources/scripts/components/server/files/FileManagerContainer.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import Button from '@/components/elements/Button';
1515
import useServer from '@/plugins/useServer';
1616
import { ServerContext } from '@/state/server';
1717
import useFileManagerSwr from '@/plugins/useFileManagerSwr';
18-
import { Form, Formik } from 'formik';
1918
import MassActionsBar from '@/components/server/files/MassActionsBar';
2019

2120
const sortFiles = (files: FileObject[]): FileObject[] => {
@@ -28,12 +27,14 @@ export default () => {
2827
const { hash } = useLocation();
2928
const { data: files, error, mutate } = useFileManagerSwr();
3029
const setDirectory = ServerContext.useStoreActions(actions => actions.files.setDirectory);
30+
const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles);
3131

3232
useEffect(() => {
3333
// We won't automatically mutate the store when the component re-mounts, otherwise because of
3434
// my (horrible) programming this fires off way more than we intend it to.
3535
mutate();
3636

37+
setSelectedFiles([]);
3738
setDirectory(hash.length > 0 ? hash : '/');
3839
}, [ hash ]);
3940

@@ -58,27 +59,20 @@ export default () => {
5859
:
5960
<CSSTransition classNames={'fade'} timeout={150} appear in>
6061
<div>
61-
<Formik
62-
onSubmit={() => undefined}
63-
initialValues={{ selectedFiles: [] }}
64-
>
65-
<Form>
66-
{files.length > 250 &&
67-
<div css={tw`rounded bg-yellow-400 mb-px p-3`}>
68-
<p css={tw`text-yellow-900 text-sm text-center`}>
69-
This directory is too large to display in the browser,
70-
limiting the output to the first 250 files.
71-
</p>
72-
</div>
73-
}
74-
{
75-
sortFiles(files.slice(0, 250)).map(file => (
76-
<FileObjectRow key={file.uuid} file={file}/>
77-
))
78-
}
79-
<MassActionsBar/>
80-
</Form>
81-
</Formik>
62+
{files.length > 250 &&
63+
<div css={tw`rounded bg-yellow-400 mb-px p-3`}>
64+
<p css={tw`text-yellow-900 text-sm text-center`}>
65+
This directory is too large to display in the browser,
66+
limiting the output to the first 250 files.
67+
</p>
68+
</div>
69+
}
70+
{
71+
sortFiles(files.slice(0, 250)).map(file => (
72+
<FileObjectRow key={file.uuid} file={file}/>
73+
))
74+
}
75+
<MassActionsBar/>
8276
</div>
8377
</CSSTransition>
8478
}

resources/scripts/components/server/files/FileObjectRow.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import { NavLink, useHistory, useRouteMatch } from 'react-router-dom';
1010
import tw from 'twin.macro';
1111
import isEqual from 'react-fast-compare';
1212
import styled from 'styled-components/macro';
13-
import FormikCheckbox from '@/components/elements/Checkbox';
13+
import Input from '@/components/elements/Input';
1414

1515
const Row = styled.div`
1616
${tw`flex bg-neutral-700 rounded-sm mb-px text-sm hover:text-neutral-100 cursor-pointer items-center no-underline hover:bg-neutral-600`};
1717
`;
1818

19-
const Checkbox = styled(FormikCheckbox)`
19+
const Checkbox = styled(Input)`
2020
&& {
2121
${tw`border-neutral-500`};
2222
@@ -28,7 +28,9 @@ const Checkbox = styled(FormikCheckbox)`
2828

2929
const FileObjectRow = ({ file }: { file: FileObject }) => {
3030
const directory = ServerContext.useStoreState(state => state.files.directory);
31+
const selectedFiles = ServerContext.useStoreState(state => state.files.selectedFiles);
3132
const setDirectory = ServerContext.useStoreActions(actions => actions.files.setDirectory);
33+
const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles);
3234

3335
const history = useHistory();
3436
const match = useRouteMatch();
@@ -56,7 +58,19 @@ const FileObjectRow = ({ file }: { file: FileObject }) => {
5658
}}
5759
>
5860
<label css={tw`flex-none p-4 absolute self-center z-30 cursor-pointer`}>
59-
<Checkbox name={'selectedFiles'} value={file.name}/>
61+
<Checkbox
62+
name={'selectedFiles'}
63+
value={file.name}
64+
checked={selectedFiles.indexOf(file.name) >= 0}
65+
type={'checkbox'}
66+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
67+
if (e.currentTarget.checked) {
68+
setSelectedFiles(selectedFiles.filter(f => f !== file.name).concat(file.name));
69+
} else {
70+
setSelectedFiles(selectedFiles.filter(f => f !== file.name));
71+
}
72+
}}
73+
/>
6074
</label>
6175
<NavLink
6276
to={`${match.url}/${file.isFile ? 'edit/' : ''}#${cleanDirectoryPath(`${directory}/${file.name}`)}`}

resources/scripts/components/server/files/MassActionsBar.tsx

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { useEffect, useState } from 'react';
22
import tw from 'twin.macro';
33
import Button from '@/components/elements/Button';
4-
import { useFormikContext } from 'formik';
54
import Fade from '@/components/elements/Fade';
65
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
76
import { faFileArchive, faLevelUpAlt, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
@@ -13,6 +12,7 @@ import useServer from '@/plugins/useServer';
1312
import { ServerContext } from '@/state/server';
1413
import ConfirmationModal from '@/components/elements/ConfirmationModal';
1514
import deleteFiles from '@/api/server/files/deleteFiles';
15+
import RenameFileModal from '@/components/server/files/RenameFileModal';
1616

1717
const MassActionsBar = () => {
1818
const { uuid } = useServer();
@@ -21,9 +21,12 @@ const MassActionsBar = () => {
2121
const [ loading, setLoading ] = useState(false);
2222
const [ loadingMessage, setLoadingMessage ] = useState('');
2323
const [ showConfirm, setShowConfirm ] = useState(false);
24-
const { values, setFieldValue } = useFormikContext<{ selectedFiles: string[] }>();
24+
const [ showMove, setShowMove ] = useState(false);
2525
const directory = ServerContext.useStoreState(state => state.files.directory);
2626

27+
const selectedFiles = ServerContext.useStoreState(state => state.files.selectedFiles);
28+
const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles);
29+
2730
useEffect(() => {
2831
if (!loading) setLoadingMessage('');
2932
}, [ loading ]);
@@ -33,9 +36,9 @@ const MassActionsBar = () => {
3336
clearFlashes('files');
3437
setLoadingMessage('Archiving files...');
3538

36-
compressFiles(uuid, directory, values.selectedFiles)
39+
compressFiles(uuid, directory, selectedFiles)
3740
.then(() => mutate())
38-
.then(() => setFieldValue('selectedFiles', []))
41+
.then(() => setSelectedFiles([]))
3942
.catch(error => clearAndAddHttpError({ key: 'files', error }))
4043
.then(() => setLoading(false));
4144
};
@@ -46,10 +49,10 @@ const MassActionsBar = () => {
4649
clearFlashes('files');
4750
setLoadingMessage('Deleting files...');
4851

49-
deleteFiles(uuid, directory, values.selectedFiles)
52+
deleteFiles(uuid, directory, selectedFiles)
5053
.then(() => {
51-
mutate(files => files.filter(f => values.selectedFiles.indexOf(f.name) < 0), false);
52-
setFieldValue('selectedFiles', []);
54+
mutate(files => files.filter(f => selectedFiles.indexOf(f.name) < 0), false);
55+
setSelectedFiles([]);
5356
})
5457
.catch(error => {
5558
mutate();
@@ -59,7 +62,7 @@ const MassActionsBar = () => {
5962
};
6063

6164
return (
62-
<Fade timeout={75} in={values.selectedFiles.length > 0} unmountOnExit>
65+
<Fade timeout={75} in={selectedFiles.length > 0} unmountOnExit>
6366
<div css={tw`fixed bottom-0 z-50 left-0 right-0 flex justify-center`}>
6467
<SpinnerOverlay visible={loading} size={'large'} fixed>
6568
{loadingMessage}
@@ -73,15 +76,17 @@ const MassActionsBar = () => {
7376
>
7477
Deleting files is a permanent operation, you cannot undo this action.
7578
</ConfirmationModal>
79+
<RenameFileModal
80+
files={selectedFiles}
81+
visible={showMove}
82+
useMoveTerminology
83+
onDismissed={() => setShowMove(false)}
84+
/>
7685
<div css={tw`rounded p-4 mb-6`} style={{ background: 'rgba(0, 0, 0, 0.35)' }}>
77-
<Button size={'xsmall'} css={tw`mr-4`}>
86+
<Button size={'xsmall'} css={tw`mr-4`} onClick={() => setShowMove(true)}>
7887
<FontAwesomeIcon icon={faLevelUpAlt} css={tw`mr-2`}/> Move
7988
</Button>
80-
<Button
81-
size={'xsmall'}
82-
css={tw`mr-4`}
83-
onClick={onClickCompress}
84-
>
89+
<Button size={'xsmall'} css={tw`mr-4`} onClick={onClickCompress}>
8590
<FontAwesomeIcon icon={faFileArchive} css={tw`mr-2`}/> Archive
8691
</Button>
8792
<Button size={'xsmall'} color={'red'} isSecondary onClick={() => setShowConfirm(true)}>

resources/scripts/components/server/files/RenameFileModal.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default ({ files, useMoveTerminology, ...props }: Props) => {
2222
const { mutate } = useFileManagerSwr();
2323
const { clearFlashes, clearAndAddHttpError } = useFlash();
2424
const directory = ServerContext.useStoreState(state => state.files.directory);
25+
const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles);
2526

2627
const submit = ({ name }: FormikValues, { setSubmitting }: FormikHelpers<FormikValues>) => {
2728
clearFlashes('files');
@@ -45,12 +46,14 @@ export default ({ files, useMoveTerminology, ...props }: Props) => {
4546
}
4647

4748
renameFiles(uuid, directory, data)
48-
.then(() => props.onDismissed())
49+
.then((): Promise<any> => files.length > 0 ? mutate() : Promise.resolve())
50+
.then(() => setSelectedFiles([]))
4951
.catch(error => {
5052
mutate();
5153
setSubmitting(false);
5254
clearAndAddHttpError({ key: 'files', error });
53-
});
55+
})
56+
.then(() => props.onDismissed());
5457
};
5558

5659
return (

resources/scripts/state/server/files.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@ import { cleanDirectoryPath } from '@/helpers';
33

44
export interface ServerFileStore {
55
directory: string;
6+
selectedFiles: string[];
7+
68
setDirectory: Action<ServerFileStore, string>;
9+
setSelectedFiles: Action<ServerFileStore, string[]>;
710
}
811

912
const files: ServerFileStore = {
1013
directory: '/',
14+
selectedFiles: [],
1115

1216
setDirectory: action((state, payload) => {
1317
state.directory = cleanDirectoryPath(payload);
1418
}),
19+
20+
setSelectedFiles: action((state, payload) => {
21+
state.selectedFiles = payload;
22+
}),
1523
};
1624

1725
export default files;

resources/scripts/state/server/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export const ServerContext = createContextStore<ServerStore>({
7777
state.databases.data = [];
7878
state.subusers.data = [];
7979
state.files.directory = '/';
80+
state.files.selectedFiles = [];
8081
state.backups.data = [];
8182
state.schedules.data = [];
8283

0 commit comments

Comments
 (0)