🦣🦣🦣 Table virtualization (#14743)
This big PR implements table virtualization with an offset paging, allowing a way more fluid UX. It is a v1 that should be improved in the future with partial data loading and optimization of the browser display performance of a row. But with this PR we have the solid enough technical foundation, both frontend and backend, to get to a smooth table UX. Fixes and improvements after first successful round of development (needed to have main clean) : - [x] Delete should refresh virtualized portion only and reset all table - [x] Fix add new : top and bottom - [x] Table empty shouldn’t show when first loading - [x] Fix d&d - [x] Fix sorts - [x] Fix drag when scrolling after a full virtual page (it throws an error) - [x] Si update mais qu’on a un sort ou filter, alors il faut trigger le refresh - [x] Reset scroll position between tables - [x] Reset scroll shadows between tables - [x] Setup d&n for virtual list : https://github.com/hello-pangea/dnd/blob/main/docs/patterns/virtual-lists.md - [x] Full table re-render when entering edit mode - [x] Clean code and prepare for merge Fixes https://github.com/twentyhq/core-team-issues/issues/1613 that contains other bugs to be fixed before merge --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+9
-3
@@ -1,13 +1,19 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { print } from 'graphql';
|
||||
|
||||
import { PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS } from '@/object-record/hooks/__mocks__/personFragments';
|
||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||
import { print } from 'graphql';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
|
||||
const expectedQueryTemplate = `
|
||||
query FindManyPeople($filter: PersonFilterInput, $orderBy: [PersonOrderByInput], $lastCursor: String, $limit: Int) {
|
||||
people(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor) {
|
||||
query FindManyPeople($filter: PersonFilterInput, $orderBy: [PersonOrderByInput], $lastCursor: String, $limit: Int, $offset: Int) {
|
||||
people(
|
||||
filter: $filter
|
||||
orderBy: $orderBy
|
||||
first: $limit
|
||||
after: $lastCursor
|
||||
offset: $offset
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
${PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS}
|
||||
|
||||
+2
@@ -74,12 +74,14 @@ const mock: MockedResponse = {
|
||||
$orderBy: [PersonOrderByInput]
|
||||
$lastCursor: String
|
||||
$limit: Int
|
||||
$offset: Int
|
||||
) {
|
||||
people(
|
||||
filter: $filter
|
||||
orderBy: $orderBy
|
||||
first: $limit
|
||||
after: $lastCursor
|
||||
offset: $offset
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
|
||||
+2
@@ -35,12 +35,14 @@ const mocks: MockedResponse[] = [
|
||||
$orderBy: [PersonOrderByInput]
|
||||
$lastCursor: String
|
||||
$limit: Int
|
||||
$offset: Int
|
||||
) {
|
||||
people(
|
||||
filter: $filter
|
||||
orderBy: $orderBy
|
||||
first: $limit
|
||||
after: $lastCursor
|
||||
offset: $offset
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type useCreateManyRecordsProps,
|
||||
} from '@/object-record/hooks/useCreateManyRecords';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
@@ -26,6 +27,8 @@ export const useBatchCreateManyRecords = <
|
||||
setBatchedRecordsCount?: (count: number) => void;
|
||||
abortController?: AbortController;
|
||||
}) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const { createManyRecords } = useCreateManyRecords({
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
@@ -97,6 +100,8 @@ export const useBatchCreateManyRecords = <
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectNameSingular, { type: 'create-many' });
|
||||
|
||||
return allCreatedRecords;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/
|
||||
import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type FieldActorForInputValue } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
@@ -49,6 +50,8 @@ export const useCreateManyRecords = <
|
||||
shouldMatchRootQueryFilter,
|
||||
shouldRefetchAggregateQueries = true,
|
||||
}: useCreateManyRecordsProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
@@ -228,7 +231,11 @@ export const useCreateManyRecords = <
|
||||
throw error;
|
||||
});
|
||||
|
||||
if (shouldRefetchAggregateQueries) await refetchAggregateQueries();
|
||||
if (shouldRefetchAggregateQueries) {
|
||||
await refetchAggregateQueries();
|
||||
}
|
||||
|
||||
registerObjectOperation(objectNameSingular, { type: 'create-many' });
|
||||
|
||||
return createdObjects.data?.[mutationResponseField] ?? [];
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/
|
||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticCreateRecordBaseRecordInput } from '@/object-record/utils/computeOptimisticCreateRecordBaseRecordInput';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
@@ -38,6 +39,7 @@ export const useCreateOneRecord = <
|
||||
skipPostOptimisticEffect = false,
|
||||
shouldMatchRootQueryFilter,
|
||||
}: useCreateOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -174,6 +176,9 @@ export const useCreateOneRecord = <
|
||||
});
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectNameSingular, { type: 'create-one' });
|
||||
|
||||
return createdObject.data?.[mutationResponseField] ?? null;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode'
|
||||
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -32,6 +33,7 @@ export type DeleteManyRecordsProps = {
|
||||
export const useDeleteManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useDeleteManyRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
|
||||
const mutationPageSize =
|
||||
@@ -215,6 +217,11 @@ export const useDeleteManyRecords = ({
|
||||
}
|
||||
}
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectNameSingular, {
|
||||
type: 'delete-many',
|
||||
});
|
||||
|
||||
return deletedRecords;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordF
|
||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
@@ -24,6 +25,7 @@ type useDeleteOneRecordProps = {
|
||||
export const useDeleteOneRecord = ({
|
||||
objectNameSingular,
|
||||
}: useDeleteOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
@@ -154,6 +156,11 @@ export const useDeleteOneRecord = ({
|
||||
});
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectNameSingular, {
|
||||
type: 'delete-one',
|
||||
});
|
||||
|
||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||
},
|
||||
[
|
||||
@@ -165,6 +172,8 @@ export const useDeleteOneRecord = ({
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
refetchAggregateQueries,
|
||||
objectNameSingular,
|
||||
registerObjectOperation,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMu
|
||||
import { useDestroyManyRecordsMutation } from '@/object-record/hooks/useDestroyManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getDestroyManyRecordsMutationResponseField } from '@/object-record/utils/getDestroyManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -29,6 +30,7 @@ export type DestroyManyRecordsProps = {
|
||||
export const useDestroyManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useDestroyManyRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
|
||||
const mutationPageSize =
|
||||
@@ -137,6 +139,11 @@ export const useDestroyManyRecords = ({
|
||||
}
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectNameSingular, {
|
||||
type: 'destroy-many',
|
||||
});
|
||||
|
||||
return destroyedRecords;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { getDestroyOneRecordMutationResponseField } from '@/object-record/utils/getDestroyOneRecordMutationResponseField';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -19,6 +20,8 @@ type useDestroyOneRecordProps = {
|
||||
export const useDestroyOneRecord = ({
|
||||
objectNameSingular,
|
||||
}: useDestroyOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
@@ -81,6 +84,10 @@ export const useDestroyOneRecord = ({
|
||||
throw error;
|
||||
});
|
||||
|
||||
registerObjectOperation(objectMetadataItem.nameSingular, {
|
||||
type: 'destroy-one',
|
||||
});
|
||||
|
||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||
},
|
||||
[
|
||||
@@ -92,6 +99,7 @@ export const useDestroyOneRecord = ({
|
||||
objectNameSingular,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
registerObjectOperation,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+3
-1
@@ -24,6 +24,7 @@ import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindMan
|
||||
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
|
||||
import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier';
|
||||
|
||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { cursorFamilyState } from '../states/cursorFamilyState';
|
||||
import { hasNextPageFamilyState } from '../states/hasNextPageFamilyState';
|
||||
@@ -88,7 +89,7 @@ export const useLazyFetchMoreRecordsWithPagination = <
|
||||
// This function is equivalent to merge function + read function in field policy
|
||||
const fetchMoreRecordsLazy = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async () => {
|
||||
async (limit = DEFAULT_SEARCH_REQUEST_LIMIT) => {
|
||||
const hasNextPageLocal = snapshot
|
||||
.getLoadable(hasNextPageFamilyState(queryIdentifier))
|
||||
.getValue();
|
||||
@@ -105,6 +106,7 @@ export const useLazyFetchMoreRecordsWithPagination = <
|
||||
try {
|
||||
const { data: fetchMoreDataResult } = await fetchMore({
|
||||
variables: {
|
||||
limit,
|
||||
filter,
|
||||
orderBy,
|
||||
lastCursor: isNonEmptyString(lastCursorLocal)
|
||||
|
||||
@@ -96,7 +96,7 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
|
||||
return {
|
||||
data: null,
|
||||
records: [],
|
||||
records: null,
|
||||
totalCount: 0,
|
||||
hasNextPage: false,
|
||||
error: undefined,
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
import { useLazyQuery } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
|
||||
import { type UseFindManyRecordsParams } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||
import { useHandleFindManyRecordsError } from '@/object-record/hooks/useHandleFindManyRecordsError';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
|
||||
import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
|
||||
type UseLazyFindManyRecordsWithOffsetParams = Pick<
|
||||
UseFindManyRecordsParams<ObjectRecord>,
|
||||
'objectNameSingular'
|
||||
>;
|
||||
|
||||
export const useLazyFindManyRecordsWithOffset = ({
|
||||
objectNameSingular,
|
||||
}: UseLazyFindManyRecordsWithOffsetParams) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
||||
|
||||
const recordGqlFields = useRecordsFieldVisibleGqlFields({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const { findManyRecordsQuery } = useFindManyRecordsQuery({
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
const { handleFindManyRecordsError } = useHandleFindManyRecordsError({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const hasReadPermission = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const [findManyRecords] = useLazyQuery<RecordGqlOperationFindManyResult>(
|
||||
findManyRecordsQuery,
|
||||
{
|
||||
variables: {
|
||||
...params,
|
||||
},
|
||||
onError: handleFindManyRecordsError,
|
||||
client: apolloCoreClient,
|
||||
},
|
||||
);
|
||||
|
||||
const findManyRecordsLazyWithOffset = useRecoilCallback(
|
||||
() => async (limit: number, offset: number) => {
|
||||
if (!hasReadPermission) {
|
||||
return {
|
||||
data: null,
|
||||
records: null,
|
||||
totalCount: 0,
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const result = await findManyRecords({
|
||||
variables: {
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
});
|
||||
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges: result?.data?.[objectMetadataItem.namePlural]?.edges ?? [],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
endCursor: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: result?.data,
|
||||
records,
|
||||
error: result?.error,
|
||||
};
|
||||
},
|
||||
[hasReadPermission, findManyRecords, objectMetadataItem.namePlural],
|
||||
);
|
||||
|
||||
return {
|
||||
findManyRecordsLazyWithOffset,
|
||||
};
|
||||
};
|
||||
+3
-2
@@ -55,8 +55,9 @@ export const useLoadSelectedRecordsInContextStore = ({
|
||||
objectRecordIds.length,
|
||||
);
|
||||
|
||||
const records = await findManyRecordsLazy();
|
||||
upsertRecords(records.records);
|
||||
const { records } = await findManyRecordsLazy();
|
||||
|
||||
upsertRecords(records ?? []);
|
||||
};
|
||||
},
|
||||
[objectRecordIds, objectMetadataItemId, findManyRecordsLazy, upsertRecords],
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDupli
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { useMergeManyRecordsMutation } from '@/object-record/hooks/useMergeManyRecordsMutation';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getMergeManyRecordsMutationResponseField } from '@/object-record/utils/getMergeManyRecordsMutationResponseField';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
@@ -27,6 +28,7 @@ export const useMergeManyRecords = <
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
}: UseMergeManyRecordsProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -103,6 +105,10 @@ export const useMergeManyRecords = <
|
||||
await refetchAggregateQueries();
|
||||
}
|
||||
|
||||
registerObjectOperation(objectNameSingular, {
|
||||
type: 'merge-records',
|
||||
});
|
||||
|
||||
return mergedObject.data?.[mutationResponseField] ?? null;
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
@@ -116,6 +122,8 @@ export const useMergeManyRecords = <
|
||||
mergeManyRecordsMutation,
|
||||
objectMetadataItem.namePlural,
|
||||
refetchAggregateQueries,
|
||||
registerObjectOperation,
|
||||
objectNameSingular,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
type ObjectOperationData,
|
||||
objectOperationsByObjectNameSingularFamilyState,
|
||||
} from '@/object-record/states/objectOperationsByObjectNameSingularFamilyState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const useRegisterObjectOperation = () => {
|
||||
const registerObjectOperation = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(objectNameSingular: string, data: ObjectOperationData) => {
|
||||
set(
|
||||
objectOperationsByObjectNameSingularFamilyState({
|
||||
objectNameSingular,
|
||||
}),
|
||||
(currentValue) => {
|
||||
const newValue = currentValue.concat();
|
||||
|
||||
newValue.push({
|
||||
id: v4(),
|
||||
timestamp: +new Date(),
|
||||
data,
|
||||
});
|
||||
|
||||
return newValue;
|
||||
},
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
registerObjectOperation,
|
||||
};
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useRestoreManyRecordsMutation } from '@/object-record/hooks/useRestoreManyRecordsMutation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getRestoreManyRecordsMutationResponseField } from '@/object-record/utils/getRestoreManyRecordsMutationResponseField';
|
||||
@@ -30,6 +31,8 @@ type RestoreManyRecordsProps = {
|
||||
export const useRestoreManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useRestoreManyRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
|
||||
const mutationPageSize =
|
||||
@@ -191,6 +194,10 @@ export const useRestoreManyRecords = ({
|
||||
|
||||
restoredRecords.push(...restoredRecordsForThisBatch);
|
||||
|
||||
registerObjectOperation(objectMetadataItem.nameSingular, {
|
||||
type: 'restore-many',
|
||||
});
|
||||
|
||||
if (isDefined(delayInMsBetweenRequests)) {
|
||||
await sleep(delayInMsBetweenRequests);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graph
|
||||
import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
@@ -36,6 +37,7 @@ export const useUpdateOneRecord = <
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
}: useUpdateOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
@@ -217,7 +219,15 @@ export const useUpdateOneRecord = <
|
||||
});
|
||||
|
||||
await refetchAggregateQueries();
|
||||
return updatedRecord?.data?.[mutationResponseField] ?? null;
|
||||
|
||||
const udpatedRecord = updatedRecord?.data?.[mutationResponseField] ?? null;
|
||||
|
||||
registerObjectOperation(objectNameSingular, {
|
||||
type: 'update-one',
|
||||
result: { updatedRecord, updateInput: updateOneRecordInput },
|
||||
});
|
||||
|
||||
return udpatedRecord;
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user