Merge pull request #2954 from pterodactyl/fix/file-manager-transitions
use children in routes instead of component prop
This commit is contained in:
commit
e8c2b2b464
7 changed files with 80 additions and 89 deletions
|
@ -1,10 +1,9 @@
|
||||||
import React, { useRef } from 'react';
|
import React from 'react';
|
||||||
import { Route } from 'react-router';
|
import { Route } from 'react-router';
|
||||||
import { SwitchTransition } from 'react-transition-group';
|
import { SwitchTransition } from 'react-transition-group';
|
||||||
import Fade from '@/components/elements/Fade';
|
import Fade from '@/components/elements/Fade';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
import tw from 'twin.macro';
|
import tw from 'twin.macro';
|
||||||
import v4 from 'uuid/v4';
|
|
||||||
|
|
||||||
const StyledSwitchTransition = styled(SwitchTransition)`
|
const StyledSwitchTransition = styled(SwitchTransition)`
|
||||||
${tw`relative`};
|
${tw`relative`};
|
||||||
|
@ -15,13 +14,11 @@ const StyledSwitchTransition = styled(SwitchTransition)`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TransitionRouter: React.FC = ({ children }) => {
|
const TransitionRouter: React.FC = ({ children }) => {
|
||||||
const uuid = useRef(v4()).current;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Route
|
<Route
|
||||||
render={({ location }) => (
|
render={({ location }) => (
|
||||||
<StyledSwitchTransition>
|
<StyledSwitchTransition>
|
||||||
<Fade timeout={150} key={location.key || uuid} in appear unmountOnExit>
|
<Fade timeout={150} key={location.pathname + location.search} in appear unmountOnExit>
|
||||||
<section>
|
<section>
|
||||||
{children}
|
{children}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Can from '@/components/elements/Can';
|
||||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||||
import getServerAllocations from '@/api/swr/getServerAllocations';
|
import getServerAllocations from '@/api/swr/getServerAllocations';
|
||||||
import isEqual from 'react-fast-compare';
|
import isEqual from 'react-fast-compare';
|
||||||
import { Allocation } from '@/api/server/getServer';
|
|
||||||
|
|
||||||
const NetworkContainer = () => {
|
const NetworkContainer = () => {
|
||||||
const [ loading, setLoading ] = useState(false);
|
const [ loading, setLoading ] = useState(false);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import getServerSchedules from '@/api/server/schedules/getServerSchedules';
|
import getServerSchedules from '@/api/server/schedules/getServerSchedules';
|
||||||
import { ServerContext } from '@/state/server';
|
import { ServerContext } from '@/state/server';
|
||||||
import Spinner from '@/components/elements/Spinner';
|
import Spinner from '@/components/elements/Spinner';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { useHistory, useRouteMatch } from 'react-router-dom';
|
||||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
import ScheduleRow from '@/components/server/schedules/ScheduleRow';
|
import ScheduleRow from '@/components/server/schedules/ScheduleRow';
|
||||||
import { httpErrorToHuman } from '@/api/http';
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
|
@ -14,7 +14,10 @@ import GreyRowBox from '@/components/elements/GreyRowBox';
|
||||||
import Button from '@/components/elements/Button';
|
import Button from '@/components/elements/Button';
|
||||||
import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
import ServerContentBlock from '@/components/elements/ServerContentBlock';
|
||||||
|
|
||||||
export default ({ match, history }: RouteComponentProps) => {
|
export default () => {
|
||||||
|
const match = useRouteMatch();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
||||||
const { clearFlashes, addError } = useFlash();
|
const { clearFlashes, addError } = useFlash();
|
||||||
const [ loading, setLoading ] = useState(true);
|
const [ loading, setLoading ] = useState(true);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||||
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
||||||
import getServerSchedule from '@/api/server/schedules/getServerSchedule';
|
import getServerSchedule from '@/api/server/schedules/getServerSchedule';
|
||||||
import Spinner from '@/components/elements/Spinner';
|
import Spinner from '@/components/elements/Spinner';
|
||||||
|
@ -45,7 +45,11 @@ const ActivePill = ({ active }: { active: boolean }) => (
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ({ match, history, location: { state } }: RouteComponentProps<Params, Record<string, unknown>, State>) => {
|
export default () => {
|
||||||
|
const params = useParams() as Params;
|
||||||
|
const history = useHistory();
|
||||||
|
const state: State = useLocation().state;
|
||||||
|
|
||||||
const id = ServerContext.useStoreState(state => state.server.data!.id);
|
const id = ServerContext.useStoreState(state => state.server.data!.id);
|
||||||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
||||||
|
|
||||||
|
@ -57,20 +61,20 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
|
||||||
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (schedule?.id === Number(match.params.id)) {
|
if (schedule?.id === Number(params.id)) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFlashes('schedules');
|
clearFlashes('schedules');
|
||||||
getServerSchedule(uuid, Number(match.params.id))
|
getServerSchedule(uuid, Number(params.id))
|
||||||
.then(schedule => appendSchedule(schedule))
|
.then(schedule => appendSchedule(schedule))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
clearAndAddHttpError({ error, key: 'schedules' });
|
clearAndAddHttpError({ error, key: 'schedules' });
|
||||||
})
|
})
|
||||||
.then(() => setIsLoading(false));
|
.then(() => setIsLoading(false));
|
||||||
}, [ match ]);
|
}, [ params ]);
|
||||||
|
|
||||||
const toggleEditModal = useCallback(() => {
|
const toggleEditModal = useCallback(() => {
|
||||||
setShowEditModal(s => !s);
|
setShowEditModal(s => !s);
|
||||||
|
|
25
resources/scripts/hoc/RequireServerPermission.tsx
Normal file
25
resources/scripts/hoc/RequireServerPermission.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Can from '@/components/elements/Can';
|
||||||
|
import ScreenBlock from '@/components/screens/ScreenBlock';
|
||||||
|
export interface RequireServerPermissionProps {
|
||||||
|
permissions: string | string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const RequireServerPermission: React.FC<RequireServerPermissionProps> = ({ children, permissions }) => {
|
||||||
|
return (
|
||||||
|
<Can
|
||||||
|
action={permissions}
|
||||||
|
renderOnError={
|
||||||
|
<ScreenBlock
|
||||||
|
image={'/assets/svgs/server_error.svg'}
|
||||||
|
title={'Access Denied'}
|
||||||
|
message={'You do not have permission to access this page.'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Can>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RequireServerPermission;
|
|
@ -1,31 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import Can from '@/components/elements/Can';
|
|
||||||
import ScreenBlock from '@/components/screens/ScreenBlock';
|
|
||||||
import isEqual from 'react-fast-compare';
|
|
||||||
|
|
||||||
const requireServerPermission = (Component: React.ComponentType<any>, permissions: string | string[]) => {
|
|
||||||
return class extends React.Component<any, any> {
|
|
||||||
shouldComponentUpdate (nextProps: Readonly<any>) {
|
|
||||||
return !isEqual(nextProps, this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<Can
|
|
||||||
action={permissions}
|
|
||||||
renderOnError={
|
|
||||||
<ScreenBlock
|
|
||||||
image={'/assets/svgs/server_error.svg'}
|
|
||||||
title={'Access Denied'}
|
|
||||||
message={'You do not have permission to access this page.'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Component {...this.props}/>
|
|
||||||
</Can>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default requireServerPermission;
|
|
|
@ -27,10 +27,10 @@ import SubNavigation from '@/components/elements/SubNavigation';
|
||||||
import NetworkContainer from '@/components/server/network/NetworkContainer';
|
import NetworkContainer from '@/components/server/network/NetworkContainer';
|
||||||
import InstallListener from '@/components/server/InstallListener';
|
import InstallListener from '@/components/server/InstallListener';
|
||||||
import StartupContainer from '@/components/server/startup/StartupContainer';
|
import StartupContainer from '@/components/server/startup/StartupContainer';
|
||||||
import requireServerPermission from '@/hoc/requireServerPermission';
|
|
||||||
import ErrorBoundary from '@/components/elements/ErrorBoundary';
|
import ErrorBoundary from '@/components/elements/ErrorBoundary';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
|
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import RequireServerPermission from '@/hoc/RequireServerPermission';
|
||||||
|
|
||||||
const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
|
const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
|
||||||
const rootAdmin = useStoreState(state => state.user.data!.rootAdmin);
|
const rootAdmin = useStoreState(state => state.user.data!.rootAdmin);
|
||||||
|
@ -142,50 +142,44 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
|
||||||
<TransitionRouter>
|
<TransitionRouter>
|
||||||
<Switch location={location}>
|
<Switch location={location}>
|
||||||
<Route path={`${match.path}`} component={ServerConsole} exact/>
|
<Route path={`${match.path}`} component={ServerConsole} exact/>
|
||||||
<Route
|
<Route path={`${match.path}/files`} exact>
|
||||||
path={`${match.path}/files`}
|
<RequireServerPermission permissions={'file.*'}>
|
||||||
component={requireServerPermission(FileManagerContainer, 'file.*')}
|
<FileManagerContainer />
|
||||||
exact
|
</RequireServerPermission>
|
||||||
/>
|
</Route>
|
||||||
<Route
|
<Route path={`${match.path}/files/:action(edit|new)`} exact>
|
||||||
path={`${match.path}/files/:action(edit|new)`}
|
<SuspenseSpinner>
|
||||||
render={props => (
|
<FileEditContainer />
|
||||||
<SuspenseSpinner>
|
</SuspenseSpinner>
|
||||||
<FileEditContainer {...props as any}/>
|
</Route>
|
||||||
</SuspenseSpinner>
|
<Route path={`${match.path}/databases`} exact>
|
||||||
)}
|
<RequireServerPermission permissions={'database.*'}>
|
||||||
exact
|
<DatabasesContainer />
|
||||||
/>
|
</RequireServerPermission>
|
||||||
<Route
|
</Route>
|
||||||
path={`${match.path}/databases`}
|
<Route path={`${match.path}/schedules`} exact>
|
||||||
component={requireServerPermission(DatabasesContainer, 'database.*')}
|
<RequireServerPermission permissions={'schedule.*'}>
|
||||||
exact
|
<ScheduleContainer />
|
||||||
/>
|
</RequireServerPermission>
|
||||||
<Route
|
</Route>
|
||||||
path={`${match.path}/schedules`}
|
<Route path={`${match.path}/schedules/:id`} exact>
|
||||||
component={requireServerPermission(ScheduleContainer, 'schedule.*')}
|
<ScheduleEditContainer/>
|
||||||
exact
|
</Route>
|
||||||
/>
|
<Route path={`${match.path}/users`} exact>
|
||||||
<Route
|
<RequireServerPermission permissions={'user.*'}>
|
||||||
path={`${match.path}/schedules/:id`}
|
<UsersContainer />
|
||||||
component={ScheduleEditContainer}
|
</RequireServerPermission>
|
||||||
exact
|
</Route>
|
||||||
/>
|
<Route path={`${match.path}/backups`} exact>
|
||||||
<Route
|
<RequireServerPermission permissions={'backup.*'}>
|
||||||
path={`${match.path}/users`}
|
<BackupContainer />
|
||||||
component={requireServerPermission(UsersContainer, 'user.*')}
|
</RequireServerPermission>
|
||||||
exact
|
</Route>
|
||||||
/>
|
<Route path={`${match.path}/network`} exact>
|
||||||
<Route
|
<RequireServerPermission permissions={'allocation.*'}>
|
||||||
path={`${match.path}/backups`}
|
<NetworkContainer />
|
||||||
component={requireServerPermission(BackupContainer, 'backup.*')}
|
</RequireServerPermission>
|
||||||
exact
|
</Route>
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`${match.path}/network`}
|
|
||||||
component={requireServerPermission(NetworkContainer, 'allocation.*')}
|
|
||||||
exact
|
|
||||||
/>
|
|
||||||
<Route path={`${match.path}/startup`} component={StartupContainer} exact/>
|
<Route path={`${match.path}/startup`} component={StartupContainer} exact/>
|
||||||
<Route path={`${match.path}/settings`} component={SettingsContainer} exact/>
|
<Route path={`${match.path}/settings`} component={SettingsContainer} exact/>
|
||||||
<Route path={'*'} component={NotFound}/>
|
<Route path={'*'} component={NotFound}/>
|
||||||
|
|
Loading…
Reference in a new issue