From 0c28d853e4fa403024e82d3f81a3d766a2ff749a Mon Sep 17 00:00:00 2001
From: neo773 <62795688+neo773@users.noreply.github.com>
Date: Sat, 13 Sep 2025 18:54:22 +0530
Subject: [PATCH] fix merge records network race condition (#14466)
/closes #13643
---
.../object-record/record-merge/hooks/useMergePreview.ts | 6 ++++--
.../record-merge/hooks/useMergeRecordsActions.ts | 8 +++++++-
.../record-merge/states/mergeInProgressState.ts | 6 ++++++
3 files changed, 17 insertions(+), 3 deletions(-)
create mode 100644 packages/twenty-front/src/modules/object-record/record-merge/states/mergeInProgressState.ts
diff --git a/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergePreview.ts b/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergePreview.ts
index 5bf3c29ecb5..4aa7d69cadf 100644
--- a/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergePreview.ts
+++ b/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergePreview.ts
@@ -6,6 +6,7 @@ import { useMergeManyRecords } from '@/object-record/hooks/useMergeManyRecords';
import { useMergeRecordRelationships } from '@/object-record/record-merge/hooks/useMergeRecordRelationships';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
+import { isMergeInProgressState } from '../states/mergeInProgressState';
import { mergeSettingsState } from '../states/mergeSettingsState';
type UseMergePreviewProps = {
@@ -20,6 +21,7 @@ export const useMergePreview = ({
const [isGeneratingPreview, setIsGeneratingPreview] = useState(false);
const mergeSettings = useRecoilValue(mergeSettingsState);
+ const isMergeInProgress = useRecoilValue(isMergeInProgressState);
const { records: selectedRecords } = useFindManyRecordsSelectedInContextStore(
{
limit: 10,
@@ -39,7 +41,7 @@ export const useMergePreview = ({
useEffect(() => {
const fetchPreview = async () => {
- if (selectedRecords.length < 2) return;
+ if (selectedRecords.length < 2 || isMergeInProgress) return;
setIsGeneratingPreview(true);
try {
const previewRecord = await mergeManyRecords({
@@ -62,7 +64,7 @@ export const useMergePreview = ({
}
};
- if (selectedRecords.length > 0) {
+ if (selectedRecords.length > 0 && !isMergeInProgress) {
fetchPreview();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordsActions.ts b/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordsActions.ts
index b4aa2fab02b..e686c761f3e 100644
--- a/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordsActions.ts
+++ b/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordsActions.ts
@@ -1,5 +1,5 @@
import { useLingui } from '@lingui/react/macro';
-import { useRecoilValue } from 'recoil';
+import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { useFindManyRecordsSelectedInContextStore } from '@/context-store/hooks/useFindManyRecordsSelectedInContextStore';
@@ -7,6 +7,7 @@ import { useMergeManyRecords } from '@/object-record/hooks/useMergeManyRecords';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { AppPath } from 'twenty-shared/types';
import { useNavigateApp } from '~/hooks/useNavigateApp';
+import { isMergeInProgressState } from '../states/mergeInProgressState';
import { mergeSettingsState } from '../states/mergeSettingsState';
type UseMergeRecordsActionsProps = {
@@ -27,12 +28,15 @@ export const useMergeRecordsActions = ({
objectNameSingular,
});
+ const setMergeInProgress = useSetRecoilState(isMergeInProgressState);
+
const { t } = useLingui();
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
const { closeCommandMenu } = useCommandMenu();
const navigate = useNavigateApp();
const handleMergeRecords = async () => {
+ setMergeInProgress(true);
try {
const mergedRecord = await mergeManyRecords({
recordIds: selectedRecords.map((record) => record.id),
@@ -62,6 +66,8 @@ export const useMergeRecordsActions = ({
? error.message
: 'Failed to merge records. Please try again.',
});
+ } finally {
+ setMergeInProgress(false);
}
};
diff --git a/packages/twenty-front/src/modules/object-record/record-merge/states/mergeInProgressState.ts b/packages/twenty-front/src/modules/object-record/record-merge/states/mergeInProgressState.ts
new file mode 100644
index 00000000000..f7c952a11e0
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-merge/states/mergeInProgressState.ts
@@ -0,0 +1,6 @@
+import { createState } from 'twenty-ui/utilities';
+
+export const isMergeInProgressState = createState({
+ key: 'isMergeInProgress',
+ defaultValue: false,
+});