From 25a38dc6936b85a02d5c33af5c3e6fb9837e1ee8 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Tue, 11 Jun 2024 12:29:33 +0200 Subject: [PATCH] Added one request per column on board. (#5819) - Changed to one find many request per column on board. --- .../internal/useSetRecordBoardRecordIds.ts | 19 +---- .../internal/useSetRecordIdsForColumn.ts | 55 +++++++++++++ .../record-board/hooks/useRecordBoard.ts | 4 + .../RecordBoardColumnFetchMoreLoader.tsx | 6 +- .../RecordIndexBoardColumnLoaderEffect.tsx | 39 +++++++++ .../components/RecordIndexBoardDataLoader.tsx | 50 ++++++++++++ ...x => RecordIndexBoardDataLoaderEffect.tsx} | 64 +++++++-------- .../components/RecordIndexContainer.tsx | 10 ++- .../hooks/useLoadRecordIndexBoardColumn.ts | 81 +++++++++++++++++++ .../utils/sortRecordsByPosition.ts | 19 +++++ 10 files changed, 287 insertions(+), 60 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx rename packages/twenty-front/src/modules/object-record/record-index/components/{RecordIndexBoardContainerEffect.tsx => RecordIndexBoardDataLoaderEffect.tsx} (79%) create mode 100644 packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts create mode 100644 packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts index 8ead97030..e5b61e4e6 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts @@ -2,6 +2,7 @@ import { useRecoilCallback } from 'recoil'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { @@ -60,21 +61,3 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { setRecordIds, }; }; - -const sortRecordsByPosition = ( - record1: ObjectRecord, - record2: ObjectRecord, -) => { - if ( - typeof record1.position == 'number' && - typeof record2.position == 'number' - ) { - return record1.position - record2.position; - } else if (record1.position === 'first' || record2.position === 'last') { - return -1; - } else if (record2.position === 'first' || record1.position === 'last') { - return 1; - } else { - return 0; - } -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts new file mode 100644 index 000000000..25138622d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts @@ -0,0 +1,55 @@ +import { useRecoilCallback } from 'recoil'; + +import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; + +export const useSetRecordIdsForColumn = (recordBoardId?: string) => { + const { + scopeId, + recordIdsByColumnIdFamilyState, + columnsFamilySelector, + kanbanFieldMetadataNameState, + } = useRecordBoardStates(recordBoardId); + + const setRecordIdsForColumn = useRecoilCallback( + ({ set, snapshot }) => + (columnId: string, records: ObjectRecord[]) => { + const column = snapshot + .getLoadable(columnsFamilySelector(columnId)) + .getValue(); + + const existingColumnRecordIds = snapshot + .getLoadable(recordIdsByColumnIdFamilyState(columnId)) + .getValue(); + + const kanbanFieldMetadataName = snapshot + .getLoadable(kanbanFieldMetadataNameState) + .getValue(); + + if (!kanbanFieldMetadataName) { + return; + } + + const columnRecordIds = records + .filter((record) => record[kanbanFieldMetadataName] === column?.value) + .sort(sortRecordsByPosition) + .map((record) => record.id); + + if (!isDeeplyEqual(existingColumnRecordIds, columnRecordIds)) { + set(recordIdsByColumnIdFamilyState(columnId), columnRecordIds); + } + }, + [ + columnsFamilySelector, + recordIdsByColumnIdFamilyState, + kanbanFieldMetadataNameState, + ], + ); + + return { + scopeId, + setRecordIdsForColumn, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts index c8f57c6ab..b516e6dc7 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts @@ -3,6 +3,7 @@ import { useSetRecoilState } from 'recoil'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useSetRecordBoardColumns } from '@/object-record/record-board/hooks/internal/useSetRecordBoardColumns'; import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds'; +import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/internal/useSetRecordIdsForColumn'; export const useRecordBoard = (recordBoardId?: string) => { const { @@ -17,6 +18,8 @@ export const useRecordBoard = (recordBoardId?: string) => { const { setColumns } = useSetRecordBoardColumns(recordBoardId); const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId); + const { setRecordIdsForColumn } = useSetRecordIdsForColumn(recordBoardId); + const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState); const setObjectSingularName = useSetRecoilState(objectSingularNameState); const setKanbanFieldMetadataName = useSetRecoilState( @@ -33,5 +36,6 @@ export const useRecordBoard = (recordBoardId?: string) => { selectedRecordIdsSelector, isCompactModeActiveState, shouldFetchMoreSelector, + setRecordIdsForColumn, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx index 7dd71ba08..447eb6ae1 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx @@ -23,15 +23,15 @@ export const RecordBoardColumnFetchMoreLoader = () => { useRecordBoardStates(); const isFetchingRecord = useRecoilValue(isFetchingRecordState); - const shouldFetchMore = useSetRecoilState( + const setShouldFetchMore = useSetRecoilState( shouldFetchMoreInColumnFamilyState(columnDefinition.id), ); const { ref, inView } = useInView(); useEffect(() => { - shouldFetchMore(inView); - }, [shouldFetchMore, inView]); + setShouldFetchMore(inView); + }, [setShouldFetchMore, inView]); return (
diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx new file mode 100644 index 000000000..b97e6e3a6 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx @@ -0,0 +1,39 @@ +import { useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { useLoadRecordIndexBoardColumn } from '@/object-record/record-index/hooks/useLoadRecordIndexBoardColumn'; + +export const RecordIndexBoardColumnLoaderEffect = ({ + objectNameSingular, + boardFieldSelectValue, + boardFieldMetadataId, + recordBoardId, + columnId, +}: { + recordBoardId: string; + objectNameSingular: string; + boardFieldSelectValue: string; + boardFieldMetadataId: string | null; + columnId: string; +}) => { + const { shouldFetchMoreSelector } = useRecordBoard(recordBoardId); + + const shouldFetchMore = useRecoilValue(shouldFetchMoreSelector()); + + const { fetchMoreRecords, loading } = useLoadRecordIndexBoardColumn({ + objectNameSingular, + recordBoardId, + boardFieldMetadataId, + columnFieldSelectValue: boardFieldSelectValue, + columnId, + }); + + useEffect(() => { + if (!loading && shouldFetchMore) { + fetchMoreRecords?.(); + } + }, [fetchMoreRecords, loading, shouldFetchMore, boardFieldSelectValue]); + + return <>; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx new file mode 100644 index 000000000..d6ca8ddfd --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx @@ -0,0 +1,50 @@ +import { useRecoilValue } from 'recoil'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { RecordIndexBoardColumnLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect'; +import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState'; + +type RecordIndexBoardDataLoaderProps = { + objectNameSingular: string; + recordBoardId: string; +}; + +export const RecordIndexBoardDataLoader = ({ + objectNameSingular, + recordBoardId, +}: RecordIndexBoardDataLoaderProps) => { + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular, + }); + + const recordIndexKanbanFieldMetadataId = useRecoilValue( + recordIndexKanbanFieldMetadataIdState, + ); + + const recordIndexKanbanFieldMetadataItem = objectMetadataItem.fields.find( + (field) => field.id === recordIndexKanbanFieldMetadataId, + ); + + const possibleKanbanSelectFieldValues = + recordIndexKanbanFieldMetadataItem?.options ?? []; + + const { columnIdsState } = useRecordBoardStates(recordBoardId); + + // TODO: we should make sure there's no way to have a mismatch between columnIds and possibleKanbanSelectFieldValues order + const columnIds = useRecoilValue(columnIdsState); + + return ( + <> + {possibleKanbanSelectFieldValues.map((option, index) => ( + + ))} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx similarity index 79% rename from packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx rename to packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx index d2e0d279a..b7b31f73c 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx @@ -1,51 +1,62 @@ import { useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useRecordActionBar } from '@/object-record/record-action-bar/hooks/useRecordActionBar'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection'; -import { useLoadRecordIndexBoard } from '@/object-record/record-index/hooks/useLoadRecordIndexBoard'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; +import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState'; import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState'; import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { isDefined } from '~/utils/isDefined'; -type RecordIndexBoardContainerEffectProps = { +type RecordIndexBoardDataLoaderEffectProps = { objectNameSingular: string; recordBoardId: string; - viewBarId: string; }; -export const RecordIndexBoardContainerEffect = ({ +export const RecordIndexBoardDataLoaderEffect = ({ objectNameSingular, recordBoardId, - viewBarId, -}: RecordIndexBoardContainerEffectProps) => { +}: RecordIndexBoardDataLoaderEffectProps) => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); + const recordIndexFieldDefinitions = useRecoilValue( + recordIndexFieldDefinitionsState, + ); + + const recordIndexKanbanFieldMetadataId = useRecoilValue( + recordIndexKanbanFieldMetadataIdState, + ); + + const recordIndexIsCompactModeActive = useRecoilValue( + recordIndexIsCompactModeActiveState, + ); + + const { isCompactModeActiveState } = useRecordBoard(recordBoardId); + + const setIsCompactModeActive = useSetRecoilState(isCompactModeActiveState); + + useEffect(() => { + setIsCompactModeActive(recordIndexIsCompactModeActive); + }, [recordIndexIsCompactModeActive, setIsCompactModeActive]); + const { setColumns, setObjectSingularName, selectedRecordIdsSelector, setFieldDefinitions, - shouldFetchMoreSelector, setKanbanFieldMetadataName, } = useRecordBoard(recordBoardId); - const { fetchMoreRecords, loading } = useLoadRecordIndexBoard({ - objectNameSingular, - recordBoardId, - viewBarId, - }); - - const recordIndexKanbanFieldMetadataId = useRecoilValue( - recordIndexKanbanFieldMetadataIdState, - ); + useEffect(() => { + setFieldDefinitions(recordIndexFieldDefinitions); + }, [recordIndexFieldDefinitions, setFieldDefinitions]); const navigate = useNavigate(); @@ -53,21 +64,6 @@ export const RecordIndexBoardContainerEffect = ({ navigate(`/settings/objects/${objectMetadataItem.namePlural}`); }, [navigate, objectMetadataItem.namePlural]); - const columnDefinitions = - computeRecordBoardColumnDefinitionsFromObjectMetadata( - objectMetadataItem, - recordIndexKanbanFieldMetadataId ?? '', - navigateToSelectSettings, - ); - - const shouldFetchMore = useRecoilValue(shouldFetchMoreSelector()); - - useEffect(() => { - if (!loading && shouldFetchMore) { - fetchMoreRecords?.(); - } - }, [columnDefinitions, fetchMoreRecords, loading, shouldFetchMore]); - const { resetRecordSelection } = useRecordBoardSelection(recordBoardId); useEffect(() => { @@ -90,10 +86,6 @@ export const RecordIndexBoardContainerEffect = ({ setColumns, ]); - const recordIndexFieldDefinitions = useRecoilValue( - recordIndexFieldDefinitionsState, - ); - useEffect(() => { setFieldDefinitions(recordIndexFieldDefinitions); }, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]); diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx index 3deaaa9af..e74b9e9be 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx @@ -5,7 +5,8 @@ import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/u import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { RecordIndexBoardContainer } from '@/object-record/record-index/components/RecordIndexBoardContainer'; -import { RecordIndexBoardContainerEffect } from '@/object-record/record-index/components/RecordIndexBoardContainerEffect'; +import { RecordIndexBoardDataLoader } from '@/object-record/record-index/components/RecordIndexBoardDataLoader'; +import { RecordIndexBoardDataLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardDataLoaderEffect'; import { RecordIndexTableContainer } from '@/object-record/record-index/components/RecordIndexTableContainer'; import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect'; import { RecordIndexViewBarEffect } from '@/object-record/record-index/components/RecordIndexViewBarEffect'; @@ -170,10 +171,13 @@ export const RecordIndexContainer = ({ objectNameSingular={objectNameSingular} createRecord={createRecord} /> - + )} diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts new file mode 100644 index 000000000..f15f04fab --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts @@ -0,0 +1,81 @@ +import { useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; +import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; +import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; +import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; +import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; +import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; +import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore'; + +type UseLoadRecordIndexBoardProps = { + objectNameSingular: string; + boardFieldMetadataId: string | null; + recordBoardId: string; + columnFieldSelectValue: string; + columnId: string; +}; + +export const useLoadRecordIndexBoardColumn = ({ + objectNameSingular, + boardFieldMetadataId, + recordBoardId, + columnFieldSelectValue, + columnId, +}: UseLoadRecordIndexBoardProps) => { + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular, + }); + const { setRecordIdsForColumn } = useRecordBoard(recordBoardId); + const { setRecords: setRecordsInStore } = useSetRecordInStore(); + + const recordIndexFilters = useRecoilValue(recordIndexFiltersState); + const recordIndexSorts = useRecoilValue(recordIndexSortsState); + const requestFilters = turnObjectDropdownFilterIntoQueryFilter( + recordIndexFilters, + objectMetadataItem?.fields ?? [], + ); + const orderBy = turnSortsIntoOrderBy(objectMetadataItem, recordIndexSorts); + + const recordGqlFields = useRecordBoardRecordGqlFields({ + objectMetadataItem, + recordBoardId, + }); + + const recordIndexKanbanFieldMetadataItem = objectMetadataItem.fields.find( + (field) => field.id === boardFieldMetadataId, + ); + + const filter = { + ...requestFilters, + [recordIndexKanbanFieldMetadataItem?.name ?? '']: { + in: [columnFieldSelectValue], + }, + }; + + const { records, loading, fetchMoreRecords, queryStateIdentifier } = + useFindManyRecords({ + objectNameSingular, + filter, + orderBy, + recordGqlFields, + }); + + useEffect(() => { + setRecordIdsForColumn(columnId, records); + }, [records, setRecordIdsForColumn, columnId]); + + useEffect(() => { + setRecordsInStore(records); + }, [records, setRecordsInStore]); + + return { + records, + loading, + fetchMoreRecords, + queryStateIdentifier, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts b/packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts new file mode 100644 index 000000000..e6ab0e63c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts @@ -0,0 +1,19 @@ +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; + +export const sortRecordsByPosition = ( + record1: ObjectRecord, + record2: ObjectRecord, +) => { + if ( + typeof record1.position == 'number' && + typeof record2.position == 'number' + ) { + return record1.position - record2.position; + } else if (record1.position === 'first' || record2.position === 'last') { + return -1; + } else if (record2.position === 'first' || record1.position === 'last') { + return 1; + } else { + return 0; + } +};