Feat/activity optimistic activities (#4009)
* Fix naming * Fixed cache.evict bug for relation target deletion * Fixed cascade delete activity targets * Working version * Fix * fix * WIP * Fixed optimistic effect target inline cell * Removed openCreateActivityDrawer v1 * Ok for timeline * Removed console.log * Fix update record optimistic effect * Refactored activity queries into useActivities for everything * Fixed bugs * Cleaned * Fix lint --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,32 @@
|
||||
import { useInjectIntoActivitiesQuery } from '@/activities/hooks/useInjectIntoActivitiesQuery';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
|
||||
export const useInjectIntoTimelineActivitiesQueries = () => {
|
||||
const { injectActivitiesQueries } = useInjectIntoActivitiesQuery();
|
||||
|
||||
const injectIntoTimelineActivitiesQueries = ({
|
||||
activityToInject,
|
||||
activityTargetsToInject,
|
||||
timelineTargetableObject,
|
||||
}: {
|
||||
activityToInject: Activity;
|
||||
activityTargetsToInject: ActivityTarget[];
|
||||
timelineTargetableObject: ActivityTargetableObject;
|
||||
}) => {
|
||||
injectActivitiesQueries({
|
||||
activitiesFilters: {},
|
||||
activitiesOrderByVariables: {
|
||||
createdAt: 'DescNullsFirst',
|
||||
},
|
||||
activityTargetsToInject,
|
||||
activityToInject,
|
||||
targetableObjects: [timelineTargetableObject],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
injectIntoTimelineActivitiesQueries,
|
||||
};
|
||||
};
|
||||
@ -1,124 +0,0 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { makeTimelineActivitiesQueryVariables } from '@/activities/timeline/utils/makeTimelineActivitiesQueryVariables';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getTargetObjectFilterFieldName';
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useReadFindManyRecordsQueryInCache } from '@/object-record/cache/hooks/useReadFindManyRecordsQueryInCache';
|
||||
import { useUpsertFindManyRecordsQueryInCache } from '@/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache';
|
||||
|
||||
export const useInjectIntoTimelineActivitiesQueryAfterDrawerMount = () => {
|
||||
const { objectMetadataItem: objectMetadataItemActivity } =
|
||||
useObjectMetadataItemOnly({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
});
|
||||
|
||||
const {
|
||||
upsertFindManyRecordsQueryInCache: overwriteFindManyActivitiesInCache,
|
||||
} = useUpsertFindManyRecordsQueryInCache({
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: objectMetadataItemActivityTarget } =
|
||||
useObjectMetadataItemOnly({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
});
|
||||
|
||||
const {
|
||||
readFindManyRecordsQueryInCache: readFindManyActivityTargetsQueryInCache,
|
||||
} = useReadFindManyRecordsQueryInCache({
|
||||
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
});
|
||||
|
||||
const {
|
||||
readFindManyRecordsQueryInCache: readFindManyActivitiesQueryInCache,
|
||||
} = useReadFindManyRecordsQueryInCache({
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
const {
|
||||
upsertFindManyRecordsQueryInCache:
|
||||
overwriteFindManyActivityTargetsQueryInCache,
|
||||
} = useUpsertFindManyRecordsQueryInCache({
|
||||
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
});
|
||||
|
||||
const injectIntoTimelineActivitiesQueryAfterDrawerMount = ({
|
||||
activityToInject,
|
||||
activityTargetsToInject,
|
||||
timelineTargetableObject,
|
||||
}: {
|
||||
activityToInject: Activity;
|
||||
activityTargetsToInject: ActivityTarget[];
|
||||
timelineTargetableObject: ActivityTargetableObject;
|
||||
}) => {
|
||||
const newActivity = {
|
||||
...activityToInject,
|
||||
__typename: 'Activity',
|
||||
};
|
||||
|
||||
const targetObjectFieldName = getActivityTargetObjectFieldIdName({
|
||||
nameSingular: timelineTargetableObject.targetObjectNameSingular,
|
||||
});
|
||||
|
||||
const activitiyTargetsForTargetableObjectQueryVariables = {
|
||||
filter: {
|
||||
[targetObjectFieldName]: {
|
||||
eq: timelineTargetableObject.id,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const existingActivityTargetsForTargetableObject =
|
||||
readFindManyActivityTargetsQueryInCache({
|
||||
queryVariables: activitiyTargetsForTargetableObjectQueryVariables,
|
||||
});
|
||||
|
||||
const newActivityTargetsForTargetableObject = [
|
||||
...existingActivityTargetsForTargetableObject,
|
||||
...activityTargetsToInject,
|
||||
];
|
||||
|
||||
const existingActivityIds = existingActivityTargetsForTargetableObject
|
||||
?.map((activityTarget) => activityTarget.activityId)
|
||||
.filter(isNonEmptyString);
|
||||
|
||||
const timelineActivitiesQueryVariablesBeforeDrawerMount =
|
||||
makeTimelineActivitiesQueryVariables({
|
||||
activityIds: existingActivityIds,
|
||||
});
|
||||
|
||||
const existingActivities = readFindManyActivitiesQueryInCache({
|
||||
queryVariables: timelineActivitiesQueryVariablesBeforeDrawerMount,
|
||||
});
|
||||
|
||||
const activityIdsAfterDrawerMount = [
|
||||
...existingActivityIds,
|
||||
newActivity.id,
|
||||
];
|
||||
|
||||
const timelineActivitiesQueryVariablesAfterDrawerMount =
|
||||
makeTimelineActivitiesQueryVariables({
|
||||
activityIds: activityIdsAfterDrawerMount,
|
||||
});
|
||||
|
||||
overwriteFindManyActivityTargetsQueryInCache({
|
||||
objectRecordsToOverwrite: newActivityTargetsForTargetableObject,
|
||||
queryVariables: activitiyTargetsForTargetableObjectQueryVariables,
|
||||
});
|
||||
|
||||
const newActivities = [newActivity, ...existingActivities];
|
||||
|
||||
overwriteFindManyActivitiesInCache({
|
||||
objectRecordsToOverwrite: newActivities,
|
||||
queryVariables: timelineActivitiesQueryVariablesAfterDrawerMount,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
injectIntoTimelineActivitiesQueryAfterDrawerMount,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,138 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRemoveFromActivitiesQueries } from '@/activities/hooks/useRemoveFromActivitiesQueries';
|
||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/constants/FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY';
|
||||
import { timelineTargetableObjectState } from '@/activities/timeline/states/timelineTargetableObjectState';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
|
||||
export const useRemoveFromTimelineActivitiesQueries = () => {
|
||||
const timelineTargetableObject = useRecoilValue(
|
||||
timelineTargetableObjectState,
|
||||
);
|
||||
|
||||
// const { objectMetadataItem: objectMetadataItemActivity } =
|
||||
// useObjectMetadataItemOnly({
|
||||
// objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
// });
|
||||
|
||||
// const {
|
||||
// upsertFindManyRecordsQueryInCache: overwriteFindManyActivitiesInCache,
|
||||
// } = useUpsertFindManyRecordsQueryInCache({
|
||||
// objectMetadataItem: objectMetadataItemActivity,
|
||||
// });
|
||||
|
||||
// const { objectMetadataItem: objectMetadataItemActivityTarget } =
|
||||
// useObjectMetadataItemOnly({
|
||||
// objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
// });
|
||||
|
||||
// const {
|
||||
// readFindManyRecordsQueryInCache: readFindManyActivityTargetsQueryInCache,
|
||||
// } = useReadFindManyRecordsQueryInCache({
|
||||
// objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
// });
|
||||
|
||||
// const {
|
||||
// readFindManyRecordsQueryInCache: readFindManyActivitiesQueryInCache,
|
||||
// } = useReadFindManyRecordsQueryInCache({
|
||||
// objectMetadataItem: objectMetadataItemActivity,
|
||||
// });
|
||||
|
||||
// const {
|
||||
// upsertFindManyRecordsQueryInCache:
|
||||
// overwriteFindManyActivityTargetsQueryInCache,
|
||||
// } = useUpsertFindManyRecordsQueryInCache({
|
||||
// objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
// });
|
||||
|
||||
const { removeFromActivitiesQueries } = useRemoveFromActivitiesQueries();
|
||||
|
||||
const removeFromTimelineActivitiesQueries = ({
|
||||
activityIdToRemove,
|
||||
activityTargetsToRemove,
|
||||
}: {
|
||||
activityIdToRemove: string;
|
||||
activityTargetsToRemove: ActivityTarget[];
|
||||
}) => {
|
||||
if (!timelineTargetableObject) {
|
||||
throw new Error('Timeline targetable object is not defined');
|
||||
}
|
||||
|
||||
removeFromActivitiesQueries({
|
||||
activityIdToRemove,
|
||||
activityTargetsToRemove,
|
||||
targetableObjects: [timelineTargetableObject],
|
||||
activitiesFilters: {},
|
||||
activitiesOrderByVariables: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
||||
});
|
||||
|
||||
// const targetObjectFieldName = getActivityTargetObjectFieldIdName({
|
||||
// nameSingular: timelineTargetableObject.targetObjectNameSingular,
|
||||
// });
|
||||
|
||||
// const activitiyTargetsForTargetableObjectQueryVariables = {
|
||||
// filter: {
|
||||
// [targetObjectFieldName]: {
|
||||
// eq: timelineTargetableObject.id,
|
||||
// },
|
||||
// },
|
||||
// };
|
||||
|
||||
// const existingActivityTargetsForTargetableObject =
|
||||
// readFindManyActivityTargetsQueryInCache({
|
||||
// queryVariables: activitiyTargetsForTargetableObjectQueryVariables,
|
||||
// });
|
||||
|
||||
// const newActivityTargetsForTargetableObject = isNonEmptyArray(
|
||||
// activityTargetsToRemove,
|
||||
// )
|
||||
// ? existingActivityTargetsForTargetableObject.filter(
|
||||
// (existingActivityTarget) =>
|
||||
// activityTargetsToRemove.some(
|
||||
// (activityTargetToRemove) =>
|
||||
// activityTargetToRemove.id !== existingActivityTarget.id,
|
||||
// ),
|
||||
// )
|
||||
// : existingActivityTargetsForTargetableObject;
|
||||
|
||||
// overwriteFindManyActivityTargetsQueryInCache({
|
||||
// objectRecordsToOverwrite: newActivityTargetsForTargetableObject,
|
||||
// queryVariables: activitiyTargetsForTargetableObjectQueryVariables,
|
||||
// });
|
||||
|
||||
// const existingActivityIds = existingActivityTargetsForTargetableObject
|
||||
// ?.map((activityTarget) => activityTarget.activityId)
|
||||
// .filter(isNonEmptyString);
|
||||
|
||||
// const timelineActivitiesQueryVariablesBeforeDrawerMount =
|
||||
// makeTimelineActivitiesQueryVariables({
|
||||
// activityIds: existingActivityIds,
|
||||
// });
|
||||
|
||||
// const existingActivities = readFindManyActivitiesQueryInCache({
|
||||
// queryVariables: timelineActivitiesQueryVariablesBeforeDrawerMount,
|
||||
// });
|
||||
|
||||
// const activityIdsAfterRemoval = existingActivityIds.filter(
|
||||
// (existingActivityId) => existingActivityId !== activityIdToRemove,
|
||||
// );
|
||||
|
||||
// const timelineActivitiesQueryVariablesAfterRemoval =
|
||||
// makeTimelineActivitiesQueryVariables({
|
||||
// activityIds: activityIdsAfterRemoval,
|
||||
// });
|
||||
|
||||
// const newActivities = existingActivities
|
||||
// .filter((existingActivity) => existingActivity.id !== activityIdToRemove)
|
||||
// .toSorted(sortObjectRecordByDateField('createdAt', 'DescNullsFirst'));
|
||||
|
||||
// overwriteFindManyActivitiesInCache({
|
||||
// objectRecordsToOverwrite: newActivities,
|
||||
// queryVariables: timelineActivitiesQueryVariablesAfterRemoval,
|
||||
// });
|
||||
};
|
||||
|
||||
return {
|
||||
removeFromTimelineActivitiesQueries,
|
||||
};
|
||||
};
|
||||
@ -1,18 +1,37 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
|
||||
import { useActivityTargetsForTargetableObject } from '@/activities/hooks/useActivityTargetsForTargetableObject';
|
||||
import { timelineTargetableObjectState } from '@/activities/timeline/states/timelineTargetableObjectState';
|
||||
import { makeTimelineActivitiesQueryVariables } from '@/activities/timeline/utils/makeTimelineActivitiesQueryVariables';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { useActivityConnectionUtils } from '@/activities/utils/useActivityConnectionUtils';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useTimelineActivities = ({
|
||||
targetableObject,
|
||||
}: {
|
||||
targetableObject: ActivityTargetableObject;
|
||||
}) => {
|
||||
const { makeActivityWithoutConnection } = useActivityConnectionUtils();
|
||||
|
||||
const [, setTimelineTargetableObject] = useRecoilState(
|
||||
timelineTargetableObjectState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(targetableObject)) {
|
||||
setTimelineTargetableObject(targetableObject);
|
||||
}
|
||||
}, [targetableObject, setTimelineTargetableObject]);
|
||||
|
||||
const {
|
||||
activityTargets,
|
||||
loadingActivityTargets,
|
||||
@ -23,9 +42,14 @@ export const useTimelineActivities = ({
|
||||
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
|
||||
const activityIds = activityTargets
|
||||
?.map((activityTarget) => activityTarget.activityId)
|
||||
.filter(isNonEmptyString);
|
||||
const activityIds = Array.from(
|
||||
new Set(
|
||||
activityTargets
|
||||
?.map((activityTarget) => activityTarget.activityId)
|
||||
.filter(isNonEmptyString)
|
||||
.toSorted(sortByAscString),
|
||||
),
|
||||
);
|
||||
|
||||
const timelineActivitiesQueryVariables = makeTimelineActivitiesQueryVariables(
|
||||
{
|
||||
@ -33,17 +57,30 @@ export const useTimelineActivities = ({
|
||||
},
|
||||
);
|
||||
|
||||
const { records: activities, loading: loadingActivities } =
|
||||
const { records: activitiesWithConnection, loading: loadingActivities } =
|
||||
useFindManyRecords<Activity>({
|
||||
skip: loadingActivityTargets || !isNonEmptyArray(activityTargets),
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
filter: timelineActivitiesQueryVariables.filter,
|
||||
orderBy: timelineActivitiesQueryVariables.orderBy,
|
||||
onCompleted: () => {
|
||||
if (!initialized) {
|
||||
setInitialized(true);
|
||||
}
|
||||
},
|
||||
onCompleted: useRecoilCallback(
|
||||
({ set }) =>
|
||||
(data) => {
|
||||
if (!initialized) {
|
||||
setInitialized(true);
|
||||
}
|
||||
|
||||
const activities = getRecordsFromRecordConnection({
|
||||
recordConnection: data,
|
||||
});
|
||||
|
||||
for (const activity of activities) {
|
||||
set(recordStoreFamilyState(activity.id), activity);
|
||||
}
|
||||
},
|
||||
[initialized],
|
||||
),
|
||||
depth: 3,
|
||||
});
|
||||
|
||||
const noActivityTargets =
|
||||
@ -57,6 +94,11 @@ export const useTimelineActivities = ({
|
||||
|
||||
const loading = loadingActivities || loadingActivityTargets;
|
||||
|
||||
const activities = activitiesWithConnection
|
||||
?.map(makeActivityWithoutConnection as any)
|
||||
.map(({ activity }: any) => activity as any)
|
||||
.filter(isDefined);
|
||||
|
||||
return {
|
||||
activities,
|
||||
loading,
|
||||
|
||||
Reference in New Issue
Block a user