misc_pterodactyl-panel/resources/scripts/components/server/files/FileEditContainer.tsx

134 lines
5.3 KiB
TypeScript
Raw Normal View History

2019-10-20 00:35:01 +00:00
import React, { lazy, useEffect, useState } from 'react';
import { ServerContext } from '@/state/server';
2019-10-20 00:35:01 +00:00
import getFileContents from '@/api/server/files/getFileContents';
import useRouter from 'use-react-router';
import { Actions, useStoreActions } from 'easy-peasy';
2019-10-26 20:16:27 +00:00
import { ApplicationStore } from '@/state';
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';
2019-12-22 00:38:40 +00:00
import { useParams } from 'react-router';
import FileNameModal from '@/components/server/files/FileNameModal';
import Can from '@/components/elements/Can';
import FlashMessageRender from '@/components/FlashMessageRender';
import PageContentBlock from '@/components/elements/PageContentBlock';
import ServerError from '@/components/screens/ServerError';
2019-10-12 22:29:45 +00:00
2019-10-19 22:31:02 +00:00
const LazyAceEditor = lazy(() => import(/* webpackChunkName: "editor" */'@/components/elements/AceEditor'));
2019-08-17 18:40:51 +00:00
export default () => {
const [ error, setError ] = useState('');
2019-12-22 00:38:40 +00:00
const { action } = useParams();
const { history, location: { hash } } = useRouter();
const [ loading, setLoading ] = useState(action === 'edit');
2019-10-20 00:35:01 +00:00
const [ content, setContent ] = useState('');
2019-12-22 00:38:40 +00:00
const [ modalVisible, setModalVisible ] = useState(false);
2019-10-26 20:16:27 +00:00
2019-12-22 00:38:40 +00:00
const { id, uuid } = ServerContext.useStoreState(state => state.server.data!);
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
2019-10-26 20:16:27 +00:00
let fetchFileContent: null | (() => Promise<string>) = null;
2019-10-12 22:29:45 +00:00
2019-12-22 00:38:40 +00:00
if (action !== 'new') {
useEffect(() => {
setLoading(true);
setError('');
2019-12-22 00:38:40 +00:00
getFileContents(uuid, hash.replace(/^#/, ''))
.then(setContent)
.catch(error => {
console.error(error);
setError(httpErrorToHuman(error));
})
2019-12-22 00:38:40 +00:00
.then(() => setLoading(false));
}, [ uuid, hash ]);
}
2019-08-17 18:40:51 +00:00
2019-12-22 00:38:40 +00:00
const save = (name?: string) => {
2019-10-26 20:16:27 +00:00
if (!fetchFileContent) {
return;
}
setLoading(true);
clearFlashes('files:view');
fetchFileContent()
.then(content => {
return saveFileContents(uuid, name || hash.replace(/^#/, ''), content);
})
2019-12-22 00:38:40 +00:00
.then(() => {
if (name) {
2019-12-22 01:43:50 +00:00
history.push(`/server/${id}/files/edit#/${name}`);
2019-12-22 00:38:40 +00:00
return;
}
return Promise.resolve();
2019-10-26 20:16:27 +00:00
})
.catch(error => {
console.error(error);
addError({ message: httpErrorToHuman(error), key: 'files:view' });
2019-10-26 20:16:27 +00:00
})
.then(() => setLoading(false));
};
if (error) {
return (
<ServerError
message={error}
onBack={() => history.goBack()}
/>
);
}
2019-08-17 18:40:51 +00:00
return (
<PageContentBlock>
<FlashMessageRender byKey={'files:view'} className={'mb-4'}/>
2019-12-22 00:38:40 +00:00
<FileManagerBreadcrumbs withinFileEditor={true} isNewFile={action !== 'edit'}/>
2020-04-20 03:04:39 +00:00
{(name || hash.replace(/^#/, '')).endsWith('.pteroignore') &&
<div className={'mb-4 p-4 border-l-4 bg-neutral-900 rounded border-cyan-400'}>
<p className={'text-neutral-300 text-sm'}>
You're editing a <code className={'font-mono bg-black rounded py-px px-1'}>.pteroignore</code> file.
Any files or directories listed in here will be excluded from backups. Wildcards are supported by
using an asterisk (<code className={'font-mono bg-black rounded py-px px-1'}>*</code>). You can
negate a prior rule by prepending an exclamation point
(<code className={'font-mono bg-black rounded py-px px-1'}>!</code>).
</p>
</div>
}
2019-12-22 00:38:40 +00:00
<FileNameModal
visible={modalVisible}
onDismissed={() => setModalVisible(false)}
onFileNamed={(name) => {
setModalVisible(false);
save(name);
}}
/>
2019-10-26 20:16:27 +00:00
<div className={'relative'}>
<SpinnerOverlay visible={loading}/>
<LazyAceEditor
initialModePath={hash.replace(/^#/, '') || 'plain_text'}
initialContent={content}
fetchContent={value => {
fetchFileContent = value;
}}
onContentSaved={() => null}
/>
</div>
2019-10-20 00:35:01 +00:00
<div className={'flex justify-end mt-4'}>
2019-12-22 00:38:40 +00:00
{action === 'edit' ?
<Can action={'file.update'}>
<button className={'btn btn-primary btn-sm'} onClick={() => save()}>
Save Content
</button>
</Can>
2019-12-22 00:38:40 +00:00
:
<Can action={'file.create'}>
<button className={'btn btn-primary btn-sm'} onClick={() => setModalVisible(true)}>
Create File
</button>
</Can>
2019-12-22 00:38:40 +00:00
}
2019-10-20 00:35:01 +00:00
</div>
</PageContentBlock>
2019-08-17 18:40:51 +00:00
);
};