diff --git a/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts b/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts index 8671cf2bd5..30ec19f5bf 100644 --- a/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts +++ b/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts @@ -1,12 +1,14 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { useLoadRecordsToVirtualRows } from '@/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows'; import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; + import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; import { getVirtualizationOverscanWindow } from '@/object-record/record-table/virtualization/utils/getVirtualizationOverscanWindow'; import { type RecordWithPosition } from '@/object-record/utils/computeNewPositionOfDraggedRecord'; import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useRecoilCallback } from 'recoil'; import { findById, isDefined } from 'twenty-shared/utils'; @@ -15,8 +17,10 @@ import { sortByProperty } from '~/utils/array/sortByProperty'; // TODO: does not work when scrolling while dragging and does not work if not paired with a network refetch right after // But it's sufficient right now for the main use case export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => { - const recordIdByRealIndexCallbackFamilyState = - useRecoilComponentCallbackState(recordIdByRealIndexComponentFamilyState); + const recordIdByRealIndexCallbackSelector = + useRecoilComponentFamilyCallbackState( + recordIdByRealIndexComponentFamilySelector, + ); const lastScrollPositionCallbackState = useRecoilComponentCallbackState( lastScrollPositionComponentState, @@ -64,7 +68,7 @@ export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => { ) { const recordIdAtRealIndex = getSnapshotValue( snapshot, - recordIdByRealIndexCallbackFamilyState({ realIndex }), + recordIdByRealIndexCallbackSelector(realIndex), ); if (!isDefined(recordIdAtRealIndex)) { @@ -115,7 +119,7 @@ export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => { [ lastScrollPositionCallbackState, loadRecordsToVirtualRows, - recordIdByRealIndexCallbackFamilyState, + recordIdByRealIndexCallbackSelector, scrollWrapperHTMLElement?.clientHeight, totalNumberOfRecordsToVirtualizeCallbackState, ], diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts index 8b8aea5889..4949eb1e81 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts @@ -10,7 +10,8 @@ import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-tab import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; + +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; @@ -53,9 +54,9 @@ export const useSetRecordTableData = ({ recordTableId, ); - const recordIdByRealIndexCallbackState = + const recordIdByRealIndexCallbackSelector = useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, + recordIdByRealIndexComponentFamilySelector, recordTableId, ); @@ -135,11 +136,11 @@ export const useSetRecordTableData = ({ for (const [realIndex, recordId] of recordIds.entries()) { const currentRecordIdAtRealIndex = getSnapshotValue( snapshot, - recordIdByRealIndexCallbackState({ realIndex }), + recordIdByRealIndexCallbackSelector(realIndex), ); if (recordId !== currentRecordIdAtRealIndex) { - set(recordIdByRealIndexCallbackState({ realIndex }), recordId); + set(recordIdByRealIndexCallbackSelector(realIndex), recordId); } } } @@ -153,7 +154,7 @@ export const useSetRecordTableData = ({ unfocusRecordTableRow, setRecordTableHoverPosition, isRowSelectedFamilyState, - recordIdByRealIndexCallbackState, + recordIdByRealIndexCallbackSelector, isRecordTableInitialLoadingCallbackState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx index 2eff949f82..e823073745 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx @@ -25,7 +25,7 @@ import { RecordTableTr } from '@/object-record/record-table/record-table-row/com import { useIsTableRowSecondaryDragged } from '@/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged'; import { getRecordTableColumnFieldWidthClassName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthClassName'; import { getRecordTableColumnFieldWidthCSSVariableName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthCSSVariableName'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; @@ -148,8 +148,8 @@ export const RecordTableBodyVirtualizedDraggableClone = ({ const theme = useTheme(); const recordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex }, + recordIdByRealIndexComponentFamilySelector, + realIndex, ); const { lastColumnWidth } = useRecordTableLastColumnWidthToFill(); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx index 0ca227a617..08d4532ad3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx @@ -9,7 +9,8 @@ import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/ import { isRecordTableRowActiveComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowActiveComponentFamilyState'; import { isRecordTableRowFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableRowFocusActiveComponentState'; import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; + import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { forwardRef, type ReactNode } from 'react'; @@ -55,13 +56,13 @@ export const RecordTableTr = forwardRef( ); const nextRecordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex: focusIndex + 1 }, + recordIdByRealIndexComponentFamilySelector, + focusIndex + 1, ); const isNextRecordIdFirstOfGroup = useRecoilComponentFamilyValue( isRecordIdFirstOfGroupComponentFamilySelector, - { recordId: nextRecordId ?? '' }, + nextRecordId, ); const isFocused = useRecoilComponentFamilyValue( diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts index 96f1039942..733b23e6f3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts @@ -1,7 +1,8 @@ import { isRecordIdSecondaryDragMultipleComponentFamilyState } from '@/object-record/record-drag/states/isRecordIdSecondaryDragMultipleComponentFamilyState'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { type Nullable } from 'twenty-shared/types'; -export const useIsTableRowSecondaryDragged = (recordId: string | null) => { +export const useIsTableRowSecondaryDragged = (recordId: Nullable) => { const isSecondaryDragged = useRecoilComponentFamilyValue( isRecordIdSecondaryDragMultipleComponentFamilyState, { recordId: recordId ?? '' }, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts index f68b5ce80d..e6d68526b6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts @@ -3,9 +3,11 @@ import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; +import { type Nullable } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; export const isRecordIdFirstOfGroupComponentFamilySelector = - createComponentFamilySelector({ + createComponentFamilySelector>({ key: 'isRecordIdFirstOfGroupComponentFamilySelector', componentInstanceContext: ViewComponentInstanceContext, get: @@ -19,6 +21,10 @@ export const isRecordIdFirstOfGroupComponentFamilySelector = const hasRecordGroups = recordGroupIds.length > 0; + if (!isDefined(familyKey)) { + return false; + } + if (hasRecordGroups) { for (const recordGroupId of recordGroupIds) { const recordIdsForThisGroup = get( @@ -28,7 +34,7 @@ export const isRecordIdFirstOfGroupComponentFamilySelector = }), ); - if (recordIdsForThisGroup[0] === familyKey.recordId) { + if (recordIdsForThisGroup[0] === familyKey) { return true; } } @@ -39,7 +45,7 @@ export const isRecordIdFirstOfGroupComponentFamilySelector = }), ); - if (allRecordIds[0] === familyKey.recordId) { + if (allRecordIds[0] === familyKey) { return true; } } diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx index 1dd0bd1d1b..4ed35cdff2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx @@ -3,8 +3,9 @@ import { getLabelIdentifierFieldValue } from '@/object-metadata/utils/getLabelId import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { RECORD_TABLE_ROW_HEIGHT } from '@/object-record/record-table/constants/RecordTableRowHeight'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; import { realIndexByVirtualIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/realIndexByVirtualIndexComponentFamilyState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import styled from '@emotion/styled'; @@ -50,8 +51,13 @@ export const RecordTableRowVirtualizedDebugRowHelper = ({ ); const recordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex }, + recordIdByRealIndexComponentFamilySelector, + realIndex, + ); + + const dataLoadingStatus = useRecoilComponentFamilyValue( + dataLoadingStatusByRealIndexComponentFamilySelector, + realIndex, ); const pixelsFromTop = @@ -85,6 +91,9 @@ export const RecordTableRowVirtualizedDebugRowHelper = ({ id: {recordId} + + status :{isDefined(dataLoadingStatus) ? dataLoadingStatus : 'undefined'} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx index f568c2f0c0..e7a13c9094 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx @@ -9,7 +9,7 @@ import { RecordTableRowHotkeyEffect } from '@/object-record/record-table/record- import { isRecordTableRowFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableRowFocusActiveComponentState'; import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; import { RecordTableRowVirtualizedSkeleton } from '@/object-record/record-table/virtualization/components/RecordTableRowVirtualizedSkeleton'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; @@ -25,7 +25,7 @@ export const RecordTableRowVirtualizedFullData = ({ }: RecordTableRowVirtualizedFullDataProps) => { const isFocused = useRecoilComponentFamilyValue( isRecordTableRowFocusedComponentFamilyState, - realIndex ?? 0, + realIndex, ); const isRowFocusActive = useRecoilComponentValue( @@ -33,8 +33,8 @@ export const RecordTableRowVirtualizedFullData = ({ ); const recordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex }, + recordIdByRealIndexComponentFamilySelector, + realIndex, ); if (!isDefined(recordId)) { diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx index 5d18058e03..91e47be2c2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx @@ -1,7 +1,7 @@ import { RecordTableRowVirtualizedFullData } from '@/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData'; import { RecordTableRowVirtualizedSkeleton } from '@/object-record/record-table/virtualization/components/RecordTableRowVirtualizedSkeleton'; -import { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; @@ -13,11 +13,11 @@ export const RecordTableRowVirtualizedRouterLevel2 = ({ realIndex, }: RecordTableRowVirtualizedRouterLevel2Props) => { const dataLoadingStatus = useRecoilComponentFamilyValue( - dataLoadingStatusByRealIndexComponentFamilyState, - { realIndex }, + dataLoadingStatusByRealIndexComponentFamilySelector, + realIndex, ); - if (dataLoadingStatus === null) { + if (dataLoadingStatus !== 'loaded') { return ; } diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts index cb42ebc7d8..5cd27b00bb 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts @@ -3,22 +3,22 @@ import { useRecoilCallback } from 'recoil'; import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; -import { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; export const useLoadRecordsToVirtualRows = () => { - const recordIdByRealIndexCallbackState = + const recordIdByRealIndexCallbackSelector = useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, + recordIdByRealIndexComponentFamilySelector, ); - const dataLoadingStatusByRealIndexCallbackState = - useRecoilComponentCallbackState( - dataLoadingStatusByRealIndexComponentFamilyState, + const dataLoadingStatusByRealIndexCallbackSelector = + useRecoilComponentFamilyCallbackState( + dataLoadingStatusByRealIndexComponentFamilySelector, ); const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackState( @@ -52,15 +52,15 @@ export const useLoadRecordsToVirtualRows = () => { const currentRecordIdAtRealIndex = getSnapshotValue( snapshot, - recordIdByRealIndexCallbackState({ realIndex }), + recordIdByRealIndexCallbackSelector(realIndex), ); if (record.id !== currentRecordIdAtRealIndex) { - set(recordIdByRealIndexCallbackState({ realIndex }), record.id); + set(recordIdByRealIndexCallbackSelector(realIndex), record.id); } set( - dataLoadingStatusByRealIndexCallbackState({ realIndex }), + dataLoadingStatusByRealIndexCallbackSelector(realIndex), 'loaded', ); } @@ -85,8 +85,8 @@ export const useLoadRecordsToVirtualRows = () => { set(recordIndexAllRecordIdsSelector, newAllRecordIds); }, [ - recordIdByRealIndexCallbackState, - dataLoadingStatusByRealIndexCallbackState, + recordIdByRealIndexCallbackSelector, + dataLoadingStatusByRealIndexCallbackSelector, recordIndexAllRecordIdsSelector, isRowSelectedCallbackState, hasUserSelectedAllRowsCallbackState, diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts index e6ed969ac6..cd82e8012c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts @@ -4,11 +4,18 @@ import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/ho import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams'; import { useTriggerFetchPages } from '@/object-record/record-table/virtualization/hooks/useTriggerFetchPages'; +import { dataLoadingStatusByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState'; import { dataPagesLoadedComponentState } from '@/object-record/record-table/virtualization/states/dataPagesLoadedComponentState'; +import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; +import { recordIdByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState'; import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; +import { getVirtualizationOverscanWindow } from '@/object-record/record-table/virtualization/utils/getVirtualizationOverscanWindow'; +import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useCallback } from 'react'; import { useRecoilCallback } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; import { sleep } from '~/utils/sleep'; export const useResetVirtualizationBecauseDataChanged = ( @@ -18,6 +25,8 @@ export const useResetVirtualizationBecauseDataChanged = ( objectNameSingular, }); + const { scrollWrapperHTMLElement } = useScrollWrapperHTMLElement(); + const params = useFindManyRecordIndexTableParams(objectNameSingular); // TODO: we could optimize this by using an aggregate or using only id: true in recordGqlFields @@ -43,18 +52,101 @@ export const useResetVirtualizationBecauseDataChanged = ( const { triggerFetchPagesWithoutDebounce } = useTriggerFetchPages(); + const lastScrollPositionCallbackState = useRecoilComponentCallbackState( + lastScrollPositionComponentState, + ); + + const recordIdByRealIndexCallbackState = useRecoilComponentCallbackState( + recordIdByRealIndexComponentState, + ); + + const dataLoadingStatusByRealIndexCallbackState = + useRecoilComponentCallbackState(dataLoadingStatusByRealIndexComponentState); + const resetVirtualization = useRecoilCallback( - ({ set }) => + ({ set, snapshot }) => async () => { const { totalCount } = await findManyRecordsLazy(); + const tableScrollWrapperHeight = + scrollWrapperHTMLElement?.clientHeight ?? 0; + + const lastScrollPosition = getSnapshotValue( + snapshot, + lastScrollPositionCallbackState, + ); + + const totalNumberOfRecordsToVirtualize = + getSnapshotValue( + snapshot, + totalNumberOfRecordsToVirtualizeCallbackState, + ) ?? 0; + + const { + firstRealIndexInOverscanWindow, + lastRealIndexInOverscanWindow, + } = getVirtualizationOverscanWindow( + lastScrollPosition, + tableScrollWrapperHeight, + totalNumberOfRecordsToVirtualize, + ); + + const recordIdByRealIndex = getSnapshotValue( + snapshot, + recordIdByRealIndexCallbackState, + ); + + const dataLoadingStatusByRealIndex = getSnapshotValue( + snapshot, + dataLoadingStatusByRealIndexCallbackState, + ); + + const lengthOfOverscanWindow = + lastRealIndexInOverscanWindow - firstRealIndexInOverscanWindow + 1; + + const newRecordIdByRealIndex = new Map(); + const newDataLoadingStatusByRealIndex = new Map< + number, + 'loaded' | 'not-loaded' + >(); + + for (let i = 0; i < lengthOfOverscanWindow; i++) { + const realIndex = firstRealIndexInOverscanWindow + i; + + const existingRecordId = recordIdByRealIndex.get(realIndex); + + if (isDefined(existingRecordId)) { + newRecordIdByRealIndex.set(realIndex, existingRecordId); + } + + const existingDataLoadingStatus = + dataLoadingStatusByRealIndex.get(realIndex); + + if (isDefined(existingDataLoadingStatus)) { + newDataLoadingStatusByRealIndex.set( + realIndex, + existingDataLoadingStatus, + ); + } + } + + set(recordIdByRealIndexCallbackState, newRecordIdByRealIndex); + set( + dataLoadingStatusByRealIndexCallbackState, + newDataLoadingStatusByRealIndex, + ); + set(dataPagesLoadedCallbackState, []); set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount); }, [ - dataPagesLoadedCallbackState, - totalNumberOfRecordsToVirtualizeCallbackState, findManyRecordsLazy, + scrollWrapperHTMLElement?.clientHeight, + lastScrollPositionCallbackState, + totalNumberOfRecordsToVirtualizeCallbackState, + dataPagesLoadedCallbackState, + dataLoadingStatusByRealIndexCallbackState, + recordIdByRealIndexCallbackState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts index 9c3940fd2b..69dd20cc78 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts @@ -14,12 +14,12 @@ import { useReapplyRowSelection } from '@/object-record/record-table/virtualizat import { useResetTableFocuses } from '@/object-record/record-table/virtualization/hooks/useResetTableFocuses'; import { useResetVirtualizedRowTreadmill } from '@/object-record/record-table/virtualization/hooks/useResetVirtualizedRowTreadmill'; -import { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; import { dataPagesLoadedComponentState } from '@/object-record/record-table/virtualization/states/dataPagesLoadedComponentState'; import { isInitializingVirtualTableDataLoadingComponentState } from '@/object-record/record-table/virtualization/states/isInitializingVirtualTableDataLoadingComponentState'; import { lastRealIndexSetComponentState } from '@/object-record/record-table/virtualization/states/lastRealIndexSetComponentState'; import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { scrollAtRealIndexComponentState } from '@/object-record/record-table/virtualization/states/scrollAtRealIndexComponentState'; import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -56,14 +56,14 @@ export const useTriggerInitialRecordTableDataLoad = () => { recordIndexAllRecordIdsComponentSelector, ); - const recordIdByRealIndexCallbackState = + const recordIdByRealIndexCallbackSelector = useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, + recordIdByRealIndexComponentFamilySelector, ); - const dataLoadingStatusByRealIndexCallbackState = - useRecoilComponentCallbackState( - dataLoadingStatusByRealIndexComponentFamilyState, + const dataLoadingStatusByRealIndexCallbackSelector = + useRecoilComponentFamilyCallbackState( + dataLoadingStatusByRealIndexComponentFamilySelector, ); const setIsRecordTableScrolledHorizontally = useSetRecoilComponentState( @@ -144,20 +144,12 @@ export const useTriggerInitialRecordTableDataLoad = () => { records = SIGN_IN_BACKGROUND_MOCK_COMPANIES; totalCount = SIGN_IN_BACKGROUND_MOCK_COMPANIES.length; } else { - for (const [index] of currentRecordIds.entries()) { + for (const [realIndex] of currentRecordIds.entries()) { set( - dataLoadingStatusByRealIndexCallbackState({ - realIndex: index, - }), - null, - ); - - set( - recordIdByRealIndexCallbackState({ - realIndex: index, - }), - null, + dataLoadingStatusByRealIndexCallbackSelector(realIndex), + 'not-loaded', ); + set(recordIdByRealIndexCallbackSelector(realIndex), undefined); } const { records: findManyRecords, totalCount: findManyTotalCount } = @@ -215,8 +207,8 @@ export const useTriggerInitialRecordTableDataLoad = () => { setIsRecordTableScrolledVertically, scrollTableToPosition, findManyRecordsLazy, - dataLoadingStatusByRealIndexCallbackState, - recordIdByRealIndexCallbackState, + dataLoadingStatusByRealIndexCallbackSelector, + recordIdByRealIndexCallbackSelector, totalNumberOfRecordsToVirtualizeCallbackState, upsertRecordsInStore, loadRecordsToVirtualRows, diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts new file mode 100644 index 0000000000..a230db5338 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts @@ -0,0 +1,67 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { dataLoadingStatusByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState'; +import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; +import { type DefaultValue } from 'recoil'; +import { type Nullable } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +export const dataLoadingStatusByRealIndexComponentFamilySelector = + createComponentFamilySelector< + 'loaded' | 'not-loaded' | undefined, + Nullable + >({ + key: 'dataLoadingStatusByRealIndexComponentFamilySelector', + componentInstanceContext: RecordTableComponentInstanceContext, + get: + ({ instanceId, familyKey }) => + ({ get }) => { + const realIndex = familyKey; + + const dataLoadingStatusByRealIndex = get( + dataLoadingStatusByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + if (!isDefined(realIndex)) { + return undefined; + } + + const dataLoadingStatus = dataLoadingStatusByRealIndex.get(realIndex); + + return dataLoadingStatus; + }, + set: + ({ familyKey, instanceId }) => + ( + { get, set }, + newValue: 'loaded' | 'not-loaded' | DefaultValue | undefined, + ) => { + if (!isDefined(familyKey)) { + return; + } + + const actualDataLoadingStatusByRealIndex = get( + dataLoadingStatusByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + const newDataLoadingStatusByRealIndex = new Map( + actualDataLoadingStatusByRealIndex, + ); + + if (newValue === 'loaded') { + newDataLoadingStatusByRealIndex.set(familyKey, 'loaded'); + } else { + newDataLoadingStatusByRealIndex.set(familyKey, 'not-loaded'); + } + + set( + dataLoadingStatusByRealIndexComponentState.atomFamily({ + instanceId, + }), + newDataLoadingStatusByRealIndex, + ); + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts deleted file mode 100644 index d080af94f8..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; - -export const dataLoadingStatusByRealIndexComponentFamilyState = - createComponentFamilyState<'loaded' | null, { realIndex: number | null }>({ - key: 'dataLoadingStatusByRealIndexComponentFamilyState', - componentInstanceContext: RecordTableComponentInstanceContext, - defaultValue: null, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts new file mode 100644 index 0000000000..680117d409 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts @@ -0,0 +1,10 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const dataLoadingStatusByRealIndexComponentState = createComponentState< + Map +>({ + key: 'dataLoadingStatusByRealIndexComponentState', + componentInstanceContext: RecordTableComponentInstanceContext, + defaultValue: new Map(), +}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts deleted file mode 100644 index f11308bf7f..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; -import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; -import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector'; -import { isDefined } from 'twenty-shared/utils'; - -export const realIndexByRecordIdComponentSelector = createComponentSelector<{ - realIndexByRecordIdMap: Map; -}>({ - key: 'realIndexByRecordIdComponentSelector', - componentInstanceContext: RecordTableComponentInstanceContext, - get: - ({ instanceId }) => - ({ get }) => { - const totalNumberOfRecordsToVirtualize = get( - totalNumberOfRecordsToVirtualizeComponentState.atomFamily({ - instanceId, - }), - ); - - const realIndexByRecordIdMap = new Map(); - - if (!isDefined(totalNumberOfRecordsToVirtualize)) { - return { realIndexByRecordIdMap }; - } - - for ( - let realIndex = 0; - realIndex < totalNumberOfRecordsToVirtualize; - realIndex++ - ) { - const recordId = get( - recordIdByRealIndexComponentFamilyState.atomFamily({ - instanceId, - familyKey: { realIndex }, - }), - ); - - if (isDefined(recordId)) { - realIndexByRecordIdMap.set(recordId, realIndex); - } - } - - return { realIndexByRecordIdMap }; - }, -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts new file mode 100644 index 0000000000..f48c5b3fe4 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts @@ -0,0 +1,58 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { recordIdByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState'; +import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; +import { isNonEmptyString } from '@sniptt/guards'; +import { type DefaultValue } from 'recoil'; +import { type Nullable } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +export const recordIdByRealIndexComponentFamilySelector = + createComponentFamilySelector, Nullable>({ + key: 'recordIdByRealIndexComponentFamilySelector', + componentInstanceContext: RecordTableComponentInstanceContext, + get: + ({ instanceId, familyKey }) => + ({ get }) => { + const realIndex = familyKey; + + const recordIdByRealIndex = get( + recordIdByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + if (!isDefined(realIndex)) { + return null; + } + + const recordId = recordIdByRealIndex.get(realIndex); + + return recordId; + }, + set: + ({ familyKey, instanceId }) => + ({ get, set }, newValue: Nullable | DefaultValue) => { + if (!isDefined(familyKey)) { + return; + } + + const actualRecordIdByRealIndex = get( + recordIdByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + const newRecordIdByRealIndex = new Map(actualRecordIdByRealIndex); + + if (isNonEmptyString(newValue)) { + newRecordIdByRealIndex.set(familyKey, newValue); + } else { + newRecordIdByRealIndex.delete(familyKey); + } + + set( + recordIdByRealIndexComponentState.atomFamily({ instanceId }), + newRecordIdByRealIndex, + ); + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts deleted file mode 100644 index fab769404d..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; - -export const recordIdByRealIndexComponentFamilyState = - createComponentFamilyState({ - key: 'recordIdByRealIndexComponentFamilyState', - componentInstanceContext: RecordTableComponentInstanceContext, - defaultValue: null, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts new file mode 100644 index 0000000000..ad0de49189 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts @@ -0,0 +1,10 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const recordIdByRealIndexComponentState = createComponentState< + Map +>({ + key: 'recordIdByRealIndexComponentState', + componentInstanceContext: RecordTableComponentInstanceContext, + defaultValue: new Map(), +});