import React, { type ReactNode, createContext, useContext, useMemo } from 'react' import { Center } from '@keystone-ui/core' import { ToastProvider } from '@keystone-ui/toast' import { LoadingDots } from '@keystone-ui/loading' import { DrawerProvider } from '@keystone-ui/modals' import { createUploadLink } from 'apollo-upload-client' import { type AdminConfig, type AdminMeta, type FieldViews } from '../types' import { useAdminMeta } from './utils/useAdminMeta' import { type ApolloError, type DocumentNode, ApolloProvider, ApolloClient, InMemoryCache, } from './apollo' import { type AuthenticatedItem, type CreateViewFieldModes, type VisibleLists, useLazyMetadata, } from './utils/useLazyMetadata' type KeystoneContextType = { adminConfig: AdminConfig adminMeta: | { state: 'loaded', value: AdminMeta } | { state: 'error', error: ApolloError, refetch: () => Promise } fieldViews: FieldViews authenticatedItem: AuthenticatedItem visibleLists: VisibleLists createViewFieldModes: CreateViewFieldModes reinitContext: () => Promise apiPath: string } const KeystoneContext = createContext(undefined) type KeystoneProviderProps = { children: ReactNode adminConfig: AdminConfig adminMetaHash: string fieldViews: FieldViews lazyMetadataQuery: DocumentNode apiPath: string } function InternalKeystoneProvider ({ adminConfig, fieldViews, adminMetaHash, children, lazyMetadataQuery, apiPath, }: KeystoneProviderProps) { const adminMeta = useAdminMeta(adminMetaHash, fieldViews) const { authenticatedItem, visibleLists, createViewFieldModes, refetch } = useLazyMetadata(lazyMetadataQuery) const reinitContext = async () => { await adminMeta?.refetch?.() await refetch() } if (adminMeta.state === 'loading') { return (
) } return ( {children} ) } export function KeystoneProvider (props: KeystoneProviderProps) { const apolloClient = useMemo( () => new ApolloClient({ cache: new InMemoryCache(), link: createUploadLink({ uri: props.apiPath, headers: { 'Apollo-Require-Preflight': 'true' }, }), }), [props.apiPath] ) return ( ) } export function useKeystone (): { adminConfig: AdminConfig adminMeta: AdminMeta authenticatedItem: AuthenticatedItem visibleLists: VisibleLists createViewFieldModes: CreateViewFieldModes apiPath: string } { const value = useContext(KeystoneContext) if (!value) throw new Error('useKeystone must be called inside a KeystoneProvider component') if (value.adminMeta.state === 'error') throw new Error('An error occurred when loading Admin Metadata') return { adminConfig: value.adminConfig, adminMeta: value.adminMeta.value, authenticatedItem: value.authenticatedItem, visibleLists: value.visibleLists, createViewFieldModes: value.createViewFieldModes, apiPath: value.apiPath, } } export function useReinitContext () { const value = useContext(KeystoneContext) if (value) return value.reinitContext throw new Error('useReinitContext must be called inside a KeystoneProvider component') } export function useRawKeystone () { const value = useContext(KeystoneContext) if (value) return value throw new Error('useRawKeystone must be called inside a KeystoneProvider component') } export function useList (listKey: string) { const { adminMeta: { lists }, } = useKeystone() const list = lists[listKey] if (!list) throw new Error(`Unknown field ${listKey}`) return list } export function useField (listKey: string, fieldKey: string) { const list = useList(listKey) const field = list.fields[fieldKey] if (!field) throw new Error(`Unknown field ${listKey}.${fieldKey}`) return field }