## Query depth deprecation

I'm deprecating depth parameter in our graphql query / cache tooling.
They were obsolete since we introduce the possibility to provide
RecordGqlFields

## Refactor combinedFindManyRecordHook

The hook can now take an array of operationSignatures

## Fix tasks issues

Fix optimistic rendering issue. Note that we still haven't handle
optimisticEffect on creation properly
This commit is contained in:
Charles Bochet
2024-04-29 23:33:23 +02:00
committed by GitHub
parent c946572fde
commit 6a14b1c6d6
187 changed files with 958 additions and 1482 deletions

View File

@ -79,7 +79,7 @@ const mocks: MockedResponse[] = [
}
`,
variables: {
filter: { activityTargetId: { eq: '123' } },
filter: { companyId: { eq: '123' } },
limit: undefined,
orderBy: undefined,
},
@ -180,7 +180,6 @@ describe('useActivities', () => {
activitiesFilters: {},
activitiesOrderByVariables: {},
skip: false,
skipActivityTargets: false,
}),
{ wrapper: Wrapper },
);
@ -188,8 +187,6 @@ describe('useActivities', () => {
expect(result.current).toEqual({
activities: [],
loading: false,
initialized: true,
noActivities: true,
});
});
@ -202,12 +199,11 @@ describe('useActivities', () => {
const activities = useActivities({
targetableObjects: [
{ targetObjectNameSingular: 'activityTarget', id: '123' },
{ targetObjectNameSingular: 'company', id: '123' },
],
activitiesFilters: {},
activitiesOrderByVariables: {},
skip: false,
skipActivityTargets: false,
});
return { activities, setCurrentWorkspaceMember };
},
@ -218,18 +214,9 @@ describe('useActivities', () => {
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
});
expect(result.current.activities.loading).toBe(true);
// Wait for activityTargets to complete fetching
await waitFor(() => !result.current.activities.loading);
expect(result.current.activities.loading).toBe(false);
// Wait for request to fetch activities to be made
await waitFor(() => result.current.activities.loading);
// Wait for activities to complete fetching
await waitFor(() => !result.current.activities.loading);
await waitFor(() => {
expect(result.current.activities.loading).toBe(false);
});
const { activities } = result.current;

View File

@ -10,8 +10,8 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
const mockObjectMetadataItems = getObjectMetadataItemsMock();
const cache = new InMemoryCache();
@ -85,7 +85,6 @@ cache.writeFragment({
id
createdAt
updatedAt
targetObjectNameSingular
personId
companyId
company {
@ -114,9 +113,11 @@ cache.writeFragment({
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
<MockedProvider cache={cache}>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
<JestObjectMetadataItemSetter>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
{children}
</SnackBarProviderScope>
</JestObjectMetadataItemSetter>
</MockedProvider>
</RecoilRoot>
);

View File

@ -82,7 +82,10 @@ describe('useCreateActivityInDB', () => {
});
await act(async () => {
await result.current.createActivityInDB(mockedActivity);
await result.current.createActivityInDB({
...mockedActivity,
__typename: 'Activity',
});
});
expect(mocks[0].result).toHaveBeenCalled();

View File

@ -1,15 +1,14 @@
import { useEffect, useState } from 'react';
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
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 { FIND_MANY_ACTIVITIES_QUERY_KEY } from '@/activities/query-keys/FindManyActivitiesQueryKey';
import { Activity } from '@/activities/types/Activity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { OrderByField } from '@/object-metadata/types/OrderByField';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { sortByAscString } from '~/utils/array/sortByAscString';
@ -18,26 +17,19 @@ export const useActivities = ({
activitiesFilters,
activitiesOrderByVariables,
skip,
skipActivityTargets,
}: {
targetableObjects: ActivityTargetableObject[];
activitiesFilters: ObjectRecordQueryFilter;
activitiesOrderByVariables: OrderByField;
activitiesFilters: RecordGqlOperationFilter;
activitiesOrderByVariables: RecordGqlOperationOrderBy;
skip?: boolean;
skipActivityTargets?: boolean;
}) => {
const [initialized, setInitialized] = useState(false);
const { objectMetadataItems } = useObjectMetadataItems();
const {
activityTargets,
loadingActivityTargets,
initialized: initializedActivityTargets,
} = useActivityTargetsForTargetableObjects({
targetableObjects,
skip: skipActivityTargets || skip,
});
const { activityTargets, loadingActivityTargets } =
useActivityTargetsForTargetableObjects({
targetableObjects,
skip: skip,
});
const activityIds = [
...new Set(
@ -51,70 +43,40 @@ export const useActivities = ({
),
];
const activityTargetsFound =
initializedActivityTargets && isNonEmptyArray(activityTargets);
const filter: ObjectRecordQueryFilter = {
id: activityTargetsFound
? {
in: activityIds,
}
: undefined,
const filter: RecordGqlOperationFilter = {
id:
targetableObjects.length > 0
? {
in: activityIds,
}
: undefined,
...activitiesFilters,
};
const skipActivities =
skip ||
(!skipActivityTargets &&
(!initializedActivityTargets || !activityTargetsFound));
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
findActivitiesOperationSignatureFactory({ objectMetadataItems });
const { records: activities, loading: loadingActivities } =
useFindManyRecords<Activity>({
skip: skipActivities,
objectNameSingular: FIND_MANY_ACTIVITIES_QUERY_KEY.objectNameSingular,
depth: FIND_MANY_ACTIVITIES_QUERY_KEY.depth,
queryFields:
FIND_MANY_ACTIVITIES_QUERY_KEY.fieldsFactory?.(objectMetadataItems),
skip: skip || loadingActivityTargets,
objectNameSingular:
FIND_ACTIVITIES_OPERATION_SIGNATURE.objectNameSingular,
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
filter,
orderBy: activitiesOrderByVariables,
onCompleted: useRecoilCallback(
({ set }) =>
(activities) => {
if (!initialized) {
setInitialized(true);
}
for (const activity of activities) {
set(recordStoreFamilyState(activity.id), activity);
}
},
[initialized],
[],
),
});
const loading = loadingActivities || loadingActivityTargets;
const noActivities =
(!activityTargetsFound && !skipActivityTargets && initialized) ||
(initialized && !loading && !isNonEmptyArray(activities));
useEffect(() => {
if (skipActivities || noActivities) {
setInitialized(true);
}
}, [
activities,
initialized,
loading,
noActivities,
skipActivities,
skipActivityTargets,
]);
return {
activities,
loading,
initialized,
noActivities,
loading: loadingActivities || loadingActivityTargets,
};
};

View File

@ -1,4 +1,3 @@
import { useState } from 'react';
import { isNonEmptyString } from '@sniptt/guards';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
@ -16,8 +15,6 @@ export const useActivityTargetsForTargetableObject = ({
nameSingular: targetableObject.targetObjectNameSingular,
});
const [initialized, setInitialized] = useState(false);
const targetableObjectId = targetableObject.id;
const skipRequest = !isNonEmptyString(targetableObjectId);
@ -34,16 +31,10 @@ export const useActivityTargetsForTargetableObject = ({
eq: targetableObject.id,
},
},
onCompleted: () => {
if (!initialized) {
setInitialized(true);
}
},
});
return {
activityTargets,
loadingActivityTargets,
initialized,
};
};

View File

@ -1,7 +1,6 @@
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY } from '@/activities/query-keys/FindManyActivityTargetsQueryKey';
import { findActivityTargetsOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { getActivityTargetsFilter } from '@/activities/utils/getActivityTargetsFilter';
@ -11,12 +10,14 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
export const useActivityTargetsForTargetableObjects = ({
targetableObjects,
skip,
onCompleted,
}: {
targetableObjects: Pick<
ActivityTargetableObject,
'id' | 'targetObjectNameSingular'
>[];
skip?: boolean;
onCompleted?: (activityTargets: ActivityTarget[]) => void;
}) => {
const activityTargetsFilter = getActivityTargetsFilter({
targetableObjects: targetableObjects,
@ -24,7 +25,8 @@ export const useActivityTargetsForTargetableObjects = ({
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const [initialized, setInitialized] = useState(false);
const FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE =
findActivityTargetsOperationSignatureFactory({ objectMetadataItems });
// 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
@ -33,22 +35,14 @@ export const useActivityTargetsForTargetableObjects = ({
useFindManyRecords<ActivityTarget>({
skip,
objectNameSingular:
FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY.objectNameSingular,
FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.objectNameSingular,
filter: activityTargetsFilter,
queryFields:
FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY.fieldsFactory?.(
objectMetadataItems,
),
onCompleted: () => {
if (!initialized) {
setInitialized(true);
}
},
recordGqlFields: FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.fields,
onCompleted,
});
return {
activityTargets,
loadingActivityTargets,
initialized,
};
};

View File

@ -34,7 +34,6 @@ export const useCreateActivityInCache = () => {
const { record: currentWorkspaceMemberRecord } = useFindOneRecord({
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
objectRecordId: currentWorkspaceMember?.id,
depth: 0,
});
const { objectMetadataItem: objectMetadataItemActivity } =
@ -66,6 +65,7 @@ export const useCreateActivityInCache = () => {
const createdActivityInCache = createOneActivityInCache({
id: activityId,
__typename: 'Activity',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
author: currentWorkspaceMemberRecord,

View File

@ -1,6 +1,6 @@
import { isNonEmptyArray } from '@sniptt/guards';
import { CREATE_ONE_ACTIVITY_QUERY_KEY } from '@/activities/query-keys/CreateOneActivityQueryKey';
import { CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE } from '@/activities/graphql/operation-signatures/CreateOneActivityOperationSignature';
import { ActivityForEditor } from '@/activities/types/ActivityForEditor';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -9,9 +9,9 @@ import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
export const useCreateActivityInDB = () => {
const { createOneRecord: createOneActivity } = useCreateOneRecord({
objectNameSingular: CREATE_ONE_ACTIVITY_QUERY_KEY.objectNameSingular,
queryFields: CREATE_ONE_ACTIVITY_QUERY_KEY.fields,
depth: CREATE_ONE_ACTIVITY_QUERY_KEY.depth,
objectNameSingular:
CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.objectNameSingular,
recordGqlFields: CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.fields,
});
const { createManyRecords: createManyActivityTargets } =

View File

@ -1,6 +1,6 @@
import { useApolloClient } from '@apollo/client';
import { FIND_MANY_ACTIVITIES_QUERY_KEY } from '@/activities/query-keys/FindManyActivitiesQueryKey';
import { findActivitiesOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory';
import { Activity } from '@/activities/types/Activity';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
@ -104,15 +104,16 @@ export const usePrepareFindManyActivitiesQuery = () => {
return a.createdAt > b.createdAt ? -1 : 1;
});
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
findActivitiesOperationSignatureFactory({ objectMetadataItems });
upsertFindManyActivitiesInCache({
objectRecordsToOverwrite: filteredActivities,
queryVariables: {
...nextFindManyActivitiesQueryFilter,
orderBy: { createdAt: 'DescNullsFirst' },
},
depth: FIND_MANY_ACTIVITIES_QUERY_KEY.depth,
queryFields:
FIND_MANY_ACTIVITIES_QUERY_KEY.fieldsFactory?.(objectMetadataItems),
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
computeReferences: true,
});
};