From e78421864575885f141a3915af5890fd5f81ed78 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 21 Dec 2019 16:38:40 -0800 Subject: [PATCH] Add support for creating a new file --- .../scripts/components/elements/AceEditor.tsx | 2 +- .../server/files/FileEditContainer.tsx | 58 ++++++++++++++----- .../server/files/FileManagerBreadcrumbs.tsx | 17 ++++-- .../server/files/FileManagerContainer.tsx | 6 +- .../components/server/files/FileNameModal.tsx | 54 +++++++++++++++++ resources/scripts/routers/ServerRouter.tsx | 2 +- 6 files changed, 116 insertions(+), 23 deletions(-) create mode 100644 resources/scripts/components/server/files/FileNameModal.tsx diff --git a/resources/scripts/components/elements/AceEditor.tsx b/resources/scripts/components/elements/AceEditor.tsx index 238174d6f..3b01307fa 100644 --- a/resources/scripts/components/elements/AceEditor.tsx +++ b/resources/scripts/components/elements/AceEditor.tsx @@ -59,7 +59,7 @@ export interface Props { } export default ({ style, initialContent, initialModePath, fetchContent, onContentSaved }: Props) => { - const [ mode, setMode ] = useState('plain_text'); + const [ mode, setMode ] = useState('ace/mode/plain_text'); const [ editor, setEditor ] = useState(); const ref = useCallback(node => { diff --git a/resources/scripts/components/server/files/FileEditContainer.tsx b/resources/scripts/components/server/files/FileEditContainer.tsx index e8ff165f6..8c9b192b5 100644 --- a/resources/scripts/components/server/files/FileEditContainer.tsx +++ b/resources/scripts/components/server/files/FileEditContainer.tsx @@ -8,27 +8,33 @@ import { httpErrorToHuman } from '@/api/http'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import saveFileContents from '@/api/server/files/saveFileContents'; import FileManagerBreadcrumbs from '@/components/server/files/FileManagerBreadcrumbs'; +import { useParams } from 'react-router'; +import FileNameModal from '@/components/server/files/FileNameModal'; const LazyAceEditor = lazy(() => import(/* webpackChunkName: "editor" */'@/components/elements/AceEditor')); export default () => { - const { location: { hash } } = useRouter(); - const [ loading, setLoading ] = useState(true); + const { action } = useParams(); + const { history, location: { hash } } = useRouter(); + const [ loading, setLoading ] = useState(action === 'edit'); const [ content, setContent ] = useState(''); + const [ modalVisible, setModalVisible ] = useState(false); - const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const { id, uuid } = ServerContext.useStoreState(state => state.server.data!); const addError = useStoreState((state: Actions) => state.flashes.addError); let fetchFileContent: null | (() => Promise) = null; - useEffect(() => { - getFileContents(uuid, hash.replace(/^#/, '')) - .then(setContent) - .catch(error => console.error(error)) - .then(() => setLoading(false)); - }, [ uuid, hash ]); + if (action !== 'new') { + useEffect(() => { + getFileContents(uuid, hash.replace(/^#/, '')) + .then(setContent) + .catch(error => console.error(error)) + .then(() => setLoading(false)); + }, [ uuid, hash ]); + } - const save = (e: React.MouseEvent) => { + const save = (name?: string) => { if (!fetchFileContent) { return; } @@ -36,7 +42,15 @@ export default () => { setLoading(true); fetchFileContent() .then(content => { - return saveFileContents(uuid, hash.replace(/^#/, ''), content); + return saveFileContents(uuid, name || hash.replace(/^#/, ''), content); + }) + .then(() => { + if (name) { + history.push(`/server/${id}/files/edit#${hash.replace(/^#/, '')}/${name}`); + return; + } + + return Promise.resolve(); }) .catch(error => { console.error(error); @@ -47,7 +61,15 @@ export default () => { return (
- + + setModalVisible(false)} + onFileNamed={(name) => { + setModalVisible(false); + save(name); + }} + />
{ />
- + {action === 'edit' ? + + : + + }
); diff --git a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx index 7f670e3d0..d0bee1a86 100644 --- a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx +++ b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx @@ -1,8 +1,14 @@ import React, { useEffect, useState } from 'react'; import { ServerContext } from '@/state/server'; -import { NavLink } from 'react-router-dom'; +import { NavLink, useParams } from 'react-router-dom'; -export default ({ withinFileEditor }: { withinFileEditor?: boolean }) => { +interface Props { + withinFileEditor?: boolean; + isNewFile?: boolean; +} + +export default ({ withinFileEditor, isNewFile }: Props) => { + const { action } = useParams(); const [ file, setFile ] = useState(null); const id = ServerContext.useStoreState(state => state.server.data!.id); const directory = ServerContext.useStoreState(state => state.files.directory); @@ -11,12 +17,12 @@ export default ({ withinFileEditor }: { withinFileEditor?: boolean }) => { useEffect(() => { const parts = window.location.hash.replace(/^#(\/)*/, '/').split('/'); - if (withinFileEditor) { + if (withinFileEditor && !isNewFile) { setFile(parts.pop() || null); } setDirectory(parts.join('/')); - }, [ withinFileEditor, setDirectory ]); + }, [ withinFileEditor, isNewFile, setDirectory ]); const breadcrumbs = (): { name: string; path?: string }[] => directory.split('/') .filter(directory => !!directory) @@ -28,6 +34,9 @@ export default ({ withinFileEditor }: { withinFileEditor?: boolean }) => { return { name: directory, path: `/${dirs.slice(0, index + 1).join('/')}` }; }); + if (withinFileEditor) + console.log(breadcrumbs()); + return (
/home/ diff --git a/resources/scripts/components/server/files/FileManagerContainer.tsx b/resources/scripts/components/server/files/FileManagerContainer.tsx index 6e5b13320..2ed28f467 100644 --- a/resources/scripts/components/server/files/FileManagerContainer.tsx +++ b/resources/scripts/components/server/files/FileManagerContainer.tsx @@ -10,6 +10,7 @@ import FileObjectRow from '@/components/server/files/FileObjectRow'; import FileManagerBreadcrumbs from '@/components/server/files/FileManagerBreadcrumbs'; import { FileObject } from '@/api/server/files/loadDirectory'; import NewDirectoryButton from '@/components/server/files/NewDirectoryButton'; +import { Link } from 'react-router-dom'; const sortFiles = (files: FileObject[]): FileObject[] => { return files.sort((a, b) => a.name.localeCompare(b.name)) @@ -19,6 +20,7 @@ const sortFiles = (files: FileObject[]): FileObject[] => { export default () => { const [ loading, setLoading ] = useState(true); const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + const { id } = ServerContext.useStoreState(state => state.server.data!); const { contents: files, directory } = ServerContext.useStoreState(state => state.files); const { getDirectoryContents } = ServerContext.useStoreActions(actions => actions.files); @@ -79,9 +81,9 @@ export default () => { }
- +
} diff --git a/resources/scripts/components/server/files/FileNameModal.tsx b/resources/scripts/components/server/files/FileNameModal.tsx new file mode 100644 index 000000000..16aafc0d0 --- /dev/null +++ b/resources/scripts/components/server/files/FileNameModal.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import Modal, { RequiredModalProps } from '@/components/elements/Modal'; +import { Form, Formik, FormikActions } from 'formik'; +import { object, string } from 'yup'; +import Field from '@/components/elements/Field'; + +type Props = RequiredModalProps & { + onFileNamed: (name: string) => void; +}; + +interface Values { + fileName: string; +} + +export default ({ onFileNamed, onDismissed, ...props }: Props) => { + const submit = (values: Values, { setSubmitting }: FormikActions) => { + onFileNamed(values.fileName); + setSubmitting(false); + }; + + return ( + + {({ resetForm }) => ( + { + resetForm(); + onDismissed(); + }} + {...props} + > +
+ +
+ +
+ +
+ )} +
+ ); +}; diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 5867134ee..ac380a9bd 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -58,7 +58,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) (