kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
WrappedRoute: refactor with TSX+FC
rodzic
5af0e40ad2
commit
3c90937bf2
|
@ -2,7 +2,13 @@ import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import StickyBox from 'react-sticky-box';
|
import StickyBox from 'react-sticky-box';
|
||||||
|
|
||||||
const Layout: React.FC = ({ children }) => (
|
interface LayoutType extends React.FC {
|
||||||
|
Sidebar: React.FC,
|
||||||
|
Main: React.FC<React.HTMLAttributes<HTMLDivElement>>,
|
||||||
|
Aside: React.FC,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout: LayoutType = ({ children }) => (
|
||||||
<div className='sm:pt-4 relative pb-36'>
|
<div className='sm:pt-4 relative pb-36'>
|
||||||
<div className='max-w-3xl mx-auto sm:px-6 md:max-w-7xl md:px-8 md:grid md:grid-cols-12 md:gap-8'>
|
<div className='max-w-3xl mx-auto sm:px-6 md:max-w-7xl md:px-8 md:grid md:grid-cols-12 md:gap-8'>
|
||||||
{children}
|
{children}
|
||||||
|
@ -10,7 +16,6 @@ const Layout: React.FC = ({ children }) => (
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const Sidebar: React.FC = ({ children }) => (
|
const Sidebar: React.FC = ({ children }) => (
|
||||||
<div className='hidden lg:block lg:col-span-3'>
|
<div className='hidden lg:block lg:col-span-3'>
|
||||||
<StickyBox offsetTop={80} className='pb-4'>
|
<StickyBox offsetTop={80} className='pb-4'>
|
||||||
|
@ -37,11 +42,8 @@ const Aside: React.FC = ({ children }) => (
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
Layout.Sidebar = Sidebar;
|
Layout.Sidebar = Sidebar;
|
||||||
// @ts-ignore
|
|
||||||
Layout.Main = Main;
|
Layout.Main = Main;
|
||||||
// @ts-ignore
|
|
||||||
Layout.Aside = Aside;
|
Layout.Aside = Aside;
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { Layout } from '../../../components/ui';
|
|
||||||
|
|
||||||
export default @(component => injectIntl(component, { withRef: true }))
|
|
||||||
class ColumnsArea extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
columns: ImmutablePropTypes.list.isRequired,
|
|
||||||
children: PropTypes.node,
|
|
||||||
layout: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { children } = this.props;
|
|
||||||
const layout = this.props.layout || { LEFT: null, RIGHT: null };
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<Layout.Sidebar>
|
|
||||||
{layout.LEFT}
|
|
||||||
</Layout.Sidebar>
|
|
||||||
|
|
||||||
<Layout.Main>
|
|
||||||
{children}
|
|
||||||
</Layout.Main>
|
|
||||||
|
|
||||||
<Layout.Aside>
|
|
||||||
{layout.RIGHT}
|
|
||||||
</Layout.Aside>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Layout } from '../../../components/ui';
|
||||||
|
|
||||||
|
interface IColumnsArea {
|
||||||
|
layout: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColumnsArea: React.FC<IColumnsArea> = (props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const layout = props.layout || { LEFT: null, RIGHT: null };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Layout.Sidebar>
|
||||||
|
{layout.LEFT}
|
||||||
|
</Layout.Sidebar>
|
||||||
|
|
||||||
|
<Layout.Main>
|
||||||
|
{children}
|
||||||
|
</Layout.Main>
|
||||||
|
|
||||||
|
<Layout.Aside>
|
||||||
|
{layout.RIGHT}
|
||||||
|
</Layout.Aside>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColumnsArea;
|
|
@ -1,11 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
|
||||||
|
|
||||||
import ColumnsArea from '../components/columns_area';
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
columns: getSettings(state).get('columns'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, null, null, { forwardRef: true })(ColumnsArea);
|
|
|
@ -1,131 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Redirect, Route } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
|
||||||
|
|
||||||
import BundleColumnError from '../components/bundle_column_error';
|
|
||||||
import ColumnForbidden from '../components/column_forbidden';
|
|
||||||
import ColumnLoading from '../components/column_loading';
|
|
||||||
import BundleContainer from '../containers/bundle_container';
|
|
||||||
import ColumnsAreaContainer from '../containers/columns_area_container';
|
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
|
||||||
const me = state.get('me');
|
|
||||||
|
|
||||||
return {
|
|
||||||
account: state.getIn(['accounts', me]),
|
|
||||||
settings: getSettings(state),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class WrappedRoute extends React.Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
component: PropTypes.func.isRequired,
|
|
||||||
page: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
|
||||||
content: PropTypes.node,
|
|
||||||
componentParams: PropTypes.object,
|
|
||||||
layout: PropTypes.object,
|
|
||||||
account: ImmutablePropTypes.record,
|
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
|
||||||
publicRoute: PropTypes.bool,
|
|
||||||
staffOnly: PropTypes.bool,
|
|
||||||
adminOnly: PropTypes.bool,
|
|
||||||
developerOnly: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
componentParams: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
renderComponent = ({ match }) => {
|
|
||||||
const { component, content, componentParams, layout, page: Page } = this.props;
|
|
||||||
|
|
||||||
if (Page) {
|
|
||||||
return (
|
|
||||||
<BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
|
|
||||||
{Component =>
|
|
||||||
(
|
|
||||||
<Page params={match.params} layout={layout} {...componentParams}>
|
|
||||||
<Component params={match.params} {...componentParams}>
|
|
||||||
{content}
|
|
||||||
</Component>
|
|
||||||
</Page>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</BundleContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
|
|
||||||
{Component =>
|
|
||||||
(
|
|
||||||
<ColumnsAreaContainer layout={layout}>
|
|
||||||
<Component params={match.params} {...componentParams}>
|
|
||||||
{content}
|
|
||||||
</Component>
|
|
||||||
</ColumnsAreaContainer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</BundleContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLoading = () => {
|
|
||||||
return (
|
|
||||||
<ColumnsAreaContainer layout={this.props.layout}>
|
|
||||||
<ColumnLoading />
|
|
||||||
</ColumnsAreaContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderForbidden = () => {
|
|
||||||
return (
|
|
||||||
<ColumnsAreaContainer layout={this.props.layout}>
|
|
||||||
<ColumnForbidden />
|
|
||||||
</ColumnsAreaContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderError = (props) => {
|
|
||||||
return (
|
|
||||||
<ColumnsAreaContainer layout={this.props.layout}>
|
|
||||||
<BundleColumnError {...props} />
|
|
||||||
</ColumnsAreaContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
loginRedirect = () => {
|
|
||||||
const actualUrl = encodeURIComponent(`${this.props.computedMatch.url}${this.props.location.search}`); // eslint-disable-line react/prop-types
|
|
||||||
return <Redirect to={`/login?redirect_uri=${actualUrl}`} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { component: Component, content, account, settings, publicRoute, developerOnly, staffOnly, adminOnly, ...rest } = this.props;
|
|
||||||
|
|
||||||
const authorized = [
|
|
||||||
account || publicRoute,
|
|
||||||
developerOnly ? settings.get('isDeveloper') : true,
|
|
||||||
staffOnly ? account && account.staff : true,
|
|
||||||
adminOnly ? account && account.admin : true,
|
|
||||||
].every(c => c);
|
|
||||||
|
|
||||||
if (!authorized) {
|
|
||||||
if (!account) {
|
|
||||||
return this.loginRedirect();
|
|
||||||
} else {
|
|
||||||
return this.renderForbidden();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Route {...rest} render={this.renderComponent} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappedRoute = connect(mapStateToProps)(WrappedRoute);
|
|
||||||
export { wrappedRoute as WrappedRoute };
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Redirect, Route, useHistory, RouteComponentProps, match as MatchType } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useOwnAccount, useSettings } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import BundleColumnError from '../components/bundle_column_error';
|
||||||
|
import ColumnForbidden from '../components/column_forbidden';
|
||||||
|
import ColumnLoading from '../components/column_loading';
|
||||||
|
import ColumnsArea from '../components/columns_area';
|
||||||
|
import BundleContainer from '../containers/bundle_container';
|
||||||
|
|
||||||
|
type PageProps = {
|
||||||
|
params?: MatchType['params'],
|
||||||
|
layout?: any,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IWrappedRoute {
|
||||||
|
component: (...args: any[]) => any,
|
||||||
|
page: React.ComponentType<PageProps>,
|
||||||
|
content: React.ReactNode,
|
||||||
|
componentParams: Record<string, any>,
|
||||||
|
layout: any,
|
||||||
|
publicRoute?: boolean,
|
||||||
|
staffOnly?: boolean,
|
||||||
|
adminOnly?: boolean,
|
||||||
|
developerOnly?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
const WrappedRoute: React.FC<IWrappedRoute> = ({
|
||||||
|
component,
|
||||||
|
page: Page,
|
||||||
|
content,
|
||||||
|
componentParams = {},
|
||||||
|
layout,
|
||||||
|
publicRoute = false,
|
||||||
|
staffOnly = false,
|
||||||
|
adminOnly = false,
|
||||||
|
developerOnly = false,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const account = useOwnAccount();
|
||||||
|
const settings = useSettings();
|
||||||
|
|
||||||
|
const renderComponent = ({ match }: RouteComponentProps) => {
|
||||||
|
if (Page) {
|
||||||
|
return (
|
||||||
|
<BundleContainer fetchComponent={component} loading={renderLoading} error={renderError}>
|
||||||
|
{Component =>
|
||||||
|
(
|
||||||
|
<Page params={match.params} layout={layout} {...componentParams}>
|
||||||
|
<Component params={match.params} {...componentParams}>
|
||||||
|
{content}
|
||||||
|
</Component>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</BundleContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BundleContainer fetchComponent={component} loading={renderLoading} error={renderError}>
|
||||||
|
{Component =>
|
||||||
|
(
|
||||||
|
<ColumnsArea layout={layout}>
|
||||||
|
<Component params={match.params} {...componentParams}>
|
||||||
|
{content}
|
||||||
|
</Component>
|
||||||
|
</ColumnsArea>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</BundleContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderLoading = () => {
|
||||||
|
return (
|
||||||
|
<ColumnsArea layout={layout}>
|
||||||
|
<ColumnLoading />
|
||||||
|
</ColumnsArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderForbidden = () => {
|
||||||
|
return (
|
||||||
|
<ColumnsArea layout={layout}>
|
||||||
|
<ColumnForbidden />
|
||||||
|
</ColumnsArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderError = (props: any) => {
|
||||||
|
return (
|
||||||
|
<ColumnsArea layout={layout}>
|
||||||
|
<BundleColumnError {...props} />
|
||||||
|
</ColumnsArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginRedirect = () => {
|
||||||
|
const actualUrl = encodeURIComponent(`${history.location.pathname}${history.location.search}`);
|
||||||
|
return <Redirect to={`/login?redirect_uri=${actualUrl}`} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const authorized = [
|
||||||
|
account || publicRoute,
|
||||||
|
developerOnly ? settings.get('isDeveloper') : true,
|
||||||
|
staffOnly ? account && account.staff : true,
|
||||||
|
adminOnly ? account && account.admin : true,
|
||||||
|
].every(c => c);
|
||||||
|
|
||||||
|
if (!authorized) {
|
||||||
|
if (!account) {
|
||||||
|
return loginRedirect();
|
||||||
|
} else {
|
||||||
|
return renderForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Route {...rest} render={renderComponent} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
WrappedRoute,
|
||||||
|
};
|
Ładowanie…
Reference in New Issue