Activity injection into Apollo cache (#3665)
- Created addRecordInCache to inject a record in Apollo cache and inject single read query on this record - Created createOneRecordInCache and createManyRecordsInCache that uses this addRecordInCache - Created useOpenCreateActivityDrawerV2 hook to create an activity in cache and inject it into all other relevant requests in the app before opening activity drawer - Refactored DEFAULT_SEARCH_REQUEST_LIMIT constant and hardcoded arbitrary request limits - Added Apollo dev logs to see errors in the console when manipulating cache
This commit is contained in:
@ -5,7 +5,7 @@ export const query = gql`
|
||||
$filter: PersonFilterInput
|
||||
$orderBy: PersonOrderByInput
|
||||
$lastCursor: String
|
||||
$limit: Float = 30
|
||||
$limit: Float
|
||||
) {
|
||||
people(
|
||||
filter: $filter
|
||||
|
||||
@ -5,7 +5,7 @@ import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { useModifyRecordFromCache } from '@/object-record/hooks/useModifyRecordFromCache';
|
||||
import { useModifyRecordFromCache } from '@/object-record/cache/hooks/useModifyRecordFromCache';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<MockedProvider addTypename={false}>
|
||||
|
||||
@ -3,7 +3,7 @@ import { useApolloClient } from '@apollo/client';
|
||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { useGenerateCachedObjectRecord } from '@/object-record/hooks/useGenerateCachedObjectRecord';
|
||||
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { useAddRecordInCache } from '@/object-record/cache/hooks/useAddRecordInCache';
|
||||
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
||||
export const useCreateManyRecordsInCache = <T extends ObjectRecord>({
|
||||
objectNameSingular,
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { generateCachedObjectRecord } = useGenerateCachedObjectRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const addRecordInCache = useAddRecordInCache({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const createManyRecordsInCache = async (data: Partial<T>[]) => {
|
||||
const recordsWithId = data.map((record) => ({
|
||||
...record,
|
||||
id: (record.id as string) ?? v4(),
|
||||
}));
|
||||
|
||||
const createdRecordsInCache = [] as T[];
|
||||
|
||||
for (const record of recordsWithId) {
|
||||
const generatedCachedObjectRecord = generateCachedObjectRecord<T>({
|
||||
...record,
|
||||
});
|
||||
|
||||
if (generatedCachedObjectRecord) {
|
||||
addRecordInCache(generatedCachedObjectRecord);
|
||||
|
||||
createdRecordsInCache.push(generatedCachedObjectRecord);
|
||||
}
|
||||
}
|
||||
|
||||
return createdRecordsInCache;
|
||||
};
|
||||
|
||||
return { createManyRecordsInCache };
|
||||
};
|
||||
@ -2,7 +2,7 @@ import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useGenerateCachedObjectRecord } from '@/object-record/hooks/useGenerateCachedObjectRecord';
|
||||
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useAddRecordInCache } from '@/object-record/cache/hooks/useAddRecordInCache';
|
||||
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
||||
type useCreateOneRecordInCacheProps = {
|
||||
objectNameSingular: string;
|
||||
};
|
||||
|
||||
export const useCreateOneRecordInCache = <T>({
|
||||
objectNameSingular,
|
||||
}: useCreateOneRecordInCacheProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { generateCachedObjectRecord } = useGenerateCachedObjectRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const addRecordInCache = useAddRecordInCache({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const createOneRecordInCache = async (input: ObjectRecord) => {
|
||||
const generatedCachedObjectRecord = generateCachedObjectRecord({
|
||||
createdAt: new Date().toISOString(),
|
||||
...input,
|
||||
});
|
||||
|
||||
addRecordInCache(generatedCachedObjectRecord);
|
||||
|
||||
return generatedCachedObjectRecord as T;
|
||||
};
|
||||
|
||||
return {
|
||||
createOneRecordInCache,
|
||||
};
|
||||
};
|
||||
@ -13,7 +13,6 @@ import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnec
|
||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
||||
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
|
||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { logError } from '~/utils/logError';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
@ -28,7 +27,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectNameSingular,
|
||||
filter,
|
||||
orderBy,
|
||||
limit = DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||
limit,
|
||||
onCompleted,
|
||||
skip,
|
||||
useRecordsWithoutConnection = false,
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import { v4 } from 'uuid';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { generateEmptyFieldValue } from '@/object-record/utils/generateEmptyFieldValue';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useGenerateCachedObjectRecord = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const generateCachedObjectRecord = <
|
||||
GeneratedObjectRecord extends ObjectRecord,
|
||||
>(
|
||||
input: Record<string, unknown>,
|
||||
) => {
|
||||
const recordSchema = z.object(
|
||||
Object.fromEntries(
|
||||
objectMetadataItem.fields.map((fieldMetadataItem) => [
|
||||
fieldMetadataItem.name,
|
||||
z.unknown().default(generateEmptyFieldValue(fieldMetadataItem)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
__typename: capitalize(objectMetadataItem.nameSingular),
|
||||
...recordSchema.parse({
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
...input,
|
||||
}),
|
||||
} as GeneratedObjectRecord & { __typename: string };
|
||||
};
|
||||
|
||||
return {
|
||||
generateCachedObjectRecord,
|
||||
};
|
||||
};
|
||||
@ -20,7 +20,7 @@ export const useGenerateFindManyRecordsQuery = () => {
|
||||
objectMetadataItem.nameSingular,
|
||||
)}FilterInput, $orderBy: ${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}OrderByInput, $lastCursor: String, $limit: Float = 60) {
|
||||
)}OrderByInput, $lastCursor: String, $limit: Float) {
|
||||
${
|
||||
objectMetadataItem.namePlural
|
||||
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
import { gql, useApolloClient } from '@apollo/client';
|
||||
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useGetRecordFromCache = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
return <CachedObjectRecord extends ObjectRecord = ObjectRecord>(
|
||||
recordId: string,
|
||||
) => {
|
||||
if (!objectMetadataItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
const cacheReadFragment = gql`
|
||||
fragment ${capitalizedObjectName}Fragment on ${capitalizedObjectName} {
|
||||
id
|
||||
${objectMetadataItem.fields
|
||||
.map((field) => mapFieldMetadataToGraphQLQuery(field))
|
||||
.join('\n')}
|
||||
}
|
||||
`;
|
||||
|
||||
const cache = apolloClient.cache;
|
||||
const cachedRecordId = cache.identify({
|
||||
__typename: capitalize(objectMetadataItem.nameSingular),
|
||||
id: recordId,
|
||||
});
|
||||
|
||||
return cache.readFragment<CachedObjectRecord & { __typename: string }>({
|
||||
id: cachedRecordId,
|
||||
fragment: cacheReadFragment,
|
||||
});
|
||||
};
|
||||
};
|
||||
@ -1,31 +0,0 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { Modifiers } from '@apollo/client/cache';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useModifyRecordFromCache = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { cache } = useApolloClient();
|
||||
|
||||
return <CachedObjectRecord extends ObjectRecord = ObjectRecord>(
|
||||
recordId: string,
|
||||
fieldModifiers: Modifiers<CachedObjectRecord>,
|
||||
) => {
|
||||
if (!objectMetadataItem) return;
|
||||
|
||||
const cachedRecordId = cache.identify({
|
||||
__typename: capitalize(objectMetadataItem.nameSingular),
|
||||
id: recordId,
|
||||
});
|
||||
|
||||
cache.modify<CachedObjectRecord>({
|
||||
id: cachedRecordId,
|
||||
fields: fieldModifiers,
|
||||
});
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user