Jotai 13 (#18178)
## Recoil to Jotai Migration — PR 13: Workflow, Page Layout, AI, Activities, Settings, SSE & Remaining Modules ### Summary This is the 13th PR in the [14-PR migration series](packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md) to replace Recoil with Jotai as the state management library in `twenty-front`. This PR migrates the remaining consumer code across all modules — updating ~1,076 files with ~10k insertions/deletions. ### What changed **New Jotai infrastructure (13 new files):** - `createComponentSelectorV2` and `createComponentFamilySelectorV2` — instance-scoped derived atoms, replacing Recoil's component selectors - 6 new hooks: `useRecoilComponentSelectorValueV2`, `useRecoilComponentFamilySelectorValueV2`, `useRecoilComponentSelectorCallbackStateV2`, `useRecoilComponentFamilySelectorCallbackStateV2`, `useRecoilComponentStateCallbackStateV2`, `useRecoilComponentFamilyStateCallbackStateV2` - New types: `ComponentSelectorV2`, `ComponentFamilySelectorV2`, `SelectorCallbacksV2` - Extended `buildGetHelper` to support the new selector patterns **State definition migrations:** - `createState()` → `createStateV2()` (Jotai `atom()`) - `createFamilyState()` → `createFamilyStateV2()` (Jotai `atom()` with family cache) - Recoil `selector`/`selectorFamily` → Jotai derived `atom()` via V2 utilities **Hook migrations (~2,400 removed, ~1,545 added V2 equivalents):** - `useRecoilValue` → `useRecoilValueV2` - `useRecoilState` → `useRecoilStateV2` - `useSetRecoilState` → `useSetRecoilStateV2` - `useRecoilCallback` → `useCallback` + `jotaiStore.get/set` - `useRecoilComponentValue` → `useRecoilComponentValueV2` - `useSetRecoilComponentState` → `useSetRecoilComponentStateV2` - `useRecoilComponentFamilyValue` → `useRecoilComponentFamilyValueV2` / `useFamilySelectorValueV2` - ~397 direct `import from 'recoil'` removed **Deleted files (9 files):** - `dateLocaleState.ts` — consolidated - `currentAIChatThreadTitleStateV2.ts` — renamed to replace the original - `isCommandMenuOpenedState.ts` — removed - `filesFieldUploadState.ts` — replaced by V2 - `recordStoreFamilySelector.ts`, `recordStoreFieldValueSelector.ts`, `recordStoreRecordsSelector.ts` — replaced by V2 equivalents - `settingsAllRolesSelector.ts` — replaced by `useSettingsAllRoles` hook - `settingsRoleIdsStateV2.ts` — consolidated **Modules migrated:** - Workflow (diagram, steps, variables, filters) - Page Layout (widgets, fields, graph, tabs) - AI (chat, agents, browsing context) - Activities (rich text editor, targets, timeline) - Action Menu (single/multiple record actions) - Command Menu (navigation, history, pages, workflow) - Context Store - Record Board, Record Table, Record Calendar, Record Index - Record Field, Record Filter, Record Sort, Record Group - Record Drag, Record Picker, Record Store - Views (view bar, view picker, filters, sorts) - Settings (roles, permissions, SSO, security, data model, developers, domains) - SSE (event streams, subscriptions) - Spreadsheet Import - Auth, Favorites, Front Components, Information Banner
This commit is contained in:
+106
-107
@@ -8,8 +8,7 @@ import {
|
||||
import { type Unmasked } from '@apollo/client/masking';
|
||||
import { isNonEmptyArray } from '@apollo/client/utilities';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
@@ -30,6 +29,9 @@ import {
|
||||
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
|
||||
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useSetFamilyRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetFamilyRecoilStateV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export type UseFindManyRecordsParams<T> = ObjectMetadataItemIdentifier &
|
||||
@@ -86,10 +88,14 @@ export const useFetchMoreRecordsWithPagination = <
|
||||
orderBy,
|
||||
});
|
||||
|
||||
const [hasNextPage] = useRecoilState(hasNextPageFamilyState(queryIdentifier));
|
||||
const hasNextPage = useFamilyRecoilValueV2(
|
||||
hasNextPageFamilyState,
|
||||
queryIdentifier,
|
||||
);
|
||||
|
||||
const setIsFetchingMoreObjects = useSetRecoilState(
|
||||
isFetchingMoreRecordsFamilyState(queryIdentifier),
|
||||
const setIsFetchingMoreObjects = useSetFamilyRecoilStateV2(
|
||||
isFetchingMoreRecordsFamilyState,
|
||||
queryIdentifier,
|
||||
);
|
||||
|
||||
const { handleFindManyRecordsError } = useHandleFindManyRecordsError({
|
||||
@@ -98,115 +104,108 @@ export const useFetchMoreRecordsWithPagination = <
|
||||
|
||||
// TODO: put this into a util inspired from https://github.com/apollographql/apollo-client/blob/master/src/utilities/policies/pagination.ts
|
||||
// This function is equivalent to merge function + read function in field policy
|
||||
const fetchMoreRecords = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async () => {
|
||||
const hasNextPageLocal = snapshot
|
||||
.getLoadable(hasNextPageFamilyState(queryIdentifier))
|
||||
.getValue();
|
||||
const fetchMoreRecords = useCallback(async () => {
|
||||
const hasNextPageLocal = jotaiStore.get(
|
||||
hasNextPageFamilyState.atomFamily(queryIdentifier),
|
||||
);
|
||||
|
||||
const lastCursorLocal = snapshot
|
||||
.getLoadable(cursorFamilyState(queryIdentifier))
|
||||
.getValue();
|
||||
const lastCursorLocal = jotaiStore.get(
|
||||
cursorFamilyState.atomFamily(queryIdentifier),
|
||||
);
|
||||
|
||||
// Remote objects does not support hasNextPage. We cannot rely on it to fetch more records.
|
||||
if (
|
||||
hasNextPageLocal ||
|
||||
(!isAggregationEnabled(objectMetadataItem) && !error)
|
||||
) {
|
||||
setIsFetchingMoreObjects(true);
|
||||
// Remote objects does not support hasNextPage. We cannot rely on it to fetch more records.
|
||||
if (
|
||||
hasNextPageLocal ||
|
||||
(!isAggregationEnabled(objectMetadataItem) && !error)
|
||||
) {
|
||||
setIsFetchingMoreObjects(true);
|
||||
|
||||
try {
|
||||
const { data: fetchMoreDataResult } = await fetchMore({
|
||||
variables: {
|
||||
filter,
|
||||
orderBy,
|
||||
lastCursor: isNonEmptyString(lastCursorLocal)
|
||||
? lastCursorLocal
|
||||
: undefined,
|
||||
try {
|
||||
const { data: fetchMoreDataResult } = await fetchMore({
|
||||
variables: {
|
||||
filter,
|
||||
orderBy,
|
||||
lastCursor: isNonEmptyString(lastCursorLocal)
|
||||
? lastCursorLocal
|
||||
: undefined,
|
||||
},
|
||||
updateQuery: (prev, { fetchMoreResult }) => {
|
||||
const previousEdges = prev?.[objectMetadataItem.namePlural]?.edges;
|
||||
const nextEdges =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
||||
let newEdges: RecordGqlEdge[] = previousEdges ?? [];
|
||||
|
||||
if (isNonEmptyArray(nextEdges)) {
|
||||
newEdges = filterUniqueRecordEdgesByCursor([
|
||||
...newEdges,
|
||||
...(fetchMoreResult?.[objectMetadataItem.namePlural]?.edges ??
|
||||
[]),
|
||||
]);
|
||||
}
|
||||
|
||||
const pageInfo =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;
|
||||
|
||||
if (isDefined(pageInfo)) {
|
||||
jotaiStore.set(
|
||||
cursorFamilyState.atomFamily(queryIdentifier),
|
||||
pageInfo.endCursor ?? '',
|
||||
);
|
||||
jotaiStore.set(
|
||||
hasNextPageFamilyState.atomFamily(queryIdentifier),
|
||||
pageInfo.hasNextPage ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges: newEdges,
|
||||
pageInfo,
|
||||
},
|
||||
updateQuery: (prev, { fetchMoreResult }) => {
|
||||
const previousEdges =
|
||||
prev?.[objectMetadataItem.namePlural]?.edges;
|
||||
const nextEdges =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
||||
let newEdges: RecordGqlEdge[] = previousEdges ?? [];
|
||||
}) as T[];
|
||||
|
||||
if (isNonEmptyArray(nextEdges)) {
|
||||
newEdges = filterUniqueRecordEdgesByCursor([
|
||||
...newEdges,
|
||||
...(fetchMoreResult?.[objectMetadataItem.namePlural]
|
||||
?.edges ?? []),
|
||||
]);
|
||||
}
|
||||
|
||||
const pageInfo =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;
|
||||
|
||||
if (isDefined(pageInfo)) {
|
||||
set(
|
||||
cursorFamilyState(queryIdentifier),
|
||||
pageInfo.endCursor ?? '',
|
||||
);
|
||||
set(
|
||||
hasNextPageFamilyState(queryIdentifier),
|
||||
pageInfo.hasNextPage ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges: newEdges,
|
||||
pageInfo,
|
||||
},
|
||||
}) as T[];
|
||||
|
||||
onCompleted?.(records, {
|
||||
pageInfo,
|
||||
totalCount:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]
|
||||
?.totalCount,
|
||||
});
|
||||
|
||||
return Object.assign({}, prev, {
|
||||
[objectMetadataItem.namePlural]: {
|
||||
__typename: `${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}Connection`,
|
||||
edges: newEdges,
|
||||
pageInfo:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
||||
totalCount:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]
|
||||
.totalCount,
|
||||
},
|
||||
} as RecordGqlOperationFindManyResult);
|
||||
},
|
||||
onCompleted?.(records, {
|
||||
pageInfo,
|
||||
totalCount:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.totalCount,
|
||||
});
|
||||
|
||||
return {
|
||||
data: fetchMoreDataResult?.[objectMetadataItem.namePlural],
|
||||
};
|
||||
} catch (error) {
|
||||
handleFindManyRecordsError(error as ApolloError);
|
||||
return { error: error as ApolloError };
|
||||
} finally {
|
||||
setIsFetchingMoreObjects(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
objectMetadataItem,
|
||||
error,
|
||||
setIsFetchingMoreObjects,
|
||||
fetchMore,
|
||||
filter,
|
||||
orderBy,
|
||||
onCompleted,
|
||||
handleFindManyRecordsError,
|
||||
queryIdentifier,
|
||||
],
|
||||
);
|
||||
return Object.assign({}, prev, {
|
||||
[objectMetadataItem.namePlural]: {
|
||||
__typename: `${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}Connection`,
|
||||
edges: newEdges,
|
||||
pageInfo:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
||||
totalCount:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].totalCount,
|
||||
},
|
||||
} as RecordGqlOperationFindManyResult);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: fetchMoreDataResult?.[objectMetadataItem.namePlural],
|
||||
};
|
||||
} catch (error) {
|
||||
handleFindManyRecordsError(error as ApolloError);
|
||||
return { error: error as ApolloError };
|
||||
} finally {
|
||||
setIsFetchingMoreObjects(false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
objectMetadataItem,
|
||||
error,
|
||||
setIsFetchingMoreObjects,
|
||||
fetchMore,
|
||||
filter,
|
||||
orderBy,
|
||||
onCompleted,
|
||||
handleFindManyRecordsError,
|
||||
queryIdentifier,
|
||||
]);
|
||||
|
||||
const totalCount = data?.[objectMetadataItem.namePlural]?.totalCount;
|
||||
|
||||
|
||||
+23
-20
@@ -4,7 +4,8 @@ import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/t
|
||||
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
|
||||
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
|
||||
import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useHandleFindManyRecordsCompleted = <T>({
|
||||
@@ -16,28 +17,30 @@ export const useHandleFindManyRecordsCompleted = <T>({
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
onCompleted?: OnFindManyRecordsCompleted<T>;
|
||||
}) => {
|
||||
const handleFindManyRecordsCompleted = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(data: RecordGqlOperationFindManyResult) => {
|
||||
const pageInfo = data?.[objectMetadataItem.namePlural]?.pageInfo;
|
||||
const handleFindManyRecordsCompleted = useCallback(
|
||||
(data: RecordGqlOperationFindManyResult) => {
|
||||
const pageInfo = data?.[objectMetadataItem.namePlural]?.pageInfo;
|
||||
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: data?.[objectMetadataItem.namePlural],
|
||||
}) as T[];
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: data?.[objectMetadataItem.namePlural],
|
||||
}) as T[];
|
||||
|
||||
onCompleted?.(records, {
|
||||
pageInfo,
|
||||
totalCount: data?.[objectMetadataItem.namePlural]?.totalCount,
|
||||
});
|
||||
onCompleted?.(records, {
|
||||
pageInfo,
|
||||
totalCount: data?.[objectMetadataItem.namePlural]?.totalCount,
|
||||
});
|
||||
|
||||
if (isDefined(data?.[objectMetadataItem.namePlural])) {
|
||||
set(cursorFamilyState(queryIdentifier), pageInfo.endCursor ?? '');
|
||||
set(
|
||||
hasNextPageFamilyState(queryIdentifier),
|
||||
pageInfo.hasNextPage ?? false,
|
||||
);
|
||||
}
|
||||
},
|
||||
if (isDefined(data?.[objectMetadataItem.namePlural])) {
|
||||
jotaiStore.set(
|
||||
cursorFamilyState.atomFamily(queryIdentifier),
|
||||
pageInfo.endCursor ?? '',
|
||||
);
|
||||
jotaiStore.set(
|
||||
hasNextPageFamilyState.atomFamily(queryIdentifier),
|
||||
pageInfo.hasNextPage ?? false,
|
||||
);
|
||||
}
|
||||
},
|
||||
[objectMetadataItem.namePlural, onCompleted, queryIdentifier],
|
||||
);
|
||||
|
||||
|
||||
+82
-84
@@ -8,7 +8,7 @@ import {
|
||||
import { type Unmasked } from '@apollo/client/masking';
|
||||
import { isNonEmptyArray } from '@apollo/client/utilities';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
|
||||
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
|
||||
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export type UseFindManyRecordsParams<T> = ObjectMetadataItemIdentifier &
|
||||
@@ -89,99 +90,96 @@ export const useLazyFetchMoreRecordsWithPagination = <
|
||||
|
||||
// TODO: put this into a util inspired from https://github.com/apollographql/apollo-client/blob/master/src/utilities/policies/pagination.ts
|
||||
// This function is equivalent to merge function + read function in field policy
|
||||
const fetchMoreRecordsLazy = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (limit = DEFAULT_SEARCH_REQUEST_LIMIT) => {
|
||||
const hasNextPageLocal = snapshot
|
||||
.getLoadable(hasNextPageFamilyState(queryIdentifier))
|
||||
.getValue();
|
||||
const fetchMoreRecordsLazy = useCallback(
|
||||
async (limit = DEFAULT_SEARCH_REQUEST_LIMIT) => {
|
||||
const hasNextPageLocal = jotaiStore.get(
|
||||
hasNextPageFamilyState.atomFamily(queryIdentifier),
|
||||
);
|
||||
|
||||
const lastCursorLocal = snapshot
|
||||
.getLoadable(cursorFamilyState(queryIdentifier))
|
||||
.getValue();
|
||||
const lastCursorLocal = jotaiStore.get(
|
||||
cursorFamilyState.atomFamily(queryIdentifier),
|
||||
);
|
||||
|
||||
// Remote objects does not support hasNextPage. We cannot rely on it to fetch more records.
|
||||
if (
|
||||
hasNextPageLocal ||
|
||||
(!isAggregationEnabled(objectMetadataItem) && !error)
|
||||
) {
|
||||
try {
|
||||
const { data: fetchMoreDataResult } = await fetchMore({
|
||||
variables: {
|
||||
limit,
|
||||
filter,
|
||||
orderBy,
|
||||
lastCursor: isNonEmptyString(lastCursorLocal)
|
||||
? lastCursorLocal
|
||||
: undefined,
|
||||
},
|
||||
updateQuery: (prev, { fetchMoreResult }) => {
|
||||
const previousEdges =
|
||||
prev?.[objectMetadataItem.namePlural]?.edges;
|
||||
const nextEdges =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
||||
// Remote objects does not support hasNextPage. We cannot rely on it to fetch more records.
|
||||
if (
|
||||
hasNextPageLocal ||
|
||||
(!isAggregationEnabled(objectMetadataItem) && !error)
|
||||
) {
|
||||
try {
|
||||
const { data: fetchMoreDataResult } = await fetchMore({
|
||||
variables: {
|
||||
limit,
|
||||
filter,
|
||||
orderBy,
|
||||
lastCursor: isNonEmptyString(lastCursorLocal)
|
||||
? lastCursorLocal
|
||||
: undefined,
|
||||
},
|
||||
updateQuery: (prev, { fetchMoreResult }) => {
|
||||
const previousEdges =
|
||||
prev?.[objectMetadataItem.namePlural]?.edges;
|
||||
const nextEdges =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
||||
|
||||
let newEdges: RecordGqlEdge[] = previousEdges ?? [];
|
||||
let newEdges: RecordGqlEdge[] = previousEdges ?? [];
|
||||
|
||||
if (isNonEmptyArray(nextEdges)) {
|
||||
newEdges = filterUniqueRecordEdgesByCursor([
|
||||
...newEdges,
|
||||
...(fetchMoreResult?.[objectMetadataItem.namePlural]
|
||||
?.edges ?? []),
|
||||
]);
|
||||
}
|
||||
if (isNonEmptyArray(nextEdges)) {
|
||||
newEdges = filterUniqueRecordEdgesByCursor([
|
||||
...newEdges,
|
||||
...(fetchMoreResult?.[objectMetadataItem.namePlural]?.edges ??
|
||||
[]),
|
||||
]);
|
||||
}
|
||||
|
||||
const pageInfo =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;
|
||||
const pageInfo =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;
|
||||
|
||||
if (isDefined(pageInfo)) {
|
||||
set(
|
||||
cursorFamilyState(queryIdentifier),
|
||||
pageInfo.endCursor ?? '',
|
||||
);
|
||||
set(
|
||||
hasNextPageFamilyState(queryIdentifier),
|
||||
pageInfo.hasNextPage ?? false,
|
||||
);
|
||||
}
|
||||
if (isDefined(pageInfo)) {
|
||||
jotaiStore.set(
|
||||
cursorFamilyState.atomFamily(queryIdentifier),
|
||||
pageInfo.endCursor ?? '',
|
||||
);
|
||||
jotaiStore.set(
|
||||
hasNextPageFamilyState.atomFamily(queryIdentifier),
|
||||
pageInfo.hasNextPage ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
return Object.assign({}, prev, {
|
||||
[objectMetadataItem.namePlural]: {
|
||||
__typename: `${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}Connection`,
|
||||
edges: newEdges,
|
||||
pageInfo:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
||||
totalCount:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]
|
||||
.totalCount,
|
||||
},
|
||||
} as RecordGqlOperationFindManyResult);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: fetchMoreDataResult?.[objectMetadataItem.namePlural],
|
||||
totalCount:
|
||||
fetchMoreDataResult?.[objectMetadataItem.namePlural]
|
||||
?.totalCount,
|
||||
records: getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges:
|
||||
fetchMoreDataResult?.[objectMetadataItem.namePlural]?.edges,
|
||||
return Object.assign({}, prev, {
|
||||
[objectMetadataItem.namePlural]: {
|
||||
__typename: `${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}Connection`,
|
||||
edges: newEdges,
|
||||
pageInfo:
|
||||
fetchMoreDataResult?.[objectMetadataItem.namePlural]
|
||||
?.pageInfo,
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
||||
totalCount:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].totalCount,
|
||||
},
|
||||
}) as T[],
|
||||
};
|
||||
} catch (error) {
|
||||
handleFindManyRecordsError(error as ApolloError);
|
||||
return { error: error as ApolloError };
|
||||
}
|
||||
} as RecordGqlOperationFindManyResult);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: fetchMoreDataResult?.[objectMetadataItem.namePlural],
|
||||
totalCount:
|
||||
fetchMoreDataResult?.[objectMetadataItem.namePlural]?.totalCount,
|
||||
records: getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges:
|
||||
fetchMoreDataResult?.[objectMetadataItem.namePlural]?.edges,
|
||||
pageInfo:
|
||||
fetchMoreDataResult?.[objectMetadataItem.namePlural]
|
||||
?.pageInfo,
|
||||
},
|
||||
}) as T[],
|
||||
};
|
||||
} catch (error) {
|
||||
handleFindManyRecordsError(error as ApolloError);
|
||||
return { error: error as ApolloError };
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
[
|
||||
queryIdentifier,
|
||||
objectMetadataItem,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useLazyQuery } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
@@ -13,6 +13,7 @@ import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPe
|
||||
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
|
||||
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier';
|
||||
import { QUERY_DEFAULT_LIMIT_RECORDS } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -80,70 +81,67 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const findManyRecordsLazy = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async () => {
|
||||
if (!hasReadPermission) {
|
||||
set(hasNextPageFamilyState(queryIdentifier), false);
|
||||
set(cursorFamilyState(queryIdentifier), '');
|
||||
const findManyRecordsLazy = useCallback(async () => {
|
||||
if (!hasReadPermission) {
|
||||
jotaiStore.set(hasNextPageFamilyState.atomFamily(queryIdentifier), false);
|
||||
jotaiStore.set(cursorFamilyState.atomFamily(queryIdentifier), '');
|
||||
|
||||
return {
|
||||
data: null,
|
||||
records: null,
|
||||
totalCount: 0,
|
||||
hasNextPage: false,
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
return {
|
||||
data: null,
|
||||
records: null,
|
||||
totalCount: 0,
|
||||
hasNextPage: false,
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const result = await findManyRecords();
|
||||
if (isDefined(result?.error)) {
|
||||
handleFindManyRecordsError(result.error);
|
||||
}
|
||||
const result = await findManyRecords();
|
||||
if (isDefined(result?.error)) {
|
||||
handleFindManyRecordsError(result.error);
|
||||
}
|
||||
|
||||
const hasNextPage =
|
||||
result?.data?.[objectMetadataItem.namePlural]?.pageInfo.hasNextPage ??
|
||||
false;
|
||||
const hasNextPage =
|
||||
result?.data?.[objectMetadataItem.namePlural]?.pageInfo.hasNextPage ??
|
||||
false;
|
||||
|
||||
const lastCursor =
|
||||
result?.data?.[objectMetadataItem.namePlural]?.pageInfo.endCursor ??
|
||||
'';
|
||||
const lastCursor =
|
||||
result?.data?.[objectMetadataItem.namePlural]?.pageInfo.endCursor ?? '';
|
||||
|
||||
set(hasNextPageFamilyState(queryIdentifier), hasNextPage);
|
||||
set(cursorFamilyState(queryIdentifier), lastCursor);
|
||||
jotaiStore.set(
|
||||
hasNextPageFamilyState.atomFamily(queryIdentifier),
|
||||
hasNextPage,
|
||||
);
|
||||
jotaiStore.set(cursorFamilyState.atomFamily(queryIdentifier), lastCursor);
|
||||
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges: result?.data?.[objectMetadataItem.namePlural]?.edges ?? [],
|
||||
pageInfo: result?.data?.[objectMetadataItem.namePlural]
|
||||
?.pageInfo ?? {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
endCursor: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const totalCount =
|
||||
result?.data?.[objectMetadataItem.namePlural]?.totalCount ?? 0;
|
||||
|
||||
return {
|
||||
data: result?.data,
|
||||
records,
|
||||
totalCount,
|
||||
hasNextPage,
|
||||
error: result?.error,
|
||||
};
|
||||
const records = getRecordsFromRecordConnection({
|
||||
recordConnection: {
|
||||
edges: result?.data?.[objectMetadataItem.namePlural]?.edges ?? [],
|
||||
pageInfo: result?.data?.[objectMetadataItem.namePlural]?.pageInfo ?? {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
endCursor: '',
|
||||
},
|
||||
},
|
||||
[
|
||||
hasReadPermission,
|
||||
findManyRecords,
|
||||
objectMetadataItem.namePlural,
|
||||
queryIdentifier,
|
||||
handleFindManyRecordsError,
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
const totalCount =
|
||||
result?.data?.[objectMetadataItem.namePlural]?.totalCount ?? 0;
|
||||
|
||||
return {
|
||||
data: result?.data,
|
||||
records,
|
||||
totalCount,
|
||||
hasNextPage,
|
||||
error: result?.error,
|
||||
};
|
||||
}, [
|
||||
hasReadPermission,
|
||||
findManyRecords,
|
||||
objectMetadataItem.namePlural,
|
||||
queryIdentifier,
|
||||
handleFindManyRecordsError,
|
||||
]);
|
||||
|
||||
return {
|
||||
findManyRecordsLazy,
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
import { useLazyQuery } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
@@ -60,8 +60,8 @@ export const useLazyFindManyRecordsWithOffset = ({
|
||||
},
|
||||
);
|
||||
|
||||
const findManyRecordsLazyWithOffset = useRecoilCallback(
|
||||
() => async (limit: number, offset: number) => {
|
||||
const findManyRecordsLazyWithOffset = useCallback(
|
||||
async (limit: number, offset: number) => {
|
||||
if (!hasReadPermission) {
|
||||
return {
|
||||
data: null,
|
||||
|
||||
Reference in New Issue
Block a user