mirror of
https://github.com/zhigang1992/firecms.git
synced 2026-06-19 18:03:20 +08:00
Making NavigationBuilder async
This commit is contained in:
@@ -138,8 +138,8 @@ function SampleApp() {
|
||||
// models.firestore().useEmulator("localhost", 8080);
|
||||
};
|
||||
|
||||
const navigation: NavigationBuilder = ({user}: NavigationBuilderProps) => {
|
||||
const navigation:Navigation = {
|
||||
const navigation: NavigationBuilder = async ({ user }: NavigationBuilderProps) => {
|
||||
const navigation: Navigation = {
|
||||
collections: [
|
||||
productsCollection,
|
||||
usersCollection,
|
||||
|
||||
156
src/CMSApp.tsx
156
src/CMSApp.tsx
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import {
|
||||
createMuiTheme,
|
||||
createStyles,
|
||||
@@ -8,12 +7,6 @@ import {
|
||||
Theme,
|
||||
ThemeProvider
|
||||
} from "@material-ui/core";
|
||||
import { MuiPickersUtilsProvider } from "@material-ui/pickers";
|
||||
|
||||
import DateFnsUtils from "@date-io/date-fns";
|
||||
import * as locales from "date-fns/locale";
|
||||
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
import firebase from "firebase/app";
|
||||
import "firebase/analytics";
|
||||
@@ -26,62 +19,20 @@ import "typeface-space-mono";
|
||||
import CircularProgressCenter from "./components/CircularProgressCenter";
|
||||
import { Authenticator } from "./models";
|
||||
import { blue, pink, red } from "@material-ui/core/colors";
|
||||
import { BreadcrumbsProvider } from "./contexts/BreacrumbsContext";
|
||||
import { AuthContext, AuthProvider } from "./contexts/AuthContext";
|
||||
import { SnackbarProvider } from "./contexts/SnackbarContext";
|
||||
import { AppConfigProvider } from "./contexts/AppConfigContext";
|
||||
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { CMSAppProps, Navigation } from "./CMSAppProps";
|
||||
import { CMSAppProps } from "./CMSAppProps";
|
||||
import { LoginView } from "./LoginView";
|
||||
import { CMSDrawer } from "./CMSDrawer";
|
||||
import { CMSRouterSwitch } from "./CMSRouterSwitch";
|
||||
import { CMSAppBar } from "./components/CMSAppBar";
|
||||
import { EntitySideDialogs } from "./side_dialog/EntitySideDialogs";
|
||||
import { SideEntityProvider } from "./contexts/SideEntityController";
|
||||
import { SchemaRegistryProvider } from "./side_dialog/SchemaRegistry";
|
||||
import { CMSMainView } from "./CMSMainView";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
logo: {
|
||||
padding: theme.spacing(3),
|
||||
maxWidth: 240
|
||||
},
|
||||
main: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100vw",
|
||||
height: "100vh"
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflow: "auto"
|
||||
},
|
||||
tableNoBottomBorder: {
|
||||
"&:last-child th, &:last-child td": {
|
||||
borderBottom: 0
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
tree: {
|
||||
height: 216,
|
||||
flexGrow: 1,
|
||||
maxWidth: 400
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
export function CMSApp(props: CMSAppProps) {
|
||||
|
||||
const {
|
||||
name,
|
||||
logo,
|
||||
navigation: navigationOrCollections,
|
||||
navigation,
|
||||
authentication,
|
||||
allowSkipLogin,
|
||||
firebaseConfig,
|
||||
@@ -97,8 +48,6 @@ export function CMSApp(props: CMSAppProps) {
|
||||
]
|
||||
} = props;
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
const mode: "light" | "dark" = "light";
|
||||
const makeTheme = () => {
|
||||
|
||||
@@ -182,8 +131,6 @@ export function CMSApp(props: CMSAppProps) {
|
||||
};
|
||||
const theme = makeTheme();
|
||||
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
||||
|
||||
const [
|
||||
firebaseConfigInitialized,
|
||||
setFirebaseConfigInitialized
|
||||
@@ -247,79 +194,6 @@ export function CMSApp(props: CMSAppProps) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleDrawerToggle = () => setDrawerOpen(!drawerOpen);
|
||||
const closeDrawer = () => setDrawerOpen(false);
|
||||
|
||||
function renderLoginView() {
|
||||
return <LoginView logo={logo}
|
||||
skipLoginButtonEnabled={skipLoginButtonEnabled}
|
||||
signInOptions={signInOptions}/>;
|
||||
}
|
||||
|
||||
function getNavigation(user: firebase.User | null): Navigation {
|
||||
if (Array.isArray(navigationOrCollections)) {
|
||||
return {
|
||||
collections: navigationOrCollections
|
||||
};
|
||||
} else if (typeof navigationOrCollections === "function") {
|
||||
return navigationOrCollections({ user });
|
||||
} else {
|
||||
return navigationOrCollections;
|
||||
}
|
||||
}
|
||||
|
||||
function renderMainView(user: firebase.User | null) {
|
||||
|
||||
const dateUtilsLocale = locale ? locales[locale] : undefined;
|
||||
const navigation = getNavigation(user);
|
||||
|
||||
const collections = navigation.collections;
|
||||
const additionalViews = navigation.views;
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<SchemaRegistryProvider collections={collections}
|
||||
schemaResolver={schemaResolver}>
|
||||
<SideEntityProvider collections={collections}>
|
||||
<BreadcrumbsProvider>
|
||||
<MuiPickersUtilsProvider
|
||||
utils={DateFnsUtils}
|
||||
locale={dateUtilsLocale}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
|
||||
<nav>
|
||||
<CMSDrawer logo={logo}
|
||||
drawerOpen={drawerOpen}
|
||||
collections={collections}
|
||||
closeDrawer={closeDrawer}
|
||||
additionalViews={additionalViews}/>
|
||||
</nav>
|
||||
|
||||
<div className={classes.main}>
|
||||
<CMSAppBar title={name}
|
||||
handleDrawerToggle={handleDrawerToggle}
|
||||
toolbarExtraWidget={toolbarExtraWidget}/>
|
||||
|
||||
<main
|
||||
className={classes.content}>
|
||||
<CMSRouterSwitch
|
||||
collections={collections}
|
||||
views={additionalViews}/>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<EntitySideDialogs collections={collections}/>
|
||||
|
||||
</DndProvider>
|
||||
</MuiPickersUtilsProvider>
|
||||
</BreadcrumbsProvider>
|
||||
</SideEntityProvider>
|
||||
</SchemaRegistryProvider>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (configError) {
|
||||
return <div> {configError} </div>;
|
||||
}
|
||||
@@ -357,21 +231,32 @@ export function CMSApp(props: CMSAppProps) {
|
||||
const hasAccessToMainView = !authenticationEnabled || authContext.loggedUser || authContext.loginSkipped;
|
||||
|
||||
return (<>
|
||||
|
||||
<CssBaseline/>
|
||||
|
||||
{authContext.authLoading ?
|
||||
<CircularProgressCenter/>
|
||||
: (
|
||||
hasAccessToMainView ?
|
||||
renderMainView(authContext.loggedUser)
|
||||
<CMSMainView
|
||||
name={name}
|
||||
logo={logo}
|
||||
navigation={navigation}
|
||||
toolbarExtraWidget={toolbarExtraWidget}
|
||||
schemaResolver={schemaResolver}
|
||||
locale={locale}
|
||||
user={authContext.loggedUser}
|
||||
/>
|
||||
:
|
||||
renderLoginView()
|
||||
)}
|
||||
<LoginView logo={logo}
|
||||
skipLoginButtonEnabled={skipLoginButtonEnabled}
|
||||
signInOptions={signInOptions}/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</AuthContext.Consumer>
|
||||
</AuthProvider>
|
||||
</SnackbarProvider>
|
||||
@@ -379,4 +264,3 @@ export function CMSApp(props: CMSAppProps) {
|
||||
</AppConfigProvider>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface CMSAppProps {
|
||||
* In you need to customize the navigation based on the logged user you
|
||||
* can use a `NavigationBuilder`
|
||||
*/
|
||||
navigation: Navigation | NavigationBuilder | EntityCollection[] ;
|
||||
navigation: Navigation | NavigationBuilder | EntityCollection[];
|
||||
|
||||
/**
|
||||
* Do the users need to log in to access the CMS.
|
||||
@@ -116,8 +116,11 @@ export interface CMSAppProps {
|
||||
* You can use this builder to customize the navigation, based on the logged in
|
||||
* user
|
||||
*/
|
||||
export type NavigationBuilder = (props: NavigationBuilderProps) => Navigation;
|
||||
export type NavigationBuilderProps = { user: firebase.User | null }
|
||||
export type NavigationBuilder =
|
||||
((props: NavigationBuilderProps) => Promise<Navigation>)
|
||||
| ((props: NavigationBuilderProps) => Navigation);
|
||||
|
||||
export type NavigationBuilderProps = { user: firebase.User | null }
|
||||
|
||||
/**
|
||||
* In this interface you define the main navigation entries of the CMS
|
||||
|
||||
240
src/CMSMainView.tsx
Normal file
240
src/CMSMainView.tsx
Normal file
@@ -0,0 +1,240 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import {
|
||||
createMuiTheme,
|
||||
createStyles,
|
||||
CssBaseline,
|
||||
makeStyles,
|
||||
Theme,
|
||||
ThemeProvider
|
||||
} from "@material-ui/core";
|
||||
import { MuiPickersUtilsProvider } from "@material-ui/pickers";
|
||||
|
||||
import DateFnsUtils from "@date-io/date-fns";
|
||||
import * as locales from "date-fns/locale";
|
||||
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
import firebase from "firebase/app";
|
||||
import "firebase/analytics";
|
||||
import "firebase/auth";
|
||||
import "firebase/storage";
|
||||
import "firebase/firestore";
|
||||
|
||||
import "typeface-space-mono";
|
||||
|
||||
import CircularProgressCenter from "./components/CircularProgressCenter";
|
||||
import { Authenticator, EntityCollection } from "./models";
|
||||
import { blue, pink, red } from "@material-ui/core/colors";
|
||||
import { BreadcrumbsProvider } from "./contexts/BreacrumbsContext";
|
||||
import { AuthContext, AuthProvider } from "./contexts/AuthContext";
|
||||
import { SnackbarProvider } from "./contexts/SnackbarContext";
|
||||
import { AppConfigProvider } from "./contexts/AppConfigContext";
|
||||
|
||||
import { DndProvider } from "react-dnd";
|
||||
import {
|
||||
CMSAppProps,
|
||||
Locale,
|
||||
Navigation,
|
||||
NavigationBuilder
|
||||
} from "./CMSAppProps";
|
||||
import { LoginView } from "./LoginView";
|
||||
import { CMSDrawer } from "./CMSDrawer";
|
||||
import { CMSRouterSwitch } from "./CMSRouterSwitch";
|
||||
import { CMSAppBar } from "./components/CMSAppBar";
|
||||
import { EntitySideDialogs } from "./side_dialog/EntitySideDialogs";
|
||||
import { SideEntityProvider } from "./contexts/SideEntityController";
|
||||
import { SchemaRegistryProvider } from "./side_dialog/SchemaRegistry";
|
||||
import { SchemaResolver } from "./side_dialog/model";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
logo: {
|
||||
padding: theme.spacing(3),
|
||||
maxWidth: 240
|
||||
},
|
||||
main: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100vw",
|
||||
height: "100vh"
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflow: "auto"
|
||||
},
|
||||
tableNoBottomBorder: {
|
||||
"&:last-child th, &:last-child td": {
|
||||
borderBottom: 0
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
tree: {
|
||||
height: 216,
|
||||
flexGrow: 1,
|
||||
maxWidth: 400
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
export interface CMSMainViewProps {
|
||||
/**
|
||||
* Name of the app, displayed as the main title and in the tab title
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Logo to be displayed in the drawer of the CMS
|
||||
*/
|
||||
logo?: string;
|
||||
|
||||
/**
|
||||
* Logged in user
|
||||
*/
|
||||
user: firebase.User | null;
|
||||
|
||||
/**
|
||||
* Use this prop to specify the views that will be generated in the CMS.
|
||||
* You usually will want to create a `Navigation` object that includes
|
||||
* collection views where you specify the path and the schema.
|
||||
* Additionally you can add custom views to the root navigation.
|
||||
* In you need to customize the navigation based on the logged user you
|
||||
* can use a `NavigationBuilder`
|
||||
*/
|
||||
navigation: Navigation | NavigationBuilder | EntityCollection[] ;
|
||||
|
||||
/**
|
||||
* A component that gets rendered on the upper side of the main toolbar
|
||||
*/
|
||||
toolbarExtraWidget?: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Format of the dates in the CMS.
|
||||
* Defaults to 'MMMM dd, yyyy, HH:mm:ss'
|
||||
*/
|
||||
dateTimeFormat?: string;
|
||||
|
||||
/**
|
||||
* Locale of the CMS, currently only affecting dates
|
||||
*/
|
||||
locale?: Locale;
|
||||
|
||||
/**
|
||||
* Used to override schemas based on the collection path and entityId.
|
||||
* This resolver allows to override the schema for specific entities, or
|
||||
* specific collections, app wide. This overrides schemas all through the app.
|
||||
*
|
||||
* You can also override schemas in place, when using `useSideEntityController`
|
||||
*/
|
||||
schemaResolver?: SchemaResolver;
|
||||
}
|
||||
|
||||
|
||||
export function CMSMainView(props: CMSMainViewProps) {
|
||||
|
||||
const {
|
||||
name,
|
||||
logo,
|
||||
navigation: navigationOrCollections,
|
||||
toolbarExtraWidget,
|
||||
schemaResolver,
|
||||
locale,
|
||||
user,
|
||||
} = props;
|
||||
|
||||
const classes = useStyles();
|
||||
const dateUtilsLocale = locale ? locales[locale] : undefined;
|
||||
|
||||
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
||||
|
||||
const handleDrawerToggle = () => setDrawerOpen(!drawerOpen);
|
||||
const closeDrawer = () => setDrawerOpen(false);
|
||||
|
||||
const [navigation, setNavigation] = useState<Navigation>();
|
||||
const [navigationLoading, setNavigationLoading] = useState(true);
|
||||
const [navigationLoadingError, setNavigationLoadingError] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
setNavigationLoading(true);
|
||||
getNavigation().then((result: Navigation) => {
|
||||
setNavigation(result);
|
||||
}).catch(setNavigationLoadingError).finally(() => setNavigationLoading(false));
|
||||
}, [user, navigationOrCollections]);
|
||||
|
||||
async function getNavigation(): Promise<Navigation> {
|
||||
if (Array.isArray(navigationOrCollections)) {
|
||||
return {
|
||||
collections: navigationOrCollections
|
||||
};
|
||||
} else if (typeof navigationOrCollections === "function") {
|
||||
return navigationOrCollections({ user });
|
||||
} else {
|
||||
return navigationOrCollections;
|
||||
}
|
||||
}
|
||||
|
||||
if(navigationLoadingError){
|
||||
return <div>
|
||||
<p>There was an error while loading
|
||||
your navigation config</p>
|
||||
<p>{navigationLoadingError}</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
if(!navigation){
|
||||
return <CircularProgressCenter/>;
|
||||
}
|
||||
|
||||
const collections = navigation.collections;
|
||||
const additionalViews = navigation.views;
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<SchemaRegistryProvider collections={collections}
|
||||
schemaResolver={schemaResolver}>
|
||||
<SideEntityProvider collections={collections}>
|
||||
<BreadcrumbsProvider>
|
||||
<MuiPickersUtilsProvider
|
||||
utils={DateFnsUtils}
|
||||
locale={dateUtilsLocale}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
|
||||
<nav>
|
||||
<CMSDrawer logo={logo}
|
||||
drawerOpen={drawerOpen}
|
||||
collections={collections}
|
||||
closeDrawer={closeDrawer}
|
||||
additionalViews={additionalViews}/>
|
||||
</nav>
|
||||
|
||||
<div className={classes.main}>
|
||||
<CMSAppBar title={name}
|
||||
handleDrawerToggle={handleDrawerToggle}
|
||||
toolbarExtraWidget={toolbarExtraWidget}/>
|
||||
|
||||
<main
|
||||
className={classes.content}>
|
||||
<CMSRouterSwitch
|
||||
collections={collections}
|
||||
views={additionalViews}/>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<EntitySideDialogs
|
||||
collections={collections}/>
|
||||
|
||||
</DndProvider>
|
||||
</MuiPickersUtilsProvider>
|
||||
</BreadcrumbsProvider>
|
||||
</SideEntityProvider>
|
||||
</SchemaRegistryProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
}
|
||||
@@ -120,8 +120,8 @@ function HomeRoute({
|
||||
}
|
||||
|
||||
const groupViews
|
||||
= allGroups.map((group) => (
|
||||
<Box mt={6}>
|
||||
= allGroups.map((group, index) => (
|
||||
<Box mt={6} key={`group_${index}`}>
|
||||
<Typography color={"textSecondary"} className={"weight-500"}>
|
||||
{group?.toUpperCase() ?? "Ungrouped".toUpperCase()}
|
||||
</Typography>
|
||||
|
||||
Reference in New Issue
Block a user