Fix board drag and drop issues (#19005)

This PR solves multiple problems : 
- An infinite loop that was happening on board with initial and fetch
more queries
- Warning messages that get triggered when we drag and drop multiple
times in a row
- An attempt to fix an existing error in Sentry, that couldn't be
reproduced for now.
This commit is contained in:
Lucas Bordeau
2026-03-26 16:06:20 +01:00
committed by GitHub
parent e63a23ea00
commit 8ef99671e2
13 changed files with 151 additions and 67 deletions
@@ -1,6 +1,7 @@
import { useStore } from 'jotai';
import { useListenToObjectRecordOperationBrowserEvent } from '@/browser-event/hooks/useListenToObjectRecordOperationBrowserEvent';
import { type ObjectRecordOperationBrowserEventDetail } from '@/browser-event/types/ObjectRecordOperationBrowserEventDetail';
import { useGetShouldInitializeRecordBoardForUpdateInputs } from '@/object-record/record-board/hooks/useGetShouldInitializeRecordBoardForUpdateInputs';
import { useRemoveRecordsFromBoard } from '@/object-record/record-board/hooks/useRemoveRecordsFromBoard';
import { useTriggerRecordBoardInitialQuery } from '@/object-record/record-board/hooks/useTriggerRecordBoardInitialQuery';
@@ -9,7 +10,6 @@ import { useRecordIndexContextOrThrow } from '@/object-record/record-index/conte
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { type ObjectRecordOperationBrowserEventDetail } from '@/browser-event/types/ObjectRecordOperationBrowserEventDetail';
import { useAtomComponentFamilySelectorCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorCallbackState';
import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
@@ -1,4 +1,5 @@
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { isRecordBoardDropProcessingComponentState } from '@/object-record/record-board/states/isRecordBoardDropProcessingComponentState';
import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
import { useEndRecordDrag } from '@/object-record/record-drag/hooks/useEndRecordDrag';
import { useProcessBoardCardDrop } from '@/object-record/record-drag/hooks/useProcessBoardCardDrop';
@@ -8,14 +9,14 @@ import { originalDragSelectionComponentState } from '@/object-record/record-drag
import { RECORD_INDEX_REMOVE_SORTING_MODAL_ID } from '@/object-record/record-index/constants/RecordIndexRemoveSortingModalId';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
import { useModal } from '@/ui/layout/modal/hooks/useModal';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useAtomComponentSelectorCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorCallbackState';
import { useStore } from 'jotai';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import {
DragDropContext,
type DragStart,
type OnDragEndResponder,
} from '@hello-pangea/dnd';
import { useStore } from 'jotai';
import { useCallback, useContext } from 'react';
export const RecordBoardDragDropContext = ({
@@ -35,7 +36,7 @@ export const RecordBoardDragDropContext = ({
const store = useStore();
const originalDragSelection = useAtomComponentStateCallbackState(
const originalDragSelectionCallbackState = useAtomComponentStateCallbackState(
originalDragSelectionComponentState,
recordBoardId,
);
@@ -45,41 +46,70 @@ export const RecordBoardDragDropContext = ({
const { processBoardCardDrop } = useProcessBoardCardDrop();
const isRecordBoardDropProcessingCallbackState =
useAtomComponentStateCallbackState(
isRecordBoardDropProcessingComponentState,
);
const { openModal } = useModal();
const handleDragStart = useCallback(
(start: DragStart) => {
const currentSelectedRecordIds = store.get(recordBoardSelectedRecordIds);
store.set(isRecordBoardDropProcessingCallbackState, true);
startRecordDrag(start, currentSelectedRecordIds);
},
[recordBoardSelectedRecordIds, startRecordDrag, store],
[
recordBoardSelectedRecordIds,
startRecordDrag,
store,
isRecordBoardDropProcessingCallbackState,
],
);
const handleDragEnd: OnDragEndResponder = useCallback(
(result) => {
endRecordDrag();
const originalDragSelection = store.get(
originalDragSelectionCallbackState,
);
if (!result.destination) return;
if (!result.destination) {
store.set(isRecordBoardDropProcessingCallbackState, false);
endRecordDrag();
return;
}
const existingRecordSorts = store.get(currentRecordSorts);
if (existingRecordSorts.length > 0) {
store.set(isRecordBoardDropProcessingCallbackState, false);
endRecordDrag();
openModal(RECORD_INDEX_REMOVE_SORTING_MODAL_ID);
return;
}
const originalSelection = store.get(originalDragSelection) as string[];
try {
processBoardCardDrop(result, originalDragSelection);
} catch (error) {
store.set(isRecordBoardDropProcessingCallbackState, false);
endRecordDrag();
processBoardCardDrop(result, originalSelection);
throw error;
}
store.set(isRecordBoardDropProcessingCallbackState, false);
endRecordDrag();
},
[
processBoardCardDrop,
originalDragSelection,
endRecordDrag,
currentRecordSorts,
openModal,
store,
originalDragSelectionCallbackState,
isRecordBoardDropProcessingCallbackState,
],
);
@@ -7,8 +7,8 @@ import { RECORD_BOARD_COLUMN_PADDING_AND_BORDER_WIDTH } from '@/object-record/re
import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth';
import { RECORD_BOARD_QUERY_PAGE_SIZE } from '@/object-record/record-board/constants/RecordBoardQueryPageSize';
import { recordBoardIsFetchingMoreComponentState } from '@/object-record/record-board/states/recordBoardIsFetchingMoreComponentState';
import { recordBoardShouldFetchMoreComponentState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreComponentState';
import { isDraggingRecordComponentState } from '@/object-record/record-drag/states/isDraggingRecordComponentState';
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
import { recordIndexRecordGroupsAreInInitialLoadingComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupsAreInInitialLoadingComponentState';
@@ -38,12 +38,12 @@ export const RecordBoardFetchMoreInViewTriggerComponent = () => {
const [recordBoardShouldFetchMore, setRecordBoardShouldFetchMore] =
useAtomComponentState(recordBoardShouldFetchMoreComponentState);
const recordIndexRecordGroupsAreInInitialLoading = useAtomComponentStateValue(
recordIndexRecordGroupsAreInInitialLoadingComponentState,
const isDraggingRecord = useAtomComponentStateValue(
isDraggingRecordComponentState,
);
const recordBoardIsFetchingMore = useAtomComponentStateValue(
recordBoardIsFetchingMoreComponentState,
const recordIndexRecordGroupsAreInInitialLoading = useAtomComponentStateValue(
recordIndexRecordGroupsAreInInitialLoadingComponentState,
);
const visibleRecordFields = useAtomComponentSelectorValue(
@@ -76,22 +76,21 @@ export const RecordBoardFetchMoreInViewTriggerComponent = () => {
1;
useEffect(() => {
if (
!recordIndexRecordGroupsAreInInitialLoading &&
!recordBoardIsFetchingMore
) {
const newShouldFetchMore = inView;
if (recordIndexRecordGroupsAreInInitialLoading || isDraggingRecord) {
return;
}
if (recordBoardShouldFetchMore !== newShouldFetchMore) {
setRecordBoardShouldFetchMore(newShouldFetchMore);
}
const newShouldFetchMore = inView;
if (recordBoardShouldFetchMore !== newShouldFetchMore) {
setRecordBoardShouldFetchMore(newShouldFetchMore);
}
}, [
recordBoardShouldFetchMore,
setRecordBoardShouldFetchMore,
inView,
recordIndexRecordGroupsAreInInitialLoading,
recordBoardIsFetchingMore,
isDraggingRecord,
]);
return (
@@ -3,8 +3,8 @@ import { useTriggerRecordBoardInitialQuery } from '@/object-record/record-board/
import { lastRecordBoardQueryIdentifierComponentState } from '@/object-record/record-board/states/lastRecordBoardQueryIdentifierComponentState';
import { lastRecordGroupIdsComponentState } from '@/object-record/record-board/states/lastRecordGroupIdsComponentState';
import { recordBoardCurrentGroupByQueryOffsetComponentState } from '@/object-record/record-board/states/recordBoardCurrentGroupByQueryOffsetComponentState';
import { recordBoardIsFetchingMoreComponentState } from '@/object-record/record-board/states/recordBoardIsFetchingMoreComponentState';
import { recordBoardShouldFetchMoreComponentState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreComponentState';
import { isDraggingRecordComponentState } from '@/object-record/record-drag/states/isDraggingRecordComponentState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecordIndexGroupCommonQueryVariables } from '@/object-record/record-index/hooks/useRecordIndexGroupCommonQueryVariables';
@@ -22,6 +22,10 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const RecordBoardQueryEffect = () => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const isDraggingRecord = useAtomComponentStateValue(
isDraggingRecordComponentState,
);
const [lastRecordBoardQueryIdentifier, setLastRecordBoardQueryIdentifier] =
useAtomComponentState(lastRecordBoardQueryIdentifierComponentState);
@@ -51,14 +55,10 @@ export const RecordBoardQueryEffect = () => {
const { scrollWrapperHTMLElement } = useScrollWrapperHTMLElement();
const [recordBoardShouldFetchMore] = useAtomComponentState(
const recordBoardShouldFetchMore = useAtomComponentStateValue(
recordBoardShouldFetchMoreComponentState,
);
const recordBoardIsFetchingMore = useAtomComponentStateValue(
recordBoardIsFetchingMoreComponentState,
);
const { triggerRecordBoardFetchMore } = useTriggerRecordBoardFetchMore();
const { triggerRecordBoardInitialQuery } =
@@ -73,6 +73,10 @@ export const RecordBoardQueryEffect = () => {
);
useEffect(() => {
if (isDraggingRecord) {
return;
}
if (
!recordIndexRecordGroupsAreInInitialLoading &&
(queryIdentifierHasChanged || recordGroupIdsHaveChanged)
@@ -82,8 +86,7 @@ export const RecordBoardQueryEffect = () => {
} else if (
!recordIndexRecordGroupsAreInInitialLoading &&
recordBoardShouldFetchMore &&
!queryIdentifierHasChanged &&
!recordBoardIsFetchingMore
!queryIdentifierHasChanged
) {
triggerRecordBoardFetchMore();
}
@@ -96,11 +99,11 @@ export const RecordBoardQueryEffect = () => {
scrollWrapperHTMLElement,
recordIndexRecordGroupsAreInInitialLoading,
recordBoardShouldFetchMore,
recordBoardIsFetchingMore,
triggerRecordBoardFetchMore,
setLastRecordGroupIds,
recordGroupIds,
recordGroupIdsHaveChanged,
isDraggingRecord,
]);
return null;
@@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { RECORD_BOARD_FETCH_MORE_THROTTLING_WAIT_TIME_IN_MILLISECONDS_TO_AVOID_REACT_FREEZE } from '@/object-record/record-board/constants/RecordBoardFetchMoreThrottlingWaitTimeInMillisecondsToAvoidReactFreeze';
@@ -7,7 +7,6 @@ import { RECORD_BOARD_QUERY_PAGE_SIZE } from '@/object-record/record-board/const
import { recordBoardCurrentGroupByQueryOffsetComponentState } from '@/object-record/record-board/states/recordBoardCurrentGroupByQueryOffsetComponentState';
import { recordBoardIsFetchingMoreComponentState } from '@/object-record/record-board/states/recordBoardIsFetchingMoreComponentState';
import { recordBoardShouldFetchMoreComponentState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreComponentState';
import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState';
import { recordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
@@ -17,10 +16,10 @@ import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { getGroupByQueryResultGqlFieldName } from '@/page-layout/utils/getGroupByQueryResultGqlFieldName';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { isNonEmptyArray } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { computeRecordGroupOptionsFilter } from '@/object-record/record-group/utils/computeRecordGroupOptionsFilter';
@@ -68,11 +67,6 @@ export const useTriggerRecordBoardFetchMore = () => {
recordBoardIsFetchingMoreComponentState,
);
const recordBoardShouldFetchMoreCallbackState =
useAtomComponentStateCallbackState(
recordBoardShouldFetchMoreComponentState,
);
const triggerRecordBoardFetchMore = useCallback(async () => {
const isAlreadyFetchingMore = store.get(recordBoardIsFetchingMore);
@@ -103,7 +97,6 @@ export const useTriggerRecordBoardFetchMore = () => {
.map((recordGroupDefinition) => recordGroupDefinition.value);
if (!isNonEmptyArray(recordGroupValuesThatShouldBeFetched)) {
store.set(recordBoardShouldFetchMoreCallbackState, false);
cleanStateBeforeExit();
return;
@@ -227,7 +220,6 @@ export const useTriggerRecordBoardFetchMore = () => {
recordBoardIsFetchingMore,
recordBoardCurrentGroupByQueryOffsetCallbackState,
recordBoardShouldFetchMoreInColumnFamilyCallbackState,
recordBoardShouldFetchMoreCallbackState,
combinedFilters,
recordIndexGroupFieldMetadataItem,
]);
@@ -15,13 +15,13 @@ import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier';
import { getGroupByQueryResultGqlFieldName } from '@/page-layout/utils/getGroupByQueryResultGqlFieldName';
import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement';
import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { isNonEmptyArray } from '@sniptt/guards';
import { useCallback } from 'react';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
export const useTriggerRecordBoardInitialQuery = () => {
@@ -1,5 +1,5 @@
import { styled } from '@linaria/react';
import { Draggable } from '@hello-pangea/dnd';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
@@ -10,10 +10,15 @@ import { RecordBoardCardMultiDragPreview } from '@/object-record/record-board/re
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { isRecordBoardCardFocusedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardFocusedComponentFamilyState';
import { isRecordBoardDropProcessingComponentState } from '@/object-record/record-board/states/isRecordBoardDropProcessingComponentState';
import { DragAndDropLibraryLegacyReRenderBreaker } from '@/ui/drag-and-drop/components/DragAndDropReRenderBreaker';
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
const StyledDraggableContainer = styled.div`
const StyledDraggableContainer = styled.div<{
isDragDisabled: boolean;
}>`
cursor: ${({ isDragDisabled }) => (isDragDisabled ? 'default' : 'grab')};
position: relative;
scroll-margin-left: 8px;
scroll-margin-right: 8px;
@@ -34,6 +39,10 @@ export const RecordBoardCardDraggableContainer = ({
objectMetadataId: objectMetadataItem.id,
});
const isRecordBoardDropProcessing = useAtomComponentStateValue(
isRecordBoardDropProcessingComponentState,
);
const { columnIndex } = useContext(RecordBoardColumnContext);
const isRecordBoardCardFocused = useAtomComponentFamilyStateValue(
@@ -48,9 +57,15 @@ export const RecordBoardCardDraggableContainer = ({
<RecordBoardCardContext.Provider
value={{ recordId, isRecordReadOnly, rowIndex, columnIndex }}
>
<Draggable key={recordId} draggableId={recordId} index={rowIndex}>
<Draggable
key={recordId}
draggableId={recordId}
index={rowIndex}
isDragDisabled={isRecordBoardDropProcessing}
>
{(draggableProvided) => (
<StyledDraggableContainer
isDragDisabled={isRecordBoardDropProcessing}
id={`record-board-card-${columnIndex}-${rowIndex}`}
ref={draggableProvided?.innerRef}
// oxlint-disable-next-line react/jsx-props-no-spreading
@@ -1,5 +1,5 @@
import { styled } from '@linaria/react';
import { Draggable, type DroppableProvided } from '@hello-pangea/dnd';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
@@ -0,0 +1,9 @@
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
export const isRecordBoardDropProcessingComponentState =
createAtomComponentState<boolean>({
key: 'isRecordBoardDropProcessingComponentState',
defaultValue: false,
componentInstanceContext: RecordBoardComponentInstanceContext,
});
@@ -6,9 +6,12 @@ import { isDefined } from 'twenty-shared/utils';
import { processGroupDrop } from '@/object-record/record-drag/utils/processGroupDrop';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { isRecordBoardDropProcessingComponentState } from '@/object-record/record-board/states/isRecordBoardDropProcessingComponentState';
import { useUpdateDroppedRecordOnBoard } from '@/object-record/record-drag/hooks/useUpdateDroppedRecordOnBoard';
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useDebouncedCallback } from 'use-debounce';
export const useProcessBoardCardDrop = () => {
const store = useStore();
@@ -21,6 +24,21 @@ export const useProcessBoardCardDrop = () => {
const { updateDroppedRecordOnBoard } = useUpdateDroppedRecordOnBoard();
const isRecordBoardDropProcessingCallbackState =
useAtomComponentStateCallbackState(
isRecordBoardDropProcessingComponentState,
);
// TODO: this is necessary to avoid race conditions when dragging right after a previous drag (~200ms to 500ms)
// A way to fix this would be to have a proper optimistic logic on drop that doesn't just resets the whole board with trigger initial query but updates everything without waiting for the request return
// Which is the problem here because it kind of destroys the existing columns that have more records than page size, and dnd library has issues computing drag when the underlying data change.
const debouncedUpdateDropProcessing = useDebouncedCallback(
(isPending: boolean) => {
store.set(isRecordBoardDropProcessingCallbackState, isPending);
},
500,
);
const processBoardCardDrop = useCallback(
(boardCardDropResult: DropResult, selectedRecordIds: string[]) => {
if (!isDefined(selectFieldMetadataItem)) return;
@@ -38,12 +56,15 @@ export const useProcessBoardCardDrop = () => {
);
},
});
debouncedUpdateDropProcessing(false);
},
[
store,
selectFieldMetadataItem,
recordIndexRecordIdsByGroupCallbackFamilyState,
updateDroppedRecordOnBoard,
debouncedUpdateDropProcessing,
],
);
@@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { draggedRecordIdsComponentState } from '@/object-record/record-drag/states/draggedRecordIdsComponentState';
import { isDraggingRecordComponentState } from '@/object-record/record-drag/states/isDraggingRecordComponentState';
@@ -1,6 +1,5 @@
import { useStore } from 'jotai';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { extractRecordPositions } from '@/object-record/record-drag/utils/extractRecordPositions';
import { recordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector';
@@ -8,6 +7,7 @@ import { type RecordGroupDefinition } from '@/object-record/record-group/types/R
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useCallback, useContext } from 'react';
@@ -35,7 +35,10 @@ export const useUpdateDroppedRecordOnBoard = () => {
{
recordId,
position: newPosition,
}: { recordId: string; position?: number },
}: {
recordId: string;
position?: number;
},
targetRecordGroupValue: RecordGroupDefinition['value'],
) => {
const initialRecord = store.get(
@@ -50,18 +53,32 @@ export const useUpdateDroppedRecordOnBoard = () => {
return;
}
const initialRecordGroupValue = initialRecord[
selectFieldMetadataItem.name
] as string | undefined;
let currentRecordIdsInInitialRecordGroup: string[] = [];
const initialRecordGroup = recordGroupDefinitions.find(
findByProperty('value', initialRecordGroupValue),
(recordGroupDefinition) => {
const recordIdsInGroup = store.get(
recordIndexRecordIdsByGroupCallbackFamilyState(
recordGroupDefinition.id,
),
);
const recordIsInGroup = recordIdsInGroup.includes(recordId);
if (recordIsInGroup) {
currentRecordIdsInInitialRecordGroup = recordIdsInGroup;
}
return recordIsInGroup;
},
);
if (!isDefined(initialRecordGroup)) {
return;
}
const initialRecordGroupId = initialRecordGroup.id;
const targetRecordGroup = recordGroupDefinitions.find(
findByProperty('value', targetRecordGroupValue),
);
@@ -70,7 +87,6 @@ export const useUpdateDroppedRecordOnBoard = () => {
return;
}
const initialRecordGroupId = initialRecordGroup.id;
const targetRecordGroupId = targetRecordGroup.id;
const movingInsideSameRecordGroup =
@@ -82,26 +98,22 @@ export const useUpdateDroppedRecordOnBoard = () => {
return;
}
const currentRecordIdsInInitialRecordGroup = store.get(
recordIndexRecordIdsByGroupCallbackFamilyState(initialRecordGroupId),
) as string[];
const positionOfDroppedRecordInInitialRecordIds =
const indexOfDroppedRecordInInitialRecordGroup =
currentRecordIdsInInitialRecordGroup.findIndex((id) => id === recordId);
let currentRecordIdsInTargetRecordGroup = store.get(
recordIndexRecordIdsByGroupCallbackFamilyState(targetRecordGroupId),
) as string[];
if (positionOfDroppedRecordInInitialRecordIds === -1) {
if (indexOfDroppedRecordInInitialRecordGroup === -1) {
throw new Error(
`Cannot find record id in initial record group ids on drop, this should not happen`,
`Cannot find record id in initial record group ids on drop, this should not happen, recordId: ${recordId}, initialRecordGroupId: ${initialRecordGroupId}`,
);
}
const newInitialGroupRecordIds =
currentRecordIdsInInitialRecordGroup.toSpliced(
positionOfDroppedRecordInInitialRecordIds,
indexOfDroppedRecordInInitialRecordGroup,
1,
);
@@ -15,7 +15,10 @@ type ProcessGroupDropParams = {
selectedRecordIds: string[];
recordIdsByGroupFamilyState: any;
onUpdateRecord: (
update: { recordId: string; position?: number },
update: {
recordId: string;
position?: number;
},
targetRecordGroupId: RecordGroupDefinition['value'],
) => void;
};