Fix fetch more notes (#16442)
fixes : https://github.com/twentyhq/twenty/issues/16320 --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+4
-11
@@ -15,7 +15,7 @@ jest.mock('@/object-record/hooks/useFindManyRecords', () => ({
|
||||
}));
|
||||
|
||||
const mockActivityTarget = {
|
||||
__typename: 'ActivityTarget',
|
||||
__typename: 'TaskTarget',
|
||||
updatedAt: '2021-08-03T19:20:06.000Z',
|
||||
createdAt: '2021-08-03T19:20:06.000Z',
|
||||
personId: '1',
|
||||
@@ -55,18 +55,11 @@ describe('useActivities', () => {
|
||||
);
|
||||
useActivityTargetsForTargetableObjectsMock.useActivityTargetsForTargetableObjects.mockReturnValue(
|
||||
{
|
||||
activityTargets: [mockActivityTarget],
|
||||
activityTargets: [{ ...mockActivityTarget, task: mockActivity }],
|
||||
loadingActivityTargets: false,
|
||||
},
|
||||
);
|
||||
|
||||
const useFindManyRecordsMock = jest.requireMock(
|
||||
'@/object-record/hooks/useFindManyRecords',
|
||||
);
|
||||
useFindManyRecordsMock.useFindManyRecords.mockReturnValue({
|
||||
records: [mockActivity],
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const activities = useActivities({
|
||||
@@ -74,9 +67,9 @@ describe('useActivities', () => {
|
||||
targetableObjects: [
|
||||
{ targetObjectNameSingular: 'company', id: '123' },
|
||||
],
|
||||
activitiesFilters: {},
|
||||
activitiesOrderByVariables: [{}],
|
||||
skip: false,
|
||||
limit: 10,
|
||||
activityTargetsOrderByVariables: [{}],
|
||||
});
|
||||
return activities;
|
||||
},
|
||||
|
||||
@@ -1,92 +1,84 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { findActivitiesOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory';
|
||||
import { useActivityTargetsForTargetableObjects } from '@/activities/hooks/useActivityTargetsForTargetableObjects';
|
||||
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { type Note } from '@/activities/types/Note';
|
||||
import { type NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { type Task } from '@/activities/types/Task';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { type RecordGqlOperationFilter } from 'twenty-shared/types';
|
||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useActivities = <T extends Task | Note>({
|
||||
objectNameSingular,
|
||||
targetableObjects,
|
||||
activitiesFilters,
|
||||
activitiesOrderByVariables,
|
||||
activityTargetsOrderByVariables,
|
||||
skip,
|
||||
limit,
|
||||
}: {
|
||||
objectNameSingular: CoreObjectNameSingular;
|
||||
objectNameSingular: CoreObjectNameSingular.Note | CoreObjectNameSingular.Task;
|
||||
targetableObjects: ActivityTargetableObject[];
|
||||
activitiesFilters: RecordGqlOperationFilter;
|
||||
activitiesOrderByVariables: RecordGqlOperationOrderBy;
|
||||
activityTargetsOrderByVariables: RecordGqlOperationOrderBy;
|
||||
skip?: boolean;
|
||||
limit: number;
|
||||
}) => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const updateActivitiesInStore = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(activityTargets: (TaskTarget | NoteTarget)[]) => {
|
||||
for (const activityTarget of activityTargets) {
|
||||
const activity = activityTarget[objectNameSingular];
|
||||
set(recordStoreFamilyState(activity.id), activity);
|
||||
}
|
||||
},
|
||||
[objectNameSingular],
|
||||
);
|
||||
|
||||
const { activityTargets, loadingActivityTargets } =
|
||||
useActivityTargetsForTargetableObjects({
|
||||
objectNameSingular,
|
||||
targetableObjects,
|
||||
skip: skip,
|
||||
const {
|
||||
activityTargets,
|
||||
loadingActivityTargets,
|
||||
totalCountActivityTargets,
|
||||
fetchMoreActivityTargets,
|
||||
hasNextPage,
|
||||
} = useActivityTargetsForTargetableObjects({
|
||||
objectNameSingular,
|
||||
targetableObjects,
|
||||
skip: skip,
|
||||
activityTargetsOrderByVariables,
|
||||
onCompleted: updateActivitiesInStore,
|
||||
limit,
|
||||
});
|
||||
|
||||
const activities = activityTargets.map((activityTarget) => {
|
||||
return activityTarget[objectNameSingular];
|
||||
}) as T[];
|
||||
|
||||
const fetchMoreActivities = async () => {
|
||||
const result = await fetchMoreActivityTargets();
|
||||
|
||||
if (!isDefined(result?.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const activityTargets = getRecordsFromRecordConnection<
|
||||
TaskTarget | NoteTarget
|
||||
>({
|
||||
recordConnection: result.data,
|
||||
});
|
||||
|
||||
const activityIds = [
|
||||
...new Set(
|
||||
activityTargets
|
||||
? [
|
||||
...activityTargets
|
||||
.map(
|
||||
(activityTarget) =>
|
||||
activityTarget.taskId ?? activityTarget.noteId,
|
||||
)
|
||||
.filter(isNonEmptyString),
|
||||
].sort(sortByAscString)
|
||||
: [],
|
||||
),
|
||||
];
|
||||
updateActivitiesInStore(activityTargets);
|
||||
|
||||
const skipBecauseNoActivityTargetFound = activityIds.length === 0;
|
||||
|
||||
const filter: RecordGqlOperationFilter = {
|
||||
id: {
|
||||
in: activityIds,
|
||||
},
|
||||
...activitiesFilters,
|
||||
return activityTargets.map((activityTarget) => {
|
||||
return activityTarget[objectNameSingular];
|
||||
}) as T[];
|
||||
};
|
||||
|
||||
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
|
||||
findActivitiesOperationSignatureFactory({
|
||||
objectMetadataItems,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { records: activities, loading: loadingActivities } =
|
||||
useFindManyRecords<Task | Note>({
|
||||
skip: skip || loadingActivityTargets || skipBecauseNoActivityTargetFound,
|
||||
objectNameSingular:
|
||||
FIND_ACTIVITIES_OPERATION_SIGNATURE.objectNameSingular,
|
||||
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
|
||||
filter,
|
||||
orderBy: activitiesOrderByVariables,
|
||||
onCompleted: useRecoilCallback(
|
||||
({ set }) =>
|
||||
(activities) => {
|
||||
for (const activity of activities) {
|
||||
set(recordStoreFamilyState(activity.id), activity);
|
||||
}
|
||||
},
|
||||
[],
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
activities: activities as T[],
|
||||
loading: loadingActivities || loadingActivityTargets,
|
||||
loading: loadingActivityTargets,
|
||||
totalCountActivities: totalCountActivityTargets,
|
||||
fetchMoreActivities,
|
||||
hasNextPage,
|
||||
};
|
||||
};
|
||||
|
||||
+25
-10
@@ -7,6 +7,7 @@ import { type TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getActivityTargetsFilter } from '@/activities/utils/getActivityTargetsFilter';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
|
||||
export const useActivityTargetsForTargetableObjects = ({
|
||||
@@ -14,14 +15,18 @@ export const useActivityTargetsForTargetableObjects = ({
|
||||
targetableObjects,
|
||||
skip,
|
||||
onCompleted,
|
||||
activityTargetsOrderByVariables,
|
||||
limit,
|
||||
}: {
|
||||
objectNameSingular: CoreObjectNameSingular;
|
||||
objectNameSingular: CoreObjectNameSingular.Note | CoreObjectNameSingular.Task;
|
||||
targetableObjects: Pick<
|
||||
ActivityTargetableObject,
|
||||
'id' | 'targetObjectNameSingular'
|
||||
>[];
|
||||
skip?: boolean;
|
||||
onCompleted?: (activityTargets: (TaskTarget | NoteTarget)[]) => void;
|
||||
activityTargetsOrderByVariables: RecordGqlOperationOrderBy;
|
||||
limit: number;
|
||||
}) => {
|
||||
const activityTargetsFilter = getActivityTargetsFilter({
|
||||
targetableObjects: targetableObjects,
|
||||
@@ -38,18 +43,28 @@ export const useActivityTargetsForTargetableObjects = ({
|
||||
// TODO: We want to optimistically remove from this request
|
||||
// If we are on a show page and we remove the current show page object corresponding activity target
|
||||
// See also if we need to update useTimelineActivities
|
||||
const { records: activityTargets, loading: loadingActivityTargets } =
|
||||
useFindManyRecords<TaskTarget | NoteTarget>({
|
||||
skip,
|
||||
objectNameSingular:
|
||||
FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.objectNameSingular,
|
||||
filter: activityTargetsFilter,
|
||||
recordGqlFields: FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.fields,
|
||||
onCompleted,
|
||||
});
|
||||
const {
|
||||
records: activityTargets,
|
||||
loading: loadingActivityTargets,
|
||||
totalCount: totalCountActivityTargets,
|
||||
fetchMoreRecords: fetchMoreActivityTargets,
|
||||
hasNextPage,
|
||||
} = useFindManyRecords<TaskTarget | NoteTarget>({
|
||||
skip,
|
||||
objectNameSingular:
|
||||
FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.objectNameSingular,
|
||||
filter: activityTargetsFilter,
|
||||
recordGqlFields: FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.fields,
|
||||
onCompleted,
|
||||
orderBy: activityTargetsOrderByVariables,
|
||||
limit,
|
||||
});
|
||||
|
||||
return {
|
||||
activityTargets,
|
||||
loadingActivityTargets,
|
||||
totalCountActivityTargets: totalCountActivityTargets ?? 0,
|
||||
fetchMoreActivityTargets,
|
||||
hasNextPage,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user