import type { GraphQLError } from 'graphql' import { useMemo } from 'react' import type { AuthenticatedItem, VisibleLists, CreateViewFieldModes } from '../../types' import { type DocumentNode, useQuery, type QueryResult, type ServerError, type ServerParseError } from '../apollo' import { type DeepNullable, makeDataGetter } from './dataGetter' export type { AuthenticatedItem, VisibleLists, CreateViewFieldModes } export function useLazyMetadata (query: DocumentNode): { authenticatedItem: AuthenticatedItem refetch: () => Promise visibleLists: VisibleLists createViewFieldModes: CreateViewFieldModes } { const result = useQuery(query, { errorPolicy: 'all', fetchPolicy: 'no-cache' }) return useMemo(() => { const refetch = async () => { await result.refetch() } const dataGetter = makeDataGetter< DeepNullable<{ authenticatedItem: | { __typename: string id: string [key: string]: any } | { __typename: string } keystone: { adminMeta: { lists: { key: string isHidden: boolean fields: { path: string, createView: { fieldMode: 'edit' | 'hidden' } }[] }[] } } }> >(result.data, result.error?.graphQLErrors) const authenticatedItemGetter = dataGetter.get('authenticatedItem') const keystoneMetaGetter = dataGetter.get('keystone') return { refetch, authenticatedItem: getAuthenticatedItem( result, authenticatedItemGetter.errors || (result.error?.networkError ?? undefined) ), visibleLists: getVisibleLists( result, keystoneMetaGetter.errors || (result.error?.networkError ?? undefined) ), createViewFieldModes: getCreateViewFieldModes( result, keystoneMetaGetter.errors || (result.error?.networkError ?? undefined) ), } }, [result]) } function getCreateViewFieldModes ( { data }: QueryResult, error?: Error | ServerParseError | ServerError | readonly [GraphQLError, ...GraphQLError[]] ): CreateViewFieldModes { if (error) { return { state: 'error', error } } if (data) { const lists: Record> = {} data.keystone.adminMeta.lists.forEach((list: any) => { lists[list.key] = {} list.fields.forEach((field: any) => { lists[list.key][field.path] = field.createView.fieldMode }) }) return { state: 'loaded', lists } } return { state: 'loading' } } function getVisibleLists ( { data }: QueryResult, error?: Error | ServerParseError | ServerError | readonly [GraphQLError, ...GraphQLError[]] ): VisibleLists { if (error) { return { state: 'error', error } } if (data) { const lists = new Set() data.keystone.adminMeta.lists.forEach((list: any) => { if (!list.isHidden) { lists.add(list.key) } }) return { state: 'loaded', lists } } return { state: 'loading' } } function getAuthenticatedItem ( { data }: QueryResult, error?: Error | ServerParseError | ServerError | readonly [GraphQLError, ...GraphQLError[]] ): AuthenticatedItem { if (error) { return { state: 'error', error } } if (data) { if ( !data.authenticatedItem || // this is for the case where there is a new type // in the AuthenticatedItem union and the query // that the admin ui has doesn't get the id // (yes, undefined is very specific and very intentional, it should not be checking for null) data.authenticatedItem.id === undefined ) { return { state: 'unauthenticated' } } const labelField = Object.keys(data.authenticatedItem).filter( x => x !== '__typename' && x !== 'id' )[0] return { state: 'authenticated', id: data.authenticatedItem.id, label: data.authenticatedItem[labelField] || data.authenticatedItem.id, listKey: data.authenticatedItem.__typename, } } return { state: 'loading' } }