From 56b0bbcad9392d4d7784638c7c1dce5241d5d71b Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Mon, 13 Oct 2025 15:30:50 +0200 Subject: [PATCH] Table virtualization fix (#15060) - Added readableFields for visible record field selector to take permission into account - Force refresh of table virtualization when updating view fields --- .../visibleRecordFieldsComponentSelector.ts | 9 +++++++- ...dTableVirtualizedInitialDataLoadEffect.tsx | 23 +++++++++++++++++++ ...alizedVisibleRecordFieldsComponentState.ts | 10 ++++++++ .../internal/usePersistViewFieldRecords.ts | 1 + 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/lastContextStoreVirtualizedVisibleRecordFieldsComponentState.ts diff --git a/packages/twenty-front/src/modules/object-record/record-field/states/visibleRecordFieldsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-field/states/visibleRecordFieldsComponentSelector.ts index ac646c7db4..66a70db8fc 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/states/visibleRecordFieldsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/states/visibleRecordFieldsComponentSelector.ts @@ -3,6 +3,7 @@ import { isActiveFieldMetadataItem } from '@/object-metadata/utils/isActiveField import { RecordFieldsComponentInstanceContext } from '@/object-record/record-field/states/context/RecordFieldsComponentInstanceContext'; import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState'; import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector'; +import { findById } from 'twenty-shared/utils'; import { sortByProperty } from '~/utils/array/sortByProperty'; export const visibleRecordFieldsComponentSelector = createComponentSelector({ @@ -47,10 +48,16 @@ export const visibleRecordFieldsComponentSelector = createComponentSelector({ return false; } - return isActiveFieldMetadataItem({ + const isActive = isActiveFieldMetadataItem({ objectNameSingular: objectMetadataItem.nameSingular, fieldMetadata: fieldMetadataItem, }); + + const isReadable = objectMetadataItem.readableFields.some( + findById(fieldMetadataItem.id), + ); + + return isReadable && isActive; }, ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableVirtualizedInitialDataLoadEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableVirtualizedInitialDataLoadEffect.tsx index 3c5de00759..804a5ab749 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableVirtualizedInitialDataLoadEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableVirtualizedInitialDataLoadEffect.tsx @@ -3,12 +3,15 @@ import { useRecoilValue } from 'recoil'; import { useRecordIndexTableFetchMore } from '@/object-record/record-index/hooks/useRecordIndexTableFetchMore'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; import { useTriggerInitialRecordTableDataLoad } from '@/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad'; import { isInitializingVirtualTableDataLoadingComponentState } from '@/object-record/record-table/virtualization/states/isInitializingVirtualTableDataLoadingComponentState'; import { lastContextStoreVirtualizedViewIdComponentState } from '@/object-record/record-table/virtualization/states/lastContextStoreVirtualizedViewIdComponentState'; +import { lastContextStoreVirtualizedVisibleRecordFieldsComponentState } from '@/object-record/record-table/virtualization/states/lastContextStoreVirtualizedVisibleRecordFieldsComponentState'; import { lastRecordTableQueryIdentifierComponentState } from '@/object-record/record-table/virtualization/states/lastRecordTableQueryIdentifierComponentState'; import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useEffect } from 'react'; @@ -21,6 +24,9 @@ export const RecordTableVirtualizedInitialDataLoadEffect = () => { const [lastRecordTableQueryIdentifier, setLastRecordTableQueryIdentifier] = useRecoilComponentState(lastRecordTableQueryIdentifierComponentState); + const visibleRecordFields = useRecoilComponentValue( + visibleRecordFieldsComponentSelector, + ); const [isInitializingVirtualTableDataLoading] = useRecoilComponentState( isInitializingVirtualTableDataLoadingComponentState, ); @@ -37,6 +43,13 @@ export const RecordTableVirtualizedInitialDataLoadEffect = () => { setLastContextStoreVirtualizedViewId, ] = useRecoilComponentState(lastContextStoreVirtualizedViewIdComponentState); + const [ + lastContextStoreVisibleRecordFields, + setLastContextStoreVisibleRecordFields, + ] = useRecoilComponentState( + lastContextStoreVirtualizedVisibleRecordFieldsComponentState, + ); + const { currentView } = useGetCurrentViewOnly(); useEffect(() => { @@ -55,6 +68,13 @@ export const RecordTableVirtualizedInitialDataLoadEffect = () => { ) { setLastRecordTableQueryIdentifier(queryIdentifier); + await triggerInitialRecordTableDataLoad(); + } else if ( + JSON.stringify(lastContextStoreVisibleRecordFields) !== + JSON.stringify(visibleRecordFields) + ) { + setLastContextStoreVisibleRecordFields(visibleRecordFields); + await triggerInitialRecordTableDataLoad(); } })(); @@ -68,6 +88,9 @@ export const RecordTableVirtualizedInitialDataLoadEffect = () => { currentView, lastContextStoreVirtualizedViewId, setLastContextStoreVirtualizedViewId, + lastContextStoreVisibleRecordFields, + setLastContextStoreVisibleRecordFields, + visibleRecordFields, ]); return <>; diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/lastContextStoreVirtualizedVisibleRecordFieldsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/lastContextStoreVirtualizedVisibleRecordFieldsComponentState.ts new file mode 100644 index 0000000000..12951af4a3 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/lastContextStoreVirtualizedVisibleRecordFieldsComponentState.ts @@ -0,0 +1,10 @@ +import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; +import { type RecordField } from '@/object-record/record-field/types/RecordField'; +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const lastContextStoreVirtualizedVisibleRecordFieldsComponentState = + createComponentState({ + key: 'lastContextStoreVirtualizedVisibleRecordFieldsComponentState', + componentInstanceContext: ContextStoreComponentInstanceContext, + defaultValue: null, + }); diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFieldRecords.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFieldRecords.ts index c2c0f74939..2e2aa46c1e 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFieldRecords.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFieldRecords.ts @@ -16,6 +16,7 @@ import { export const usePersistViewFieldRecords = () => { const { triggerViewFieldOptimisticEffect } = useTriggerViewFieldOptimisticEffect(); + const [createCoreViewFieldMutation] = useCreateCoreViewFieldMutation(); const [updateCoreViewFieldMutation] = useUpdateCoreViewFieldMutation(); const [deleteCoreViewFieldMutation] = useDeleteCoreViewFieldMutation();