Activity injection into Apollo cache (#3665)

- Created addRecordInCache to inject a record in Apollo cache and inject single read query on this record
- Created createOneRecordInCache and createManyRecordsInCache that uses this addRecordInCache
- Created useOpenCreateActivityDrawerV2 hook to create an activity in cache and inject it into all other relevant requests in the app before opening activity drawer
- Refactored DEFAULT_SEARCH_REQUEST_LIMIT constant and hardcoded arbitrary request limits
- Added Apollo dev logs to see errors in the console when manipulating cache
This commit is contained in:
Lucas Bordeau
2024-01-29 16:12:52 +01:00
committed by GitHub
parent 64d0e15ada
commit 3b458d5207
57 changed files with 1160 additions and 190 deletions

View File

@ -0,0 +1,89 @@
import { useApolloClient } from '@apollo/client';
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 { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
export const useInjectIntoTimelineActivitiesQuery = () => {
const apolloClient = useApolloClient();
const {
objectMetadataItem: objectMetadataItemActivity,
findManyRecordsQuery: findManyActivitiesQuery,
} = useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.Activity,
});
const injectIntoTimelineActivitiesQuery = ({
activityTargets,
activityToInject,
}: {
activityTargets: ActivityTarget[];
activityToInject: Activity;
}) => {
const activityIds = activityTargets
?.map((activityTarget) => activityTarget.activityId)
.filter(isNonEmptyString);
const timelineActivitiesQueryVariables =
makeTimelineActivitiesQueryVariables({
activityIds,
});
const exitistingActivitiesQueryResult = apolloClient.readQuery({
query: findManyActivitiesQuery,
variables: timelineActivitiesQueryVariables,
});
const extistingActivities = exitistingActivitiesQueryResult
? getRecordsFromRecordConnection({
recordConnection: exitistingActivitiesQueryResult[
objectMetadataItemActivity.namePlural
] as ObjectRecordConnection<Activity>,
})
: [];
const newActivity = {
...activityToInject,
__typename: 'Activity',
};
const newActivitiesSortedAsActivitiesQuery = [
newActivity,
...extistingActivities,
];
const newActivityIdsSortedAsActivityTargetsQuery = [
...extistingActivities,
newActivity,
].map((activity) => activity.id);
const newTimelineActivitiesQueryVariables =
makeTimelineActivitiesQueryVariables({
activityIds: newActivityIdsSortedAsActivityTargetsQuery,
});
const newActivityConnectionForCache = getRecordConnectionFromRecords({
objectNameSingular: CoreObjectNameSingular.Activity,
records: newActivitiesSortedAsActivitiesQuery,
});
apolloClient.writeQuery({
query: findManyActivitiesQuery,
variables: newTimelineActivitiesQueryVariables,
data: {
[objectMetadataItemActivity.namePlural]: newActivityConnectionForCache,
},
});
};
return {
injectIntoTimelineActivitiesNextQuery: injectIntoTimelineActivitiesQuery,
};
};

View File

@ -0,0 +1,73 @@
import { useEffect, useState } from 'react';
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
import { useActivityTargets } from '@/activities/hooks/useActivityTargets';
import { makeTimelineActivitiesQueryVariables } from '@/activities/timeline/utils/makeTimelineActivitiesQueryVariables';
import { Activity } from '@/activities/types/Activity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
export const useTimelineActivities = ({
targetableObject,
}: {
targetableObject: ActivityTargetableObject;
}) => {
const {
activityTargets,
loadingActivityTargets,
initialized: initializedActivityTargets,
} = useActivityTargets({
targetableObject,
});
const [initialized, setInitialized] = useState(false);
const [activities, setActivities] = useState<Activity[]>([]);
const activityIds = activityTargets
?.map((activityTarget) => activityTarget.activityId)
.filter(isNonEmptyString);
const timelineActivitiesQueryVariables = makeTimelineActivitiesQueryVariables(
{
activityIds,
},
);
const { records: activitiesFromRequest, loading: loadingActivities } =
useFindManyRecords<Activity>({
skip: loadingActivityTargets || !isNonEmptyArray(activityTargets),
objectNameSingular: CoreObjectNameSingular.Activity,
filter: timelineActivitiesQueryVariables.filter,
orderBy: timelineActivitiesQueryVariables.orderBy,
onCompleted: () => {
if (!initialized) {
setInitialized(true);
}
},
});
useEffect(() => {
if (!loadingActivities) {
setActivities(activitiesFromRequest);
}
}, [activitiesFromRequest, loadingActivities]);
const noActivityTargets =
initializedActivityTargets && !isNonEmptyArray(activityTargets);
useEffect(() => {
if (noActivityTargets) {
setInitialized(true);
}
}, [noActivityTargets]);
const loading = loadingActivities || loadingActivityTargets;
return {
activities,
loading,
initialized,
};
};