Prefetching views and favorites (#4421)

* wip

* Push

* Complete work on prefetch

* Add comment

* Fix

* Fix

* Fix

* Fix

* Remove dead code

* Simplify

* Fix tests

* Fix tests

* Fix according to review

* Fix according to review

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Charles Bochet
2024-03-15 18:35:40 +01:00
committed by GitHub
parent 38f28de4a6
commit afb9b3e375
21 changed files with 279 additions and 129 deletions

View File

@ -0,0 +1,12 @@
import React from 'react';
import { PrefetchRunQueriesEffect } from '@/prefetch/components/PrefetchRunQueriesEffect';
export const PrefetchDataProvider = ({ children }: React.PropsWithChildren) => {
return (
<>
<PrefetchRunQueriesEffect />
{children}
</>
);
};

View File

@ -0,0 +1,53 @@
import { useEffect } from 'react';
import { useQuery } from '@apollo/client';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useGenerateFindManyRecordsForMultipleMetadataItemsQuery } from '@/object-record/hooks/useGenerateFindManyRecordsForMultipleMetadataItemsQuery';
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { isDefined } from '~/utils/isDefined';
export const PrefetchRunQueriesEffect = () => {
const {
objectMetadataItem: objectMetadataItemView,
upsertRecordsInCache: upsertViewsInCache,
} = usePrefetchRunQuery({
prefetchKey: PrefetchKey.AllViews,
objectNameSingular: CoreObjectNameSingular.View,
});
const {
objectMetadataItem: objectMetadataItemFavorite,
upsertRecordsInCache: upsertFavoritesInCache,
} = usePrefetchRunQuery({
prefetchKey: PrefetchKey.AllFavorites,
objectNameSingular: CoreObjectNameSingular.Favorite,
});
const prefetchFindManyQuery =
useGenerateFindManyRecordsForMultipleMetadataItemsQuery({
objectMetadataItems: [objectMetadataItemView, objectMetadataItemFavorite],
depth: 2,
});
if (!isDefined(prefetchFindManyQuery)) {
throw new Error('Could not prefetch recrds');
}
const { data } = useQuery<MultiObjectRecordQueryResult>(
prefetchFindManyQuery,
);
useEffect(() => {
if (isDefined(data?.views)) {
upsertViewsInCache(data.views);
}
if (isDefined(data?.favorites)) {
upsertFavoritesInCache(data.favorites);
}
}, [data, upsertViewsInCache, upsertFavoritesInCache]);
return <></>;
};

View File

@ -0,0 +1,9 @@
import { QueryKey } from '@/object-record/query-keys/types/QueryKey';
import { ALL_FAVORITES_QUERY_KEY } from '@/prefetch/query-keys/AllFavoritesQueryKey';
import { ALL_VIEWS_QUERY_KEY } from '@/prefetch/query-keys/AllViewsQueryKey';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
export const PREFETCH_CONFIG: Record<PrefetchKey, QueryKey> = {
ALL_VIEWS: ALL_VIEWS_QUERY_KEY,
ALL_FAVORITES: ALL_FAVORITES_QUERY_KEY,
};

View File

@ -0,0 +1,55 @@
import { useSetRecoilState } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useUpsertFindManyRecordsQueryInCache } from '@/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache';
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { ALL_VIEWS_QUERY_KEY } from '@/prefetch/query-keys/AllViewsQueryKey';
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
export type UsePrefetchRunQuery = {
prefetchKey: PrefetchKey;
objectNameSingular: CoreObjectNameSingular;
};
export const usePrefetchRunQuery = <T extends ObjectRecord>({
prefetchKey,
objectNameSingular,
}: UsePrefetchRunQuery) => {
const setPrefetchDataIsLoadedLoaded = useSetRecoilState(
prefetchIsLoadedFamilyState(prefetchKey),
);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: objectNameSingular,
});
const { upsertFindManyRecordsQueryInCache } =
useUpsertFindManyRecordsQueryInCache({
objectMetadataItem: objectMetadataItem,
});
const mapConnectionToRecords = useMapConnectionToRecords();
const upsertRecordsInCache = (records: ObjectRecordConnection<T>) => {
upsertFindManyRecordsQueryInCache({
queryVariables: ALL_VIEWS_QUERY_KEY.variables,
depth: ALL_VIEWS_QUERY_KEY.depth,
objectRecordsToOverwrite:
mapConnectionToRecords({
objectRecordConnection: records,
objectNameSingular: CoreObjectNameSingular.View,
depth: 2,
}) ?? [],
});
setPrefetchDataIsLoadedLoaded(true);
};
return {
objectMetadataItem,
setPrefetchDataIsLoadedLoaded,
upsertRecordsInCache,
};
};

View File

@ -0,0 +1,27 @@
import { useRecoilValue } from 'recoil';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
export const usePrefetchedData = <T extends ObjectRecord>(
prefetchKey: PrefetchKey,
) => {
const isDataPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(prefetchKey),
);
const prefetchQueryKey = PREFETCH_CONFIG[prefetchKey];
const { records } = useFindManyRecords<T>({
skip: !isDataPrefetched,
...prefetchQueryKey,
useRecordsWithoutConnection: true,
});
return {
isDataPrefetched,
records,
};
};

View File

@ -0,0 +1,8 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { QueryKey } from '@/object-record/query-keys/types/QueryKey';
export const ALL_FAVORITES_QUERY_KEY: QueryKey = {
objectNameSingular: CoreObjectNameSingular.Favorite,
variables: {},
depth: 1,
};

View File

@ -0,0 +1,8 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { QueryKey } from '@/object-record/query-keys/types/QueryKey';
export const ALL_VIEWS_QUERY_KEY: QueryKey = {
objectNameSingular: CoreObjectNameSingular.View,
variables: {},
depth: 1,
};

View File

@ -0,0 +1,10 @@
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState';
export const prefetchIsLoadedFamilyState = createFamilyState<
boolean,
PrefetchKey
>({
key: 'prefetchIsLoadedFamilyState',
defaultValue: false,
});

View File

@ -0,0 +1,4 @@
export enum PrefetchKey {
AllViews = 'ALL_VIEWS',
AllFavorites = 'ALL_FAVORITES',
}