UNPKG

@firecms/core

Version:

Awesome Firebase/Firestore-based headless open-source CMS

358 lines (331 loc) 12.9 kB
import { useCallback } from "react"; import { AuthController, DataSource, DataSourceDelegate, DeleteEntityProps, Entity, EntityCollection, EntityValues, FetchCollectionProps, FetchEntityProps, FilterValues, FireCMSContext, ListenCollectionProps, ListenEntityProps, NavigationController, PropertyConfig, ResolvedProperties, SaveEntityProps } from "../types"; import { resolveCollection, updateDateAutoValues } from "../util"; /** * Use this hook to build a {@link DataSource} based on Firestore * @param firebaseApp * @group Firebase */ export function useBuildDataSource({ delegate, propertyConfigs, navigationController, authController }: { delegate: DataSourceDelegate, propertyConfigs?: Record<string, PropertyConfig>; navigationController: NavigationController; authController: AuthController; }): DataSource { return { /** * Fetch entities in a Firestore path * @param path * @param collection * @param filter * @param limit * @param startAfter * @param searchString * @param orderBy * @param order * @return Function to cancel subscription * @see useCollectionFetch if you need this functionality implemented as a hook * @group Firestore */ fetchCollection: useCallback(<M extends Record<string, any>>({ path, collection, filter, limit, startAfter, searchString, orderBy, order, }: FetchCollectionProps<M> ): Promise<Entity<M>[]> => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; return usedDelegate.fetchCollection<M>({ path, filter, limit, startAfter, searchString, orderBy, order, collection }); }, [delegate]), /** * Listen to a entities in a given path * @param path * @param collection * @param onError * @param filter * @param limit * @param startAfter * @param searchString * @param orderBy * @param order * @param onUpdate * @return Function to cancel subscription * @see useCollectionFetch if you need this functionality implemented as a hook * @group Firestore */ listenCollection: delegate.listenCollection ? useCallback(<M extends Record<string, any>>( { path, collection: collectionProp, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError }: ListenCollectionProps<M> ): () => void => { const collection = collectionProp ?? navigationController.getCollection(path); const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; if (!usedDelegate.listenCollection) throw Error("useBuildDataSource delegate not initialised"); return usedDelegate.listenCollection<M>({ path, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError, collection, }); }, [delegate, navigationController.getCollection]) : undefined, /** * Retrieve an entity given a path and a collection * @param path * @param entityId * @param collection * @group Firestore */ fetchEntity: useCallback(<M extends Record<string, any>>({ path, entityId, collection }: FetchEntityProps<M> ): Promise<Entity<M> | undefined> => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; return usedDelegate.fetchEntity({ path, entityId, collection }); }, [delegate.fetchEntity]), /** * * @param path * @param entityId * @param collection * @param onUpdate * @param onError * @return Function to cancel subscription * @group Firestore */ listenEntity: delegate.listenEntity ? useCallback(<M extends Record<string, any>>( { path, entityId, collection, onUpdate, onError }: ListenEntityProps<M>): () => void => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; if (!usedDelegate.listenEntity) throw Error("useBuildDataSource delegate not initialised"); return usedDelegate.listenEntity<M>({ path, entityId, onUpdate, onError, collection }) }, [delegate.listenEntity]) : undefined, /** * Save entity to the specified path. Note that Firestore does not allow * undefined values. * @param path * @param entityId * @param values * @param schemaId * @param collection * @param status * @group Firestore */ saveEntity: useCallback(<M extends Record<string, any>>( { path, entityId, values, collection: collectionProp, status }: SaveEntityProps<M>): Promise<Entity<M>> => { const collection = collectionProp ?? navigationController.getCollection(path); const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; const resolvedCollection = collection ? resolveCollection<M>({ collection, path, entityId, propertyConfigs: propertyConfigs, authController }) : undefined; const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties; const delegateValues = usedDelegate.cmsToDelegateModel( values, ); const updatedValues: EntityValues<M> = properties ? updateDateAutoValues( { inputValues: delegateValues, properties, status, timestampNowValue: usedDelegate.currentTime?.() ?? new Date(), setDateToMidnight: usedDelegate.setDateToMidnight }) : delegateValues; return usedDelegate.saveEntity({ path, collection, entityId, values: updatedValues, status }).then((res) => { return { id: res.id, path: res.path, values: usedDelegate.delegateToCMSModel(updatedValues) } as Entity<M>; }); }, [delegate.saveEntity, navigationController.getCollection]), /** * Delete an entity * @param entity * @param collection * @group Firestore */ deleteEntity: useCallback(<M extends Record<string, any>>( { entity, collection }: DeleteEntityProps<M> ): Promise<void> => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; return usedDelegate.deleteEntity({ entity, collection }); }, [delegate.deleteEntity]), /** * Check if the given property is unique in the given collection * @param path Collection path * @param name of the property * @param value * @param property * @param entityId * @return `true` if there are no other fields besides the given entity * @group Firestore */ checkUniqueField: useCallback(( path: string, name: string, value: any, entityId?: string, collection?: EntityCollection ): Promise<boolean> => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; return usedDelegate.checkUniqueField(path, name, value, entityId, collection); }, [delegate.checkUniqueField]), generateEntityId: useCallback((path: string, collection: EntityCollection): string => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; return usedDelegate.generateEntityId(path, collection); }, [delegate.generateEntityId]), countEntities: delegate.countEntities ? async ({ path, collection, filter, order, orderBy }: { path: string, collection: EntityCollection<any>, filter?: FilterValues<Extract<keyof any, string>>, orderBy?: string, order?: "desc" | "asc", }): Promise<number> => { const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate; return usedDelegate.countEntities!({ path, filter, orderBy, order, collection }); } : undefined, isFilterCombinationValid: useCallback(({ path, databaseId, filterValues, sortBy }: { path: string, databaseId?: string, filterValues: FilterValues<any>, sortBy?: [string, "asc" | "desc"] }): boolean => { if (!delegate.isFilterCombinationValid) return true; return delegate.isFilterCombinationValid( { path, databaseId, filterValues, sortBy } ) }, [delegate.isFilterCombinationValid]), initTextSearch: useCallback(async (props: { context: FireCMSContext, path: string, collection: EntityCollection, parentCollectionIds?: string[] }): Promise<boolean> => { const usedDelegate = props.collection?.overrides?.dataSourceDelegate ?? delegate; if (!usedDelegate.initTextSearch) return false; return usedDelegate.initTextSearch(props) }, [delegate.initTextSearch]), }; }