mirror of
https://github.com/zhigang1992/firecms.git
synced 2026-04-27 19:13:41 +08:00
Removed schema registry and merged with navigation context
This commit is contained in:
@@ -26,8 +26,6 @@ import { BreadcrumbsProvider } from "./contexts/BreacrumbsContext";
|
||||
import { ModeProvider, ModeStateContext } from "./contexts/ModeState";
|
||||
import { useBuildSideEntityController } from "./internal/useBuildSideEntityController";
|
||||
import { useBuildNavigationContext } from "./internal/useBuildNavigationContext";
|
||||
|
||||
import { useBuildSchemaRegistryController } from "./contexts/useBuildSchemaRegistryController";
|
||||
import { useBuildAuthController } from "./internal/useBuildAuthController";
|
||||
|
||||
const DEFAULT_COLLECTION_PATH = `/c`;
|
||||
@@ -180,11 +178,11 @@ export function FireCMS<UserType>(props: FireCMSProps<UserType>) {
|
||||
dateTimeFormat,
|
||||
locale,
|
||||
dataSource,
|
||||
storageSource
|
||||
storageSource,
|
||||
schemaOverrideHandler
|
||||
});
|
||||
|
||||
const schemaRegistryController = useBuildSchemaRegistryController(navigationContext, schemaOverrideHandler);
|
||||
const sideEntityController = useBuildSideEntityController(navigationContext, schemaRegistryController);
|
||||
const sideEntityController = useBuildSideEntityController(navigationContext);
|
||||
|
||||
const loading = authController.authLoading || authController.initialLoading || navigationContext.loading;
|
||||
|
||||
@@ -212,7 +210,6 @@ export function FireCMS<UserType>(props: FireCMSProps<UserType>) {
|
||||
navigationContext,
|
||||
dataSource,
|
||||
storageSource,
|
||||
schemaRegistryController,
|
||||
snackbarController
|
||||
};
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@ import { EntityCollectionResolver, SideEntityPanelProps } from "../models";
|
||||
import { SideDialogDrawer } from "./internal/SideDialogDrawer";
|
||||
import { EntityView } from "./internal/EntityView";
|
||||
import { CONTAINER_WIDTH } from "./internal/common";
|
||||
import { useSideEntityController } from "../hooks";
|
||||
import { useNavigation, useSideEntityController } from "../hooks";
|
||||
import { ErrorBoundary } from "./internal/ErrorBoundary";
|
||||
import {
|
||||
UnsavedChangesDialog,
|
||||
useNavigationUnsavedChangesDialog
|
||||
} from "./internal/useUnsavedChangesDialog";
|
||||
import { useSchemaRegistryController } from "../hooks/useSchemaRegistryController";
|
||||
import { computeSchema } from "./utils";
|
||||
|
||||
/**
|
||||
@@ -79,8 +78,8 @@ function SideEntityDialog({
|
||||
};
|
||||
|
||||
const sideEntityController = useSideEntityController();
|
||||
const schemaRegistry = useSchemaRegistryController();
|
||||
const schemaProps: EntityCollectionResolver | undefined = schemaRegistry.getSchemaConfig(panel.path, panel.entityId);
|
||||
const navigationContext = useNavigation();
|
||||
const schemaProps: EntityCollectionResolver | undefined = navigationContext.getCollectionResolver(panel.path, panel.entityId);
|
||||
if (!schemaProps) {
|
||||
throw Error("ERROR: You are trying to open an entity with no schema defined.");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ReferenceDialog } from "../../../ReferenceDialog";
|
||||
import { getPreviewSizeFrom } from "../../../../../preview/util";
|
||||
import { useInputStyles } from "./styles";
|
||||
import { getReferenceFrom } from "../../../../utils";
|
||||
import { useFireCMSContext } from "../../../../../hooks";
|
||||
import { useFireCMSContext, useNavigation } from "../../../../../hooks";
|
||||
|
||||
|
||||
export function TableReferenceField<M extends { [Key: string]: any }>(props: {
|
||||
@@ -55,8 +55,8 @@ export function TableReferenceField<M extends { [Key: string]: any }>(props: {
|
||||
const [onHover, setOnHover] = useState(false);
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
const schemaRegistry = useFireCMSContext().schemaRegistryController;
|
||||
const collectionResolver = schemaRegistry.getCollectionResolver(path);
|
||||
const navigationContext = useNavigation();
|
||||
const collectionResolver = navigationContext.getCollectionResolver(path);
|
||||
if (!collectionResolver) {
|
||||
throw Error(`Couldn't find the corresponding collection view for the path: ${path}`);
|
||||
}
|
||||
|
||||
@@ -28,10 +28,9 @@ import { canCreate, canDelete, canEdit } from "../util/permissions";
|
||||
import { Markdown } from "../../preview";
|
||||
import {
|
||||
useAuthController,
|
||||
useFireCMSContext,
|
||||
useFireCMSContext, useNavigation,
|
||||
useSideEntityController
|
||||
} from "../../hooks";
|
||||
import { useSchemaRegistryController } from "../../hooks/useSchemaRegistryController";
|
||||
import { useCollectionPersist } from "../internal/useCollectionPersist";
|
||||
|
||||
/**
|
||||
@@ -109,7 +108,7 @@ export function EntityCollectionView<M extends { [Key: string]: any }>({
|
||||
const sideEntityController = useSideEntityController();
|
||||
const context = useFireCMSContext();
|
||||
const authController = useAuthController();
|
||||
const schemaRegistry = useSchemaRegistryController();
|
||||
const navigationContext = useNavigation();
|
||||
|
||||
const theme = useTheme();
|
||||
const largeLayout = useMediaQuery(theme.breakpoints.up("md"));
|
||||
@@ -122,7 +121,7 @@ export function EntityCollectionView<M extends { [Key: string]: any }>({
|
||||
|
||||
const collection = persistedCollection ?? baseCollection;
|
||||
|
||||
const schemaConfig = schemaRegistry.getSchemaConfig(path);
|
||||
const schemaConfig = navigationContext.getCollectionResolver<M>(path);
|
||||
if (!schemaConfig) {
|
||||
throw Error(`Couldn't find the corresponding schemaConfig for the path: ${path}`);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { FireCMSContext } from "../../models";
|
||||
export const FireCMSContextInstance = React.createContext<FireCMSContext>({
|
||||
sideEntityController: {} as any,
|
||||
navigationContext: {} as any,
|
||||
schemaRegistryController: {} as any,
|
||||
dataSource: {} as any,
|
||||
storageSource: {} as any,
|
||||
authController: {} as any,
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
import React, { useRef } from "react";
|
||||
import {
|
||||
EntityCollection,
|
||||
EntityCollectionResolver,
|
||||
EntitySchema,
|
||||
EntitySchemaResolver,
|
||||
EntitySchemaResolverProps,
|
||||
NavigationContext,
|
||||
PartialEntityCollection,
|
||||
PartialProperties,
|
||||
SchemaOverrideHandler,
|
||||
SchemaRegistryController
|
||||
} from "../../models";
|
||||
import { removeInitialAndTrailingSlashes } from "../util/navigation_utils";
|
||||
import { computeProperties } from "../utils";
|
||||
import { getValueInPath, mergeDeep } from "../util/objects";
|
||||
|
||||
|
||||
export function useBuildSchemaRegistryController(
|
||||
navigationContext: NavigationContext,
|
||||
schemaOverrideHandler: SchemaOverrideHandler | undefined
|
||||
): SchemaRegistryController {
|
||||
|
||||
const initialised = navigationContext.navigation?.collections !== undefined;
|
||||
|
||||
const schemaConfigRecord = useRef<Record<string, Partial<EntityCollectionResolver> & { overrideSchemaRegistry?: boolean }>>({});
|
||||
|
||||
const getSchemaConfig = <M extends any>(path: string, entityId?: string): EntityCollectionResolver<M> => {
|
||||
|
||||
const sidePanelKey = getSidePanelKey(path, entityId);
|
||||
|
||||
let result: Partial<EntityCollectionResolver> = {};
|
||||
|
||||
const overriddenProps = schemaConfigRecord.current[sidePanelKey];
|
||||
const resolvedProps: Partial<EntityCollectionResolver> | undefined = schemaOverrideHandler && schemaOverrideHandler({
|
||||
entityId,
|
||||
path: removeInitialAndTrailingSlashes(path)
|
||||
});
|
||||
|
||||
if (resolvedProps)
|
||||
result = resolvedProps;
|
||||
|
||||
if (overriddenProps) {
|
||||
// override schema resolver default to true
|
||||
const shouldOverrideRegistry = overriddenProps.overrideSchemaRegistry === undefined || overriddenProps.overrideSchemaRegistry;
|
||||
if (shouldOverrideRegistry)
|
||||
result = {
|
||||
...overriddenProps,
|
||||
permissions: result.permissions || overriddenProps.permissions,
|
||||
schemaResolver: result.schemaResolver || overriddenProps.schemaResolver,
|
||||
subcollections: result.subcollections || overriddenProps.subcollections,
|
||||
callbacks: result.callbacks || overriddenProps.callbacks
|
||||
};
|
||||
else
|
||||
result = {
|
||||
...result,
|
||||
permissions: overriddenProps.permissions ?? result.permissions,
|
||||
schemaResolver: overriddenProps.schemaResolver ?? result.schemaResolver,
|
||||
subcollections: overriddenProps.subcollections ?? result.subcollections,
|
||||
callbacks: overriddenProps.callbacks ?? result.callbacks
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const entityCollection: EntityCollection | undefined = navigationContext.getCollection(path);
|
||||
if (entityCollection) {
|
||||
const schema = entityCollection.schema;
|
||||
const subcollections = entityCollection.subcollections;
|
||||
const callbacks = entityCollection.callbacks;
|
||||
const permissions = entityCollection.permissions;
|
||||
result = {
|
||||
...result,
|
||||
schemaResolver: result.schemaResolver ?? buildSchemaResolver({
|
||||
schema,
|
||||
path
|
||||
}),
|
||||
subcollections: result.subcollections ?? subcollections,
|
||||
callbacks: result.callbacks ?? callbacks,
|
||||
permissions: result.permissions ?? permissions
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.schemaResolver) {
|
||||
if (!result.schema)
|
||||
throw Error(`Not able to resolve schema for ${sidePanelKey}`);
|
||||
result.schemaResolver = buildSchemaResolver({
|
||||
schema: result.schema,
|
||||
path
|
||||
});
|
||||
}
|
||||
|
||||
return result as EntityCollectionResolver<M>;
|
||||
|
||||
};
|
||||
|
||||
const getCollectionResolver = <M extends any>(path: string): EntityCollectionResolver<M> => {
|
||||
const collection = navigationContext.getCollection<M>(path);
|
||||
|
||||
if (!collection) {
|
||||
throw Error(`No collection found for path ${path}`);
|
||||
}
|
||||
|
||||
const schemaConfig = getSchemaConfig<M>(path);
|
||||
if (!schemaConfig) {
|
||||
throw Error(`No schema config found for path ${path}`);
|
||||
}
|
||||
|
||||
return { ...collection, ...schemaConfig };
|
||||
};
|
||||
|
||||
const setOverride = ({
|
||||
path,
|
||||
entityId,
|
||||
schemaConfig,
|
||||
overrideSchemaRegistry
|
||||
}: {
|
||||
path: string,
|
||||
entityId?: string,
|
||||
schemaConfig?: Partial<EntityCollectionResolver>
|
||||
overrideSchemaRegistry?: boolean
|
||||
}
|
||||
) => {
|
||||
|
||||
const key = getSidePanelKey(path, entityId);
|
||||
if (!schemaConfig) {
|
||||
delete schemaConfigRecord.current[key];
|
||||
return undefined;
|
||||
} else {
|
||||
|
||||
schemaConfigRecord.current[key] = {
|
||||
...schemaConfig,
|
||||
overrideSchemaRegistry
|
||||
};
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
const onCollectionModifiedForUser = <M extends any>(path: string, partialCollection: PartialEntityCollection<M>) => {
|
||||
navigationContext.onCollectionModifiedForUser(path, partialCollection);
|
||||
}
|
||||
|
||||
const removeAllOverridesExcept = (entityRefs: {
|
||||
path: string, entityId?: string
|
||||
}[]) => {
|
||||
const keys = entityRefs.map(({
|
||||
path,
|
||||
entityId
|
||||
}) => getSidePanelKey(path, entityId));
|
||||
Object.keys(schemaConfigRecord.current).forEach((currentKey) => {
|
||||
if (!keys.includes(currentKey))
|
||||
delete schemaConfigRecord.current[currentKey];
|
||||
});
|
||||
};
|
||||
|
||||
function buildSchemaResolver<M>({
|
||||
schema,
|
||||
path
|
||||
}: { schema: EntitySchema<M>, path: string }): EntitySchemaResolver {
|
||||
|
||||
return ({
|
||||
entityId,
|
||||
values,
|
||||
}: EntitySchemaResolverProps) => {
|
||||
|
||||
const schemaOverride = navigationContext.getSchemaOverride(path);
|
||||
const storedProperties: PartialProperties<M> | undefined = getValueInPath(schemaOverride, "properties");
|
||||
|
||||
const properties = computeProperties({
|
||||
propertiesOrBuilder: schema.properties,
|
||||
path,
|
||||
entityId,
|
||||
values: values ?? schema.defaultValues
|
||||
});
|
||||
|
||||
return {
|
||||
...schema,
|
||||
properties: mergeDeep(properties, storedProperties)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
initialised,
|
||||
getSchemaConfig,
|
||||
getCollectionResolver,
|
||||
setOverride,
|
||||
removeAllOverridesExcept,
|
||||
onCollectionModifiedForUser
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function getSidePanelKey(path: string, entityId?: string) {
|
||||
if (entityId)
|
||||
return `${removeInitialAndTrailingSlashes(path)}/${removeInitialAndTrailingSlashes(entityId)}`;
|
||||
else
|
||||
return removeInitialAndTrailingSlashes(path);
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
AuthController,
|
||||
DataSource,
|
||||
EntityCollection,
|
||||
EntityCollectionResolver,
|
||||
EntitySchema,
|
||||
EntitySchemaResolver,
|
||||
EntitySchemaResolverProps,
|
||||
Locale,
|
||||
Navigation,
|
||||
NavigationBuilder,
|
||||
NavigationContext,
|
||||
PartialEntityCollection,
|
||||
PartialProperties,
|
||||
PartialSchema,
|
||||
SchemaOverrideHandler,
|
||||
StorageSource,
|
||||
User
|
||||
} from "../../models";
|
||||
@@ -20,13 +26,15 @@ import {
|
||||
getStorageCollectionConfig,
|
||||
saveStorageCollectionConfig
|
||||
} from "../util/storage";
|
||||
import { mergeDeep } from "../util/objects";
|
||||
import { getValueInPath, mergeDeep } from "../util/objects";
|
||||
import { computeProperties } from "../utils";
|
||||
|
||||
export function useBuildNavigationContext<UserType>({
|
||||
basePath,
|
||||
baseCollectionPath,
|
||||
authController,
|
||||
navigationOrBuilder,
|
||||
schemaOverrideHandler,
|
||||
dateTimeFormat,
|
||||
locale,
|
||||
dataSource,
|
||||
@@ -36,6 +44,7 @@ export function useBuildNavigationContext<UserType>({
|
||||
baseCollectionPath: string,
|
||||
authController: AuthController<UserType>;
|
||||
navigationOrBuilder: Navigation | NavigationBuilder<UserType> | EntityCollection[];
|
||||
schemaOverrideHandler: SchemaOverrideHandler | undefined;
|
||||
dateTimeFormat?: string;
|
||||
locale?: Locale;
|
||||
dataSource: DataSource;
|
||||
@@ -46,6 +55,177 @@ export function useBuildNavigationContext<UserType>({
|
||||
const [navigationLoading, setNavigationLoading] = useState<boolean>(false);
|
||||
const [navigationLoadingError, setNavigationLoadingError] = useState<Error | undefined>(undefined);
|
||||
|
||||
const schemaConfigRecord = useRef<Record<string, Partial<EntityCollectionResolver> & { overrideSchemaRegistry?: boolean }>>({});
|
||||
const cleanBasePath = removeInitialAndTrailingSlashes(basePath);
|
||||
const cleanBaseCollectionPath = removeInitialAndTrailingSlashes(baseCollectionPath);
|
||||
|
||||
const homeUrl = cleanBasePath ? `/${cleanBasePath}` : "/";
|
||||
|
||||
const fullCollectionPath = cleanBasePath ? `/${cleanBasePath}/${cleanBaseCollectionPath}` : `/${cleanBaseCollectionPath}`;
|
||||
|
||||
const initialised = navigation?.collections !== undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (!authController.canAccessMainView) {
|
||||
return;
|
||||
}
|
||||
setNavigationLoading(true);
|
||||
getNavigation({
|
||||
navigationOrCollections: navigationOrBuilder,
|
||||
user: authController.user,
|
||||
authController,
|
||||
dateTimeFormat,
|
||||
locale,
|
||||
dataSource,
|
||||
storageSource
|
||||
})
|
||||
.then((result: Navigation) => {
|
||||
setNavigation(result);
|
||||
setNavigationLoading(false);
|
||||
}).catch(setNavigationLoadingError);
|
||||
}, [authController.user, authController.canAccessMainView, navigationOrBuilder]);
|
||||
|
||||
|
||||
const getCollectionResolver = <M extends any>(path: string, entityId?: string): EntityCollectionResolver<M> => {
|
||||
|
||||
const collection = getCollection<M>(path);
|
||||
// if (!collection) {
|
||||
// throw Error(`No collection found for path ${path}`);
|
||||
// }
|
||||
const sidePanelKey = getSidePanelKey(path, entityId);
|
||||
|
||||
let result: Partial<EntityCollectionResolver> = {};
|
||||
|
||||
const overriddenProps = schemaConfigRecord.current[sidePanelKey];
|
||||
const resolvedProps: Partial<EntityCollectionResolver> | undefined = schemaOverrideHandler && schemaOverrideHandler({
|
||||
entityId,
|
||||
path: removeInitialAndTrailingSlashes(path)
|
||||
});
|
||||
|
||||
if (resolvedProps)
|
||||
result = resolvedProps;
|
||||
|
||||
if (overriddenProps) {
|
||||
// override schema resolver default to true
|
||||
const shouldOverrideRegistry = overriddenProps.overrideSchemaRegistry === undefined || overriddenProps.overrideSchemaRegistry;
|
||||
if (shouldOverrideRegistry)
|
||||
result = {
|
||||
...overriddenProps,
|
||||
permissions: result.permissions || overriddenProps.permissions,
|
||||
schemaResolver: result.schemaResolver || overriddenProps.schemaResolver,
|
||||
subcollections: result.subcollections || overriddenProps.subcollections,
|
||||
callbacks: result.callbacks || overriddenProps.callbacks
|
||||
};
|
||||
else
|
||||
result = {
|
||||
...result,
|
||||
permissions: overriddenProps.permissions ?? result.permissions,
|
||||
schemaResolver: overriddenProps.schemaResolver ?? result.schemaResolver,
|
||||
subcollections: overriddenProps.subcollections ?? result.subcollections,
|
||||
callbacks: overriddenProps.callbacks ?? result.callbacks
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const entityCollection: EntityCollection | undefined = getCollection(path);
|
||||
if (entityCollection) {
|
||||
const schema = entityCollection.schema;
|
||||
const subcollections = entityCollection.subcollections;
|
||||
const callbacks = entityCollection.callbacks;
|
||||
const permissions = entityCollection.permissions;
|
||||
result = {
|
||||
...result,
|
||||
schemaResolver: result.schemaResolver ?? buildSchemaResolver({
|
||||
schema,
|
||||
path
|
||||
}),
|
||||
subcollections: result.subcollections ?? subcollections,
|
||||
callbacks: result.callbacks ?? callbacks,
|
||||
permissions: result.permissions ?? permissions
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.schemaResolver) {
|
||||
if (!result.schema)
|
||||
throw Error(`Not able to resolve schema for ${sidePanelKey}`);
|
||||
result.schemaResolver = buildSchemaResolver({
|
||||
schema: result.schema,
|
||||
path
|
||||
});
|
||||
}
|
||||
|
||||
return { ...collection, ...(result as EntityCollectionResolver<M>) };
|
||||
|
||||
};
|
||||
|
||||
const setOverride = ({
|
||||
path,
|
||||
entityId,
|
||||
schemaConfig,
|
||||
overrideSchemaRegistry
|
||||
}: {
|
||||
path: string,
|
||||
entityId?: string,
|
||||
schemaConfig?: Partial<EntityCollectionResolver>
|
||||
overrideSchemaRegistry?: boolean
|
||||
}
|
||||
) => {
|
||||
|
||||
const key = getSidePanelKey(path, entityId);
|
||||
if (!schemaConfig) {
|
||||
delete schemaConfigRecord.current[key];
|
||||
return undefined;
|
||||
} else {
|
||||
|
||||
schemaConfigRecord.current[key] = {
|
||||
...schemaConfig,
|
||||
overrideSchemaRegistry
|
||||
};
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
const removeAllOverridesExcept = (entityRefs: {
|
||||
path: string, entityId?: string
|
||||
}[]) => {
|
||||
const keys = entityRefs.map(({
|
||||
path,
|
||||
entityId
|
||||
}) => getSidePanelKey(path, entityId));
|
||||
Object.keys(schemaConfigRecord.current).forEach((currentKey) => {
|
||||
if (!keys.includes(currentKey))
|
||||
delete schemaConfigRecord.current[currentKey];
|
||||
});
|
||||
};
|
||||
|
||||
function buildSchemaResolver<M>({
|
||||
schema,
|
||||
path
|
||||
}: { schema: EntitySchema<M>, path: string }): EntitySchemaResolver {
|
||||
|
||||
return ({
|
||||
entityId,
|
||||
values,
|
||||
}: EntitySchemaResolverProps) => {
|
||||
|
||||
const collectionOverride = getCollectionOverride<M>(path);
|
||||
const schemaOverride = collectionOverride?.schema;
|
||||
const storedProperties: PartialProperties<M> | undefined = getValueInPath(schemaOverride, "properties");
|
||||
|
||||
const properties = computeProperties({
|
||||
propertiesOrBuilder: schema.properties,
|
||||
path,
|
||||
entityId,
|
||||
values: values ?? schema.defaultValues
|
||||
});
|
||||
|
||||
return {
|
||||
...schema,
|
||||
properties: mergeDeep(properties, storedProperties)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
async function getNavigation<UserType>({ navigationOrCollections, user, authController, dateTimeFormat, locale, dataSource, storageSource }:
|
||||
{
|
||||
navigationOrCollections: Navigation | NavigationBuilder<UserType> | EntityCollection[],
|
||||
@@ -69,34 +249,6 @@ export function useBuildNavigationContext<UserType>({
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!authController.canAccessMainView) {
|
||||
return;
|
||||
}
|
||||
setNavigationLoading(true);
|
||||
getNavigation({
|
||||
navigationOrCollections: navigationOrBuilder,
|
||||
user: authController .user,
|
||||
authController,
|
||||
dateTimeFormat,
|
||||
locale,
|
||||
dataSource,
|
||||
storageSource
|
||||
})
|
||||
.then((result: Navigation) => {
|
||||
setNavigation(result);
|
||||
setNavigationLoading(false);
|
||||
}).catch(setNavigationLoadingError);
|
||||
}, [authController.user, authController.canAccessMainView, navigationOrBuilder]);
|
||||
|
||||
|
||||
const cleanBasePath = removeInitialAndTrailingSlashes(basePath);
|
||||
const cleanBaseCollectionPath = removeInitialAndTrailingSlashes(baseCollectionPath);
|
||||
|
||||
const homeUrl = cleanBasePath ? `/${cleanBasePath}` : "/";
|
||||
|
||||
const fullCollectionPath = cleanBasePath ? `/${cleanBasePath}/${cleanBaseCollectionPath}` : `/${cleanBaseCollectionPath}`;
|
||||
|
||||
function isUrlCollectionPath(path: string): boolean {
|
||||
return removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/");
|
||||
}
|
||||
@@ -132,31 +284,39 @@ export function useBuildNavigationContext<UserType>({
|
||||
|
||||
const collection = getCollectionFromCollections<M>(removeInitialAndTrailingSlashes(path), collections);
|
||||
|
||||
const dynamicCollectionConfig = { ...getStorageCollectionConfig(path) };
|
||||
const dynamicCollectionConfig = { ...getCollectionOverride(path) };
|
||||
delete dynamicCollectionConfig["schema"];
|
||||
|
||||
return collection ? mergeDeep(collection, dynamicCollectionConfig) : undefined;
|
||||
}
|
||||
|
||||
const getSchemaOverride = <M extends any>(path: string): PartialSchema<M> | undefined => {
|
||||
let storageCollectionConfig = getStorageCollectionConfig<M>(path);
|
||||
if (!storageCollectionConfig) return undefined;
|
||||
return storageCollectionConfig.schema;
|
||||
const getCollectionOverride = <M extends any>(path: string): PartialEntityCollection<M> | undefined => {
|
||||
return getStorageCollectionConfig<M>(path);
|
||||
}
|
||||
|
||||
return {
|
||||
navigation,
|
||||
loading: navigationLoading,
|
||||
navigationLoadingError,
|
||||
isUrlCollectionPath,
|
||||
urlPathToDataPath,
|
||||
buildUrlCollectionPath,
|
||||
buildCMSUrlPath,
|
||||
homeUrl,
|
||||
basePath,
|
||||
baseCollectionPath,
|
||||
onCollectionModifiedForUser,
|
||||
getCollection,
|
||||
getSchemaOverride
|
||||
initialised,
|
||||
getCollectionResolver,
|
||||
setOverride,
|
||||
removeAllOverridesExcept,
|
||||
isUrlCollectionPath,
|
||||
urlPathToDataPath,
|
||||
buildUrlCollectionPath,
|
||||
buildCMSUrlPath,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function getSidePanelKey(path: string, entityId?: string) {
|
||||
if (entityId)
|
||||
return `${removeInitialAndTrailingSlashes(path)}/${removeInitialAndTrailingSlashes(entityId)}`;
|
||||
else
|
||||
return removeInitialAndTrailingSlashes(path);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
EntityCollection,
|
||||
NavigationContext,
|
||||
SchemaRegistryController,
|
||||
SideEntityController,
|
||||
SideEntityPanelProps
|
||||
} from "../../models";
|
||||
@@ -15,7 +14,7 @@ import { removeInitialAndTrailingSlashes } from "../util/navigation_utils";
|
||||
|
||||
const NEW_URL_HASH = "new";
|
||||
|
||||
export const useBuildSideEntityController = (navigationContext: NavigationContext, schemaRegistry: SchemaRegistryController): SideEntityController => {
|
||||
export const useBuildSideEntityController = (navigationContext: NavigationContext): SideEntityController => {
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -28,11 +27,11 @@ export const useBuildSideEntityController = (navigationContext: NavigationContex
|
||||
|
||||
const updatePanels = useCallback((newPanels: SideEntityPanelProps[]) => {
|
||||
setSidePanels(newPanels);
|
||||
schemaRegistry.removeAllOverridesExcept(newPanels);
|
||||
navigationContext.removeAllOverridesExcept(newPanels);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (schemaRegistry.initialised) {
|
||||
if (navigationContext.initialised) {
|
||||
if (location?.state && location.state["panels"]) {
|
||||
const statePanel = location.state["panels"] as SideEntityPanelProps[];
|
||||
updatePanels(statePanel);
|
||||
@@ -40,7 +39,7 @@ export const useBuildSideEntityController = (navigationContext: NavigationContex
|
||||
updatePanels([]);
|
||||
}
|
||||
}
|
||||
}, [location?.state, schemaRegistry.initialised]);
|
||||
}, [location?.state, navigationContext.initialised]);
|
||||
|
||||
// only on initialisation
|
||||
useEffect(() => {
|
||||
@@ -95,7 +94,7 @@ export const useBuildSideEntityController = (navigationContext: NavigationContex
|
||||
const schemaOrResolver = schemaProps.schema;
|
||||
const subcollections = schemaProps.subcollections;
|
||||
const overrideSchemaRegistry = schemaProps.overrideSchemaRegistry;
|
||||
schemaRegistry.setOverride(
|
||||
navigationContext.setOverride(
|
||||
{
|
||||
path,
|
||||
entityId,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { EntityCollection, PartialEntityCollection } from "../../models";
|
||||
import { mergeDeep } from "../util/objects";
|
||||
import { useSchemaRegistryController } from "../../hooks/useSchemaRegistryController";
|
||||
import { useNavigation } from "../../hooks";
|
||||
|
||||
export function useCollectionPersist<M>({
|
||||
path,
|
||||
}: { path: string }) {
|
||||
|
||||
const schemaRegistry = useSchemaRegistryController();
|
||||
const collectionResolver = schemaRegistry.getCollectionResolver(path);
|
||||
const navigationContext = useNavigation();
|
||||
const collectionResolver = navigationContext.getCollectionResolver(path);
|
||||
if (!collectionResolver) {
|
||||
throw Error(`Couldn't find the corresponding collection view for the path: ${path}`);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export function useCollectionPersist<M>({
|
||||
|
||||
const onCollectionModifiedForUser = (partialCollection: PartialEntityCollection<M>) => {
|
||||
const newCollection: PartialEntityCollection<M> = mergeDeep(modifiedCollection, partialCollection);
|
||||
schemaRegistry.onCollectionModifiedForUser(path, newCollection);
|
||||
navigationContext.onCollectionModifiedForUser(path, newCollection);
|
||||
setModifiedCollection(newCollection);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@ import { ReferencePreview } from "../../preview";
|
||||
import { ArrayContainer, FieldDescription, LabelWithIcon } from "../components";
|
||||
import { ErrorView, ReferenceDialog } from "../../core";
|
||||
import { formStyles } from "../styles";
|
||||
import { useClearRestoreValue } from "../../hooks";
|
||||
import { useClearRestoreValue, useNavigation } from "../../hooks";
|
||||
import { getReferenceFrom } from "../../core/utils";
|
||||
import { useSchemaRegistryController } from "../../hooks/useSchemaRegistryController";
|
||||
|
||||
|
||||
type ArrayOfReferencesFieldProps = FieldProps<EntityReference[]>;
|
||||
@@ -54,9 +53,9 @@ export function ArrayOfReferencesField({
|
||||
setValue
|
||||
});
|
||||
|
||||
const schemaRegistry = useSchemaRegistryController();
|
||||
const navigationContext = useNavigation();
|
||||
const collectionResolver: EntityCollectionResolver | undefined = useMemo(() => {
|
||||
return schemaRegistry.getCollectionResolver(ofProperty.path);
|
||||
return navigationContext.getCollectionResolver(ofProperty.path);
|
||||
}, [ofProperty.path]);
|
||||
|
||||
if (!collectionResolver) {
|
||||
|
||||
@@ -33,11 +33,10 @@ import { PreviewComponent, SkeletonComponent } from "../../preview";
|
||||
import { LabelWithIcon } from "../components";
|
||||
import {
|
||||
useClearRestoreValue,
|
||||
useEntityFetch,
|
||||
useEntityFetch, useNavigation,
|
||||
useSideEntityController
|
||||
} from "../../hooks";
|
||||
import { getReferenceFrom } from "../../core/utils";
|
||||
import { useSchemaRegistryController } from "../../hooks/useSchemaRegistryController";
|
||||
|
||||
export const useStyles = makeStyles((theme: Theme) => createStyles({
|
||||
root: {
|
||||
@@ -127,9 +126,9 @@ export function ReferenceField<M extends { [Key: string]: any }>({
|
||||
const [open, setOpen] = React.useState(autoFocus);
|
||||
const sideEntityController = useSideEntityController();
|
||||
|
||||
const schemaRegistry = useSchemaRegistryController();
|
||||
const navigationContext = useNavigation();
|
||||
const collectionResolver: EntityCollectionResolver | undefined = useMemo(() => {
|
||||
return schemaRegistry.getCollectionResolver(property.path);
|
||||
return navigationContext.getCollectionResolver(property.path);
|
||||
}, [property.path]);
|
||||
|
||||
if (!collectionResolver) {
|
||||
|
||||
@@ -68,16 +68,13 @@ export function resolveNavigationFrom<M, UserType>({
|
||||
|
||||
|
||||
const dataSource = context.dataSource;
|
||||
const navigationContext = context.navigationContext;
|
||||
const navigation = context.navigationContext.navigation;
|
||||
const schemaRegistry = context.schemaRegistryController;
|
||||
|
||||
if (!navigation) {
|
||||
throw Error("Calling getNavigationFrom, but main navigation has not yet been initialised");
|
||||
}
|
||||
|
||||
if (!schemaRegistry) {
|
||||
throw Error("Calling getNavigationFrom, but main schemaRegistryController has not yet been initialised");
|
||||
}
|
||||
|
||||
const navigationEntries = getNavigationEntriesFromPathInternal({
|
||||
path,
|
||||
@@ -88,7 +85,7 @@ export function resolveNavigationFrom<M, UserType>({
|
||||
if (entry.type === "collection") {
|
||||
return Promise.resolve(entry);
|
||||
} else if (entry.type === "entity") {
|
||||
const entityCollectionResolver = schemaRegistry.getSchemaConfig(entry.path, entry.entityId);
|
||||
const entityCollectionResolver = navigationContext.getCollectionResolver(entry.path, entry.entityId);
|
||||
if (!entityCollectionResolver?.schemaResolver) {
|
||||
throw Error(`No schema defined in the navigation for the entity with path ${entry.path}`);
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { AuthController, SchemaRegistryController } from "../models";
|
||||
import { useFireCMSContext } from "./useFireCMSContext";
|
||||
|
||||
/**
|
||||
* Hook to retrieve the Schema registry controller.
|
||||
*
|
||||
* Consider that in order to use this hook you need to have a parent
|
||||
* `FireCMS`
|
||||
*
|
||||
* @see AuthController
|
||||
* @category Hooks and utilities
|
||||
*/
|
||||
export function useSchemaRegistryController(): SchemaRegistryController {
|
||||
const context = useFireCMSContext();
|
||||
return context.schemaRegistryController;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Locale } from "./locales";
|
||||
import { DataSource } from "./datasource";
|
||||
import { StorageSource } from "./storage";
|
||||
import { SchemaRegistryController } from "./schema_registry_controller";
|
||||
import { NavigationContext } from "./navigation";
|
||||
import { SideEntityController } from "./side_entity_controller";
|
||||
import { AuthController } from "./auth";
|
||||
@@ -39,14 +38,6 @@ export interface FireCMSContext<UserType extends User = User> {
|
||||
*/
|
||||
storageSource: StorageSource;
|
||||
|
||||
/**
|
||||
* This controller is in charge of resolving the entity schemas from a given
|
||||
* path. It takes into account the `navigation` prop set in the main level of the
|
||||
* CMSApp as well as the `schemaResolver` in case you want to override schemas
|
||||
* to specific entities.
|
||||
*/
|
||||
schemaRegistryController: SchemaRegistryController;
|
||||
|
||||
/**
|
||||
* Context that includes the resolved navigation and utility methods and
|
||||
* attributes.
|
||||
|
||||
@@ -12,7 +12,6 @@ export * from "./fields";
|
||||
export * from "./datasource";
|
||||
export * from "./entity_link_builder";
|
||||
export * from "./side_entity_controller";
|
||||
export * from "./schema_registry_controller";
|
||||
export * from "./firecms_context";
|
||||
export * from "./entity_callbacks";
|
||||
export * from "./overrides";
|
||||
|
||||
@@ -87,14 +87,40 @@ export type NavigationContext = {
|
||||
|
||||
navigationLoadingError?: any;
|
||||
|
||||
getCollection: <M>(path: string) => EntityCollection<M> | undefined;
|
||||
|
||||
getSchemaOverride: <M>(path: string) => PartialSchema<M> | undefined;
|
||||
/**
|
||||
* Is the registry ready to be used
|
||||
*/
|
||||
initialised: boolean;
|
||||
|
||||
/**
|
||||
* Set props for path
|
||||
* @return used key
|
||||
*/
|
||||
setOverride: <M>(props: {
|
||||
path: string,
|
||||
entityId?: string,
|
||||
schemaConfig?: Partial<EntityCollectionResolver>
|
||||
overrideSchemaRegistry?: boolean
|
||||
}
|
||||
) => string | undefined;
|
||||
|
||||
/**
|
||||
* Get the schema configuration for a given path
|
||||
*/
|
||||
getCollectionResolver: <M>(path: string, entityId?: string) => EntityCollectionResolver<M> | undefined;
|
||||
|
||||
/**
|
||||
* Remove all keys not used
|
||||
* @param used keys
|
||||
*/
|
||||
removeAllOverridesExcept: (entityRefs: {
|
||||
path: string, entityId?: string
|
||||
}[]) => void;
|
||||
|
||||
/**
|
||||
* Use this callback when a collection has been modified so it is persisted.
|
||||
*/
|
||||
onCollectionModifiedForUser: <M>(path: string, partialCollection: PartialEntityCollection<M>) => void;
|
||||
onCollectionModifiedForUser: <M>(path:string, partialCollection: PartialEntityCollection<M>) => void;
|
||||
|
||||
/**
|
||||
* Default path under the navigation routes of the CMS will be created
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import { EntityCollectionResolver } from "./collections";
|
||||
import { PartialEntityCollection } from "./overrides";
|
||||
|
||||
/**
|
||||
* This controller is in charge of resolving the entity schemas from a given
|
||||
* path. It takes into account the `navigation` prop set in the main level of the
|
||||
* `FireCMS` app as well as the `schemaResolver` in case you want to override schemas
|
||||
* to specific entities.
|
||||
*
|
||||
* @category Hooks and utilities
|
||||
*/
|
||||
export interface SchemaRegistryController {
|
||||
|
||||
/**
|
||||
* Is the registry ready to be used
|
||||
*/
|
||||
initialised: boolean;
|
||||
|
||||
/**
|
||||
* Set props for path
|
||||
* @return used key
|
||||
*/
|
||||
setOverride: <M>(props: {
|
||||
path: string,
|
||||
entityId?: string,
|
||||
schemaConfig?: Partial<EntityCollectionResolver>
|
||||
overrideSchemaRegistry?: boolean
|
||||
}
|
||||
) => string | undefined;
|
||||
|
||||
/**
|
||||
* Get the schema configuration for a given path
|
||||
*/
|
||||
getSchemaConfig: (path: string, entityId?: string) => EntityCollectionResolver | undefined;
|
||||
|
||||
/**
|
||||
* Get the entity collection for a given path
|
||||
*/
|
||||
getCollectionResolver: <M>(path: string) => EntityCollectionResolver<M> | undefined;
|
||||
|
||||
/**
|
||||
* Remove all keys not used
|
||||
* @param used keys
|
||||
*/
|
||||
removeAllOverridesExcept: (entityRefs: {
|
||||
path: string, entityId?: string
|
||||
}[]) => void;
|
||||
|
||||
/**
|
||||
* Use this callback when a collection has been modified so it is persisted.
|
||||
*/
|
||||
onCollectionModifiedForUser: <M>(path:string, partialCollection: PartialEntityCollection<M>) => void;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import { SkeletonComponent } from "./SkeletonComponent";
|
||||
import { ErrorView } from "../../core";
|
||||
import {
|
||||
useEntityFetch,
|
||||
useFireCMSContext,
|
||||
useFireCMSContext, useNavigation,
|
||||
useSideEntityController
|
||||
} from "../../hooks";
|
||||
|
||||
@@ -105,10 +105,10 @@ function ReferencePreviewComponent<M extends { [Key: string]: any }>(
|
||||
const reference: EntityReference = value;
|
||||
const previewProperties = property.previewProperties;
|
||||
|
||||
const schemaRegistry = useFireCMSContext().schemaRegistryController;
|
||||
const navigationContext = useNavigation();
|
||||
const sideEntityController = useSideEntityController();
|
||||
|
||||
const collectionResolver = schemaRegistry.getCollectionResolver<M>(property.path);
|
||||
const collectionResolver = navigationContext.getCollectionResolver<M>(property.path);
|
||||
if (!collectionResolver) {
|
||||
throw Error(`Couldn't find the corresponding collection view for the path: ${property.path}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user