Feat/record optimistic effect (#3076)
* WIP * WIP * POC working on hard coded completedAt field * Finished isRecordMatchingFilter, mock of pg_graphql filtering mechanism * Fixed and cleaned * Unregister unused optimistic effects * Fix lint * Fixes from review --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -7,7 +7,7 @@ import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMeta
|
||||
import { useGenerateEmptyRecord } from '@/object-record/hooks/useGenerateEmptyRecord';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useCreateManyRecords = <T>({
|
||||
export const useCreateManyRecords = <T extends Record<string, unknown>>({
|
||||
objectNameSingular,
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect({
|
||||
@ -32,10 +32,17 @@ export const useCreateManyRecords = <T>({
|
||||
}));
|
||||
|
||||
withIds.forEach((record) => {
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
generateEmptyRecord({ id: record.id }),
|
||||
);
|
||||
const emptyRecord: Record<string, unknown> | undefined =
|
||||
generateEmptyRecord({
|
||||
id: record.id,
|
||||
});
|
||||
|
||||
if (emptyRecord) {
|
||||
triggerOptimisticEffects({
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
createdRecords: [emptyRecord],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const createdObjects = await apolloClient.mutate({
|
||||
@ -59,11 +66,9 @@ export const useCreateManyRecords = <T>({
|
||||
`create${capitalize(objectMetadataItem.namePlural)}`
|
||||
] as T[]) ?? [];
|
||||
|
||||
createdRecords.forEach((record) => {
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
record,
|
||||
);
|
||||
triggerOptimisticEffects({
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
createdRecords,
|
||||
});
|
||||
|
||||
return createdRecords;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
@ -14,16 +13,16 @@ type useCreateOneRecordProps = {
|
||||
|
||||
export const useCreateOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
refetchFindManyQuery = false,
|
||||
}: useCreateOneRecordProps) => {
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { objectMetadataItem, createOneRecordMutation, findManyRecordsQuery } =
|
||||
useObjectMetadataItem({
|
||||
const { objectMetadataItem, createOneRecordMutation } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const apolloClient = useApolloClient();
|
||||
@ -35,16 +34,16 @@ export const useCreateOneRecord = <T>({
|
||||
const createOneRecord = async (input: Record<string, any>) => {
|
||||
const recordId = v4();
|
||||
|
||||
const generatedEmptyRecord = generateEmptyRecord({
|
||||
const generatedEmptyRecord = generateEmptyRecord<Record<string, unknown>>({
|
||||
id: recordId,
|
||||
...input,
|
||||
});
|
||||
|
||||
if (generatedEmptyRecord) {
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
generatedEmptyRecord,
|
||||
);
|
||||
triggerOptimisticEffects({
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
createdRecords: [generatedEmptyRecord],
|
||||
});
|
||||
}
|
||||
|
||||
const createdObject = await apolloClient.mutate({
|
||||
@ -56,22 +55,12 @@ export const useCreateOneRecord = <T>({
|
||||
[`create${capitalize(objectMetadataItem.nameSingular)}`]:
|
||||
generateEmptyRecord({ id: recordId, ...input }),
|
||||
},
|
||||
refetchQueries: refetchFindManyQuery
|
||||
? [getOperationName(findManyRecordsQuery) ?? '']
|
||||
: [],
|
||||
});
|
||||
|
||||
if (!createdObject.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
createdObject.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
],
|
||||
);
|
||||
|
||||
return createdObject.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
|
||||
@ -30,11 +30,10 @@ export const useDeleteOneRecord = <T>({
|
||||
|
||||
const deleteOneRecord = useCallback(
|
||||
async (idToDelete: string) => {
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
undefined,
|
||||
[idToDelete],
|
||||
);
|
||||
triggerOptimisticEffects({
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
deletedRecordIds: [idToDelete],
|
||||
});
|
||||
|
||||
performOptimisticEvict(
|
||||
capitalize(objectMetadataItem.nameSingular),
|
||||
|
||||
@ -4,13 +4,12 @@ import { isNonEmptyArray } from '@apollo/client/utilities';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useRecordOptimisticEffect } from '@/object-metadata/hooks/useRecordOptimisticEffect';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
||||
import { getRecordOptimisticEffectDefinition } from '@/object-record/graphql/optimistic-effect-definition/getRecordOptimisticEffectDefinition';
|
||||
import { ObjectRecordFilter } from '@/object-record/types/ObjectRecordFilter';
|
||||
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||
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';
|
||||
@ -37,7 +36,7 @@ export const useFindManyRecords = <
|
||||
onCompleted,
|
||||
skip,
|
||||
}: ObjectMetadataItemIdentifier & {
|
||||
filter?: ObjectRecordFilter;
|
||||
filter?: ObjectRecordQueryFilter;
|
||||
orderBy?: OrderByField;
|
||||
limit?: number;
|
||||
onCompleted?: (data: PaginatedRecordTypeResults<RecordType>) => void;
|
||||
@ -65,8 +64,11 @@ export const useFindManyRecords = <
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { registerOptimisticEffect } = useOptimisticEffect({
|
||||
objectNameSingular,
|
||||
useRecordOptimisticEffect({
|
||||
objectMetadataItem,
|
||||
filter,
|
||||
orderBy,
|
||||
limit,
|
||||
});
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
@ -82,19 +84,6 @@ export const useFindManyRecords = <
|
||||
orderBy: orderBy ?? {},
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
if (objectMetadataItem) {
|
||||
registerOptimisticEffect({
|
||||
variables: {
|
||||
filter: filter ?? {},
|
||||
orderBy: orderBy ?? {},
|
||||
limit: limit,
|
||||
},
|
||||
definition: getRecordOptimisticEffectDefinition({
|
||||
objectMetadataItem,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
onCompleted?.(data[objectMetadataItem.namePlural]);
|
||||
|
||||
if (data?.[objectMetadataItem.namePlural]) {
|
||||
|
||||
@ -164,6 +164,6 @@ export const useGenerateEmptyRecord = ({
|
||||
};
|
||||
|
||||
return {
|
||||
generateEmptyRecord: generateEmptyRecord,
|
||||
generateEmptyRecord,
|
||||
};
|
||||
};
|
||||
|
||||
@ -5,8 +5,8 @@ import { Company } from '@/companies/types/Company';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
||||
import { turnFiltersIntoObjectRecordFilters } from '@/object-record/utils/turnFiltersIntoWhereClause';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
|
||||
@ -43,7 +43,7 @@ export const useObjectRecordBoard = () => {
|
||||
savedPipelineStepsState,
|
||||
);
|
||||
|
||||
const filter = turnFiltersIntoObjectRecordFilters(
|
||||
const filter = turnObjectDropdownFilterIntoQueryFilter(
|
||||
boardFilters,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
);
|
||||
|
||||
@ -4,10 +4,10 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
||||
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { isRecordTableInitialLoadingState } from '@/object-record/record-table/states/isRecordTableInitialLoadingState';
|
||||
import { turnFiltersIntoObjectRecordFilters } from '@/object-record/utils/turnFiltersIntoWhereClause';
|
||||
import { signInBackgroundMockCompanies } from '@/sign-in-background-mock/constants/signInBackgroundMockCompanies';
|
||||
|
||||
import { useFindManyRecords } from './useFindManyRecords';
|
||||
@ -32,7 +32,7 @@ export const useObjectRecordTable = () => {
|
||||
const tableSorts = useRecoilValue(tableSortsState);
|
||||
const setLastRowVisible = useSetRecoilState(tableLastRowVisibleState);
|
||||
|
||||
const requestFilters = turnFiltersIntoObjectRecordFilters(
|
||||
const requestFilters = turnObjectDropdownFilterIntoQueryFilter(
|
||||
tableFilters,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
@ -11,14 +11,13 @@ type useUpdateOneRecordProps = {
|
||||
|
||||
export const useUpdateOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
refetchFindManyQuery = false,
|
||||
}: useUpdateOneRecordProps) => {
|
||||
const {
|
||||
objectMetadataItem,
|
||||
updateOneRecordMutation,
|
||||
getRecordFromCache,
|
||||
findManyRecordsQuery,
|
||||
} = useObjectMetadataItem({
|
||||
const { objectMetadataItem, updateOneRecordMutation, getRecordFromCache } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
@ -34,6 +33,16 @@ export const useUpdateOneRecord = <T>({
|
||||
}) => {
|
||||
const cachedRecord = getRecordFromCache(idToUpdate);
|
||||
|
||||
triggerOptimisticEffects({
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
updatedRecords: [
|
||||
{
|
||||
...(cachedRecord ?? {}),
|
||||
...input,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const updatedRecord = await apolloClient.mutate({
|
||||
mutation: updateOneRecordMutation,
|
||||
variables: {
|
||||
@ -48,18 +57,17 @@ export const useUpdateOneRecord = <T>({
|
||||
...input,
|
||||
},
|
||||
},
|
||||
refetchQueries: refetchFindManyQuery
|
||||
? [getOperationName(findManyRecordsQuery) ?? '']
|
||||
: [],
|
||||
});
|
||||
|
||||
if (!updatedRecord?.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return updatedRecord.data[
|
||||
const updatedData = updatedRecord.data[
|
||||
`update${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
|
||||
return updatedData;
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user