Merge branch 'develop' into pagetitles2

This commit is contained in:
Charles Morgan 2020-08-01 22:03:07 -05:00 committed by GitHub
commit d604a4a5f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 215 additions and 82 deletions

View file

@ -1,4 +1,5 @@
import * as React from 'react';
import React, { useEffect } from 'react';
import ReactGA from 'react-ga';
import { hot } from 'react-hot-loader/root';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { StoreProvider } from 'easy-peasy';
@ -48,6 +49,11 @@ const App = () => {
store.getActions().settings.setSettings(SiteConfiguration!);
}
useEffect(() => {
ReactGA.initialize(SiteConfiguration!.analytics);
ReactGA.pageview(location.pathname);
}, []);
return (
<>
<GlobalStylesheet/>

View file

@ -0,0 +1,26 @@
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
import { ServerContext } from '@/state/server';
import useServer from '@/plugins/useServer';
const InstallListener = () => {
const server = useServer();
const getServer = ServerContext.useStoreActions(actions => actions.server.getServer);
const setServer = ServerContext.useStoreActions(actions => actions.server.setServer);
// Listen for the installation completion event and then fire off a request to fetch the updated
// server information. This allows the server to automatically become available to the user if they
// just sit on the page.
useWebsocketEvent('install completed', () => {
getServer(server.uuid).catch(error => console.error(error));
});
// When we see the install started event immediately update the state to indicate such so that the
// screens automatically update.
useWebsocketEvent('install started', () => {
setServer({ ...server, isInstalling: true });
});
return null;
};
export default InstallListener;

View file

@ -57,7 +57,7 @@ export default () => {
</div>
}
{featureLimits.backups === 0 &&
<p className="text-center text-sm text-neutral-400">
<p css={tw`text-center text-sm text-neutral-400`}>
Backups cannot be created for this server.
</p>
}

View file

@ -49,7 +49,7 @@ const ModalContent = ({ ...props }: RequiredModalProps) => {
</FormikFieldWrapper>
</div>
<div css={tw`flex justify-end`}>
<Button type={'submit'}>
<Button type={'submit'} disabled={isSubmitting}>
Start backup
</Button>
</div>
@ -94,11 +94,7 @@ export default () => {
ignored: string(),
})}
>
<ModalContent
appear
visible={visible}
onDismissed={() => setVisible(false)}
/>
<ModalContent appear visible={visible} onDismissed={() => setVisible(false)}/>
</Formik>
}
<Button onClick={() => setVisible(true)}>

View file

@ -1,4 +1,4 @@
import React, { useRef, useState } from 'react';
import React, { memo, useRef, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faBoxOpen,
@ -29,6 +29,7 @@ import styled from 'styled-components/macro';
import useEventListener from '@/plugins/useEventListener';
import compressFiles from '@/api/server/files/compressFiles';
import decompressFiles from '@/api/server/files/decompressFiles';
import isEqual from 'react-fast-compare';
type ModalType = 'rename' | 'move';
@ -50,7 +51,7 @@ const Row = ({ icon, title, ...props }: RowProps) => (
</StyledRow>
);
export default ({ file }: { file: FileObject }) => {
const FileDropdownMenu = ({ file }: { file: FileObject }) => {
const onClickRef = useRef<DropdownMenu>(null);
const [ showSpinner, setShowSpinner ] = useState(false);
const [ modal, setModal ] = useState<ModalType | null>(null);
@ -60,7 +61,7 @@ export default ({ file }: { file: FileObject }) => {
const { clearAndAddHttpError, clearFlashes } = useFlash();
const directory = ServerContext.useStoreState(state => state.files.directory);
useEventListener(`pterodactyl:files:ctx:${file.uuid}`, (e: CustomEvent) => {
useEventListener(`pterodactyl:files:ctx:${file.key}`, (e: CustomEvent) => {
if (onClickRef.current) {
onClickRef.current.triggerMenu(e.detail);
}
@ -71,7 +72,7 @@ export default ({ file }: { file: FileObject }) => {
// For UI speed, immediately remove the file from the listing before calling the deletion function.
// If the delete actually fails, we'll fetch the current directory contents again automatically.
mutate(files => files.filter(f => f.uuid !== file.uuid), false);
mutate(files => files.filter(f => f.key !== file.key), false);
deleteFiles(uuid, directory, [ file.name ]).catch(error => {
mutate();
@ -166,3 +167,5 @@ export default ({ file }: { file: FileObject }) => {
</DropdownMenu>
);
};
export default memo(FileDropdownMenu, isEqual);

View file

@ -71,7 +71,7 @@ export default () => {
}
{
sortFiles(files.slice(0, 250)).map(file => (
<FileObjectRow key={file.uuid} file={file}/>
<FileObjectRow key={file.key} file={file}/>
))
}
<MassActionsBar/>

View file

@ -39,7 +39,7 @@ const FileObjectRow = ({ file }: { file: FileObject }) => {
key={file.name}
onContextMenu={e => {
e.preventDefault();
window.dispatchEvent(new CustomEvent(`pterodactyl:files:ctx:${file.uuid}`, { detail: e.clientX }));
window.dispatchEvent(new CustomEvent(`pterodactyl:files:ctx:${file.key}`, { detail: e.clientX }));
}}
>
<SelectFileCheckbox name={file.name}/>

View file

@ -6,7 +6,6 @@ import Field from '@/components/elements/Field';
import { join } from 'path';
import { object, string } from 'yup';
import createDirectory from '@/api/server/files/createDirectory';
import v4 from 'uuid/v4';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
import { mutate } from 'swr';
@ -24,7 +23,7 @@ const schema = object().shape({
});
const generateDirectoryData = (name: string): FileObject => ({
uuid: v4(),
key: `dir_${name}`,
name: name,
mode: '0644',
size: 0,

View file

@ -9,23 +9,22 @@ import tw from 'twin.macro';
import Button from '@/components/elements/Button';
import useServer from '@/plugins/useServer';
import useFileManagerSwr from '@/plugins/useFileManagerSwr';
import useFlash from '@/plugins/useFlash';
import withFlash, { WithFlashProps } from '@/hoc/withFlash';
interface FormikValues {
name: string;
}
type Props = RequiredModalProps & { files: string[]; useMoveTerminology?: boolean };
type OwnProps = WithFlashProps & RequiredModalProps & { files: string[]; useMoveTerminology?: boolean };
export default ({ files, useMoveTerminology, ...props }: Props) => {
const RenameFileModal = ({ flash, files, useMoveTerminology, ...props }: OwnProps) => {
const { uuid } = useServer();
const { mutate } = useFileManagerSwr();
const { clearFlashes, clearAndAddHttpError } = useFlash();
const directory = ServerContext.useStoreState(state => state.files.directory);
const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles);
const submit = ({ name }: FormikValues, { setSubmitting }: FormikHelpers<FormikValues>) => {
clearFlashes('files');
flash.clearFlashes('files');
const len = name.split('/').length;
if (files.length === 1) {
@ -51,7 +50,7 @@ export default ({ files, useMoveTerminology, ...props }: Props) => {
.catch(error => {
mutate();
setSubmitting(false);
clearAndAddHttpError({ key: 'files', error });
flash.clearAndAddHttpError({ key: 'files', error });
})
.then(() => props.onDismissed());
};
@ -96,3 +95,5 @@ export default ({ files, useMoveTerminology, ...props }: Props) => {
</Formik>
);
};
export default withFlash(RenameFileModal);

View file

@ -65,7 +65,7 @@ const EditScheduleModal = ({ schedule, ...props }: Omit<Props, 'onScheduleUpdate
/>
</div>
<div css={tw`mt-6 text-right`}>
<Button type={'submit'}>
<Button type={'submit'} disabled={isSubmitting}>
{schedule ? 'Save changes' : 'Create schedule'}
</Button>
</div>

View file

@ -14,7 +14,7 @@ export default ({ schedule }: { schedule: Schedule }) => (
<p>{schedule.name}</p>
<p css={tw`text-xs text-neutral-400`}>
Last run
at: {schedule.lastRunAt ? format(schedule.lastRunAt, 'MMM Do [at] h:mma') : 'never'}
at: {schedule.lastRunAt ? format(schedule.lastRunAt, 'MMM do \'at\' h:mma') : 'never'}
</p>
</div>
<div css={tw`flex items-center mx-8`}>

View file

@ -32,11 +32,16 @@ interface Values {
}
const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
const { values: { action }, setFieldValue, setFieldTouched } = useFormikContext<Values>();
const { values: { action }, initialValues, setFieldValue, setFieldTouched, isSubmitting } = useFormikContext<Values>();
useEffect(() => {
setFieldValue('payload', action === 'power' ? 'start' : '');
setFieldTouched('payload', false);
if (action !== initialValues.action) {
setFieldValue('payload', action === 'power' ? 'start' : '');
setFieldTouched('payload', false);
} else {
setFieldValue('payload', initialValues.payload);
setFieldTouched('payload', false);
}
}, [ action ]);
return (
@ -94,7 +99,7 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
/>
</div>
<div css={tw`flex justify-end mt-6`}>
<Button type={'submit'}>
<Button type={'submit'} disabled={isSubmitting}>
{isEditingTask ? 'Save Changes' : 'Create Task'}
</Button>
</div>