Fix tasks (#5199)
## 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:
@ -3,4 +3,5 @@ export type BlocklistItem = {
|
|||||||
handle: string;
|
handle: string;
|
||||||
workspaceMemberId: string;
|
workspaceMemberId: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
__typename: 'BlocklistItem';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,4 +9,5 @@ export type CalendarChannel = {
|
|||||||
isContactAutoCreationEnabled?: boolean;
|
isContactAutoCreationEnabled?: boolean;
|
||||||
isSyncEnabled?: boolean;
|
isSyncEnabled?: boolean;
|
||||||
visibility: CalendarChannelVisibility;
|
visibility: CalendarChannelVisibility;
|
||||||
|
__typename: 'CalendarChannel';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,4 +13,5 @@ export type ConnectedAccount = {
|
|||||||
authFailedAt: Date | null;
|
authFailedAt: Date | null;
|
||||||
messageChannels: MessageChannel[];
|
messageChannels: MessageChannel[];
|
||||||
calendarChannels: CalendarChannel[];
|
calendarChannels: CalendarChannel[];
|
||||||
|
__typename: 'ConnectedAccount';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,4 +7,5 @@ export type MessageChannel = {
|
|||||||
isSyncEnabled: boolean;
|
isSyncEnabled: boolean;
|
||||||
visibility: InboxSettingsVisibilityValue;
|
visibility: InboxSettingsVisibilityValue;
|
||||||
syncStatus: string;
|
syncStatus: string;
|
||||||
|
__typename: 'MessageChannel';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { sortDesc } from '~/utils/sort';
|
|||||||
|
|
||||||
type CalendarEventGeneric = Omit<
|
type CalendarEventGeneric = Omit<
|
||||||
CalendarEvent,
|
CalendarEvent,
|
||||||
'participants' | 'externalCreatedAt'
|
'participants' | 'externalCreatedAt' | '__typename'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const useCalendarEvents = <T extends CalendarEventGeneric>(
|
export const useCalendarEvents = <T extends CalendarEventGeneric>(
|
||||||
|
|||||||
@ -14,7 +14,6 @@ export const RightDrawerCalendarEvent = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.CalendarEvent,
|
objectNameSingular: CoreObjectNameSingular.CalendarEvent,
|
||||||
objectRecordId: viewableCalendarEventId ?? '',
|
objectRecordId: viewableCalendarEventId ?? '',
|
||||||
onCompleted: (record) => setRecords([record]),
|
onCompleted: (record) => setRecords([record]),
|
||||||
depth: 2,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!calendarEvent) return null;
|
if (!calendarEvent) return null;
|
||||||
|
|||||||
@ -17,4 +17,5 @@ export type CalendarEvent = {
|
|||||||
title?: string;
|
title?: string;
|
||||||
visibility: 'METADATA' | 'SHARE_EVERYTHING';
|
visibility: 'METADATA' | 'SHARE_EVERYTHING';
|
||||||
calendarEventParticipants?: CalendarEventParticipant[];
|
calendarEventParticipants?: CalendarEventParticipant[];
|
||||||
|
__typename: 'CalendarEvent';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -161,6 +161,7 @@ export const ActivityBodyEditor = ({
|
|||||||
...oldActivity,
|
...oldActivity,
|
||||||
id: activityId,
|
id: activityId,
|
||||||
body: newStringifiedBody,
|
body: newStringifiedBody,
|
||||||
|
__typename: 'Activity',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -192,6 +193,7 @@ export const ActivityBodyEditor = ({
|
|||||||
...oldActivity,
|
...oldActivity,
|
||||||
id: activityId,
|
id: activityId,
|
||||||
title: newTitleFromBody,
|
title: newTitleFromBody,
|
||||||
|
__typename: 'Activity',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -134,6 +134,7 @@ export const ActivityTitle = ({ activityId }: ActivityTitleProps) => {
|
|||||||
...currentActivity,
|
...currentActivity,
|
||||||
id: activity.id,
|
id: activity.id,
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
|
__typename: activity.__typename,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,6 @@ export const useRightDrawerEmailThread = () => {
|
|||||||
loading,
|
loading,
|
||||||
fetchMoreRecords,
|
fetchMoreRecords,
|
||||||
} = useFindManyRecords<EmailThreadMessageType>({
|
} = useFindManyRecords<EmailThreadMessageType>({
|
||||||
depth: 3,
|
|
||||||
limit: 10,
|
limit: 10,
|
||||||
filter: {
|
filter: {
|
||||||
messageThreadId: {
|
messageThreadId: {
|
||||||
|
|||||||
@ -5,4 +5,5 @@ export type EmailThreadMessage = {
|
|||||||
text: string;
|
text: string;
|
||||||
receivedAt: string;
|
receivedAt: string;
|
||||||
messageParticipants: EmailThreadMessageParticipant[];
|
messageParticipants: EmailThreadMessageParticipant[];
|
||||||
|
__typename: 'EmailThreadMessage';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { Attachment } from '@/activities/files/types/Attachment';
|
||||||
import { getFileType } from '@/activities/files/utils/getFileType';
|
import { getFileType } from '@/activities/files/utils/getFileType';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName';
|
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName';
|
||||||
import { Attachment } from '@/attachments/types/Attachment';
|
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||||
|
|||||||
@ -8,7 +8,9 @@ export type Attachment = {
|
|||||||
activityId: string;
|
activityId: string;
|
||||||
authorId: string;
|
authorId: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
__typename: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AttachmentType =
|
export type AttachmentType =
|
||||||
| 'Archive'
|
| 'Archive'
|
||||||
| 'Audio'
|
| 'Audio'
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
|
||||||
import { QueryKey } from '@/object-record/query-keys/types/QueryKey';
|
|
||||||
|
|
||||||
export const FIND_MANY_ACTIVITIES_QUERY_KEY: QueryKey = {
|
export const CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE: RecordGqlOperationSignature =
|
||||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
{
|
||||||
variables: {},
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
fieldsFactory: (_objectMetadataItems: ObjectMetadataItem[]) => {
|
variables: {},
|
||||||
return {
|
fields: {
|
||||||
id: true,
|
id: true,
|
||||||
__typename: true,
|
__typename: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
@ -31,8 +30,5 @@ export const FIND_MANY_ACTIVITIES_QUERY_KEY: QueryKey = {
|
|||||||
dueAt: true,
|
dueAt: true,
|
||||||
reminderAt: true,
|
reminderAt: true,
|
||||||
type: true,
|
type: true,
|
||||||
activityTargets: true,
|
},
|
||||||
};
|
};
|
||||||
},
|
|
||||||
depth: 2,
|
|
||||||
};
|
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import { generateActivityTargetMorphFieldKeys } from '@/activities/utils/generateActivityTargetMorphFieldKeys';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory';
|
||||||
|
|
||||||
|
export const findActivitiesOperationSignatureFactory: RecordGqlOperationSignatureFactory =
|
||||||
|
({ objectMetadataItems }: { objectMetadataItems: ObjectMetadataItem[] }) => ({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
variables: {},
|
||||||
|
fields: {
|
||||||
|
id: true,
|
||||||
|
__typename: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
author: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
__typename: true,
|
||||||
|
},
|
||||||
|
authorId: true,
|
||||||
|
assigneeId: true,
|
||||||
|
assignee: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
__typename: true,
|
||||||
|
},
|
||||||
|
comments: true,
|
||||||
|
attachments: true,
|
||||||
|
body: true,
|
||||||
|
title: true,
|
||||||
|
completedAt: true,
|
||||||
|
dueAt: true,
|
||||||
|
reminderAt: true,
|
||||||
|
type: true,
|
||||||
|
activityTargets: {
|
||||||
|
id: true,
|
||||||
|
__typename: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
activity: true,
|
||||||
|
activityId: true,
|
||||||
|
...generateActivityTargetMorphFieldKeys(objectMetadataItems),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { generateActivityTargetMorphFieldKeys } from '@/activities/utils/generateActivityTargetMorphFieldKeys';
|
import { generateActivityTargetMorphFieldKeys } from '@/activities/utils/generateActivityTargetMorphFieldKeys';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { QueryKey } from '@/object-record/query-keys/types/QueryKey';
|
import { RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory';
|
||||||
|
|
||||||
export const FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY: QueryKey = {
|
export const findActivityTargetsOperationSignatureFactory: RecordGqlOperationSignatureFactory =
|
||||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
({ objectMetadataItems }: { objectMetadataItems: ObjectMetadataItem[] }) => ({
|
||||||
variables: {},
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
fieldsFactory: (objectMetadataItems: ObjectMetadataItem[]) => {
|
variables: {},
|
||||||
return {
|
fields: {
|
||||||
id: true,
|
id: true,
|
||||||
__typename: true,
|
__typename: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
@ -15,7 +15,5 @@ export const FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY: QueryKey = {
|
|||||||
activity: true,
|
activity: true,
|
||||||
activityId: true,
|
activityId: true,
|
||||||
...generateActivityTargetMorphFieldKeys(objectMetadataItems),
|
...generateActivityTargetMorphFieldKeys(objectMetadataItems),
|
||||||
};
|
},
|
||||||
},
|
});
|
||||||
depth: 1,
|
|
||||||
};
|
|
||||||
@ -79,7 +79,7 @@ const mocks: MockedResponse[] = [
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
filter: { activityTargetId: { eq: '123' } },
|
filter: { companyId: { eq: '123' } },
|
||||||
limit: undefined,
|
limit: undefined,
|
||||||
orderBy: undefined,
|
orderBy: undefined,
|
||||||
},
|
},
|
||||||
@ -180,7 +180,6 @@ describe('useActivities', () => {
|
|||||||
activitiesFilters: {},
|
activitiesFilters: {},
|
||||||
activitiesOrderByVariables: {},
|
activitiesOrderByVariables: {},
|
||||||
skip: false,
|
skip: false,
|
||||||
skipActivityTargets: false,
|
|
||||||
}),
|
}),
|
||||||
{ wrapper: Wrapper },
|
{ wrapper: Wrapper },
|
||||||
);
|
);
|
||||||
@ -188,8 +187,6 @@ describe('useActivities', () => {
|
|||||||
expect(result.current).toEqual({
|
expect(result.current).toEqual({
|
||||||
activities: [],
|
activities: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
initialized: true,
|
|
||||||
noActivities: true,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -202,12 +199,11 @@ describe('useActivities', () => {
|
|||||||
|
|
||||||
const activities = useActivities({
|
const activities = useActivities({
|
||||||
targetableObjects: [
|
targetableObjects: [
|
||||||
{ targetObjectNameSingular: 'activityTarget', id: '123' },
|
{ targetObjectNameSingular: 'company', id: '123' },
|
||||||
],
|
],
|
||||||
activitiesFilters: {},
|
activitiesFilters: {},
|
||||||
activitiesOrderByVariables: {},
|
activitiesOrderByVariables: {},
|
||||||
skip: false,
|
skip: false,
|
||||||
skipActivityTargets: false,
|
|
||||||
});
|
});
|
||||||
return { activities, setCurrentWorkspaceMember };
|
return { activities, setCurrentWorkspaceMember };
|
||||||
},
|
},
|
||||||
@ -218,18 +214,9 @@ describe('useActivities', () => {
|
|||||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.activities.loading).toBe(true);
|
await waitFor(() => {
|
||||||
|
expect(result.current.activities.loading).toBe(false);
|
||||||
// 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);
|
|
||||||
|
|
||||||
const { activities } = result.current;
|
const { activities } = result.current;
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
|||||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
||||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||||
|
|
||||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||||
|
|
||||||
const cache = new InMemoryCache();
|
const cache = new InMemoryCache();
|
||||||
@ -85,7 +85,6 @@ cache.writeFragment({
|
|||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
targetObjectNameSingular
|
|
||||||
personId
|
personId
|
||||||
companyId
|
companyId
|
||||||
company {
|
company {
|
||||||
@ -114,9 +113,11 @@ cache.writeFragment({
|
|||||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
<RecoilRoot>
|
<RecoilRoot>
|
||||||
<MockedProvider cache={cache}>
|
<MockedProvider cache={cache}>
|
||||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
<JestObjectMetadataItemSetter>
|
||||||
{children}
|
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||||
</SnackBarProviderScope>
|
{children}
|
||||||
|
</SnackBarProviderScope>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</RecoilRoot>
|
</RecoilRoot>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -82,7 +82,10 @@ describe('useCreateActivityInDB', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await result.current.createActivityInDB(mockedActivity);
|
await result.current.createActivityInDB({
|
||||||
|
...mockedActivity,
|
||||||
|
__typename: 'Activity',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks[0].result).toHaveBeenCalled();
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { findActivitiesOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory';
|
||||||
import { useActivityTargetsForTargetableObjects } from '@/activities/hooks/useActivityTargetsForTargetableObjects';
|
import { useActivityTargetsForTargetableObjects } from '@/activities/hooks/useActivityTargetsForTargetableObjects';
|
||||||
import { FIND_MANY_ACTIVITIES_QUERY_KEY } from '@/activities/query-keys/FindManyActivitiesQueryKey';
|
|
||||||
import { Activity } from '@/activities/types/Activity';
|
import { Activity } from '@/activities/types/Activity';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
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 { 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 { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||||
|
|
||||||
@ -18,26 +17,19 @@ export const useActivities = ({
|
|||||||
activitiesFilters,
|
activitiesFilters,
|
||||||
activitiesOrderByVariables,
|
activitiesOrderByVariables,
|
||||||
skip,
|
skip,
|
||||||
skipActivityTargets,
|
|
||||||
}: {
|
}: {
|
||||||
targetableObjects: ActivityTargetableObject[];
|
targetableObjects: ActivityTargetableObject[];
|
||||||
activitiesFilters: ObjectRecordQueryFilter;
|
activitiesFilters: RecordGqlOperationFilter;
|
||||||
activitiesOrderByVariables: OrderByField;
|
activitiesOrderByVariables: RecordGqlOperationOrderBy;
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
skipActivityTargets?: boolean;
|
|
||||||
}) => {
|
}) => {
|
||||||
const [initialized, setInitialized] = useState(false);
|
|
||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
const {
|
const { activityTargets, loadingActivityTargets } =
|
||||||
activityTargets,
|
useActivityTargetsForTargetableObjects({
|
||||||
loadingActivityTargets,
|
targetableObjects,
|
||||||
initialized: initializedActivityTargets,
|
skip: skip,
|
||||||
} = useActivityTargetsForTargetableObjects({
|
});
|
||||||
targetableObjects,
|
|
||||||
skip: skipActivityTargets || skip,
|
|
||||||
});
|
|
||||||
|
|
||||||
const activityIds = [
|
const activityIds = [
|
||||||
...new Set(
|
...new Set(
|
||||||
@ -51,70 +43,40 @@ export const useActivities = ({
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
const activityTargetsFound =
|
const filter: RecordGqlOperationFilter = {
|
||||||
initializedActivityTargets && isNonEmptyArray(activityTargets);
|
id:
|
||||||
|
targetableObjects.length > 0
|
||||||
const filter: ObjectRecordQueryFilter = {
|
? {
|
||||||
id: activityTargetsFound
|
in: activityIds,
|
||||||
? {
|
}
|
||||||
in: activityIds,
|
: undefined,
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
...activitiesFilters,
|
...activitiesFilters,
|
||||||
};
|
};
|
||||||
|
|
||||||
const skipActivities =
|
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
|
||||||
skip ||
|
findActivitiesOperationSignatureFactory({ objectMetadataItems });
|
||||||
(!skipActivityTargets &&
|
|
||||||
(!initializedActivityTargets || !activityTargetsFound));
|
|
||||||
|
|
||||||
const { records: activities, loading: loadingActivities } =
|
const { records: activities, loading: loadingActivities } =
|
||||||
useFindManyRecords<Activity>({
|
useFindManyRecords<Activity>({
|
||||||
skip: skipActivities,
|
skip: skip || loadingActivityTargets,
|
||||||
objectNameSingular: FIND_MANY_ACTIVITIES_QUERY_KEY.objectNameSingular,
|
objectNameSingular:
|
||||||
depth: FIND_MANY_ACTIVITIES_QUERY_KEY.depth,
|
FIND_ACTIVITIES_OPERATION_SIGNATURE.objectNameSingular,
|
||||||
queryFields:
|
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
|
||||||
FIND_MANY_ACTIVITIES_QUERY_KEY.fieldsFactory?.(objectMetadataItems),
|
|
||||||
filter,
|
filter,
|
||||||
orderBy: activitiesOrderByVariables,
|
orderBy: activitiesOrderByVariables,
|
||||||
onCompleted: useRecoilCallback(
|
onCompleted: useRecoilCallback(
|
||||||
({ set }) =>
|
({ set }) =>
|
||||||
(activities) => {
|
(activities) => {
|
||||||
if (!initialized) {
|
|
||||||
setInitialized(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const activity of activities) {
|
for (const activity of activities) {
|
||||||
set(recordStoreFamilyState(activity.id), activity);
|
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 {
|
return {
|
||||||
activities,
|
activities,
|
||||||
loading,
|
loading: loadingActivities || loadingActivityTargets,
|
||||||
initialized,
|
|
||||||
noActivities,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
@ -16,8 +15,6 @@ export const useActivityTargetsForTargetableObject = ({
|
|||||||
nameSingular: targetableObject.targetObjectNameSingular,
|
nameSingular: targetableObject.targetObjectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState(false);
|
|
||||||
|
|
||||||
const targetableObjectId = targetableObject.id;
|
const targetableObjectId = targetableObject.id;
|
||||||
|
|
||||||
const skipRequest = !isNonEmptyString(targetableObjectId);
|
const skipRequest = !isNonEmptyString(targetableObjectId);
|
||||||
@ -34,16 +31,10 @@ export const useActivityTargetsForTargetableObject = ({
|
|||||||
eq: targetableObject.id,
|
eq: targetableObject.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onCompleted: () => {
|
|
||||||
if (!initialized) {
|
|
||||||
setInitialized(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activityTargets,
|
activityTargets,
|
||||||
loadingActivityTargets,
|
loadingActivityTargets,
|
||||||
initialized,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
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 { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { getActivityTargetsFilter } from '@/activities/utils/getActivityTargetsFilter';
|
import { getActivityTargetsFilter } from '@/activities/utils/getActivityTargetsFilter';
|
||||||
@ -11,12 +10,14 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
|||||||
export const useActivityTargetsForTargetableObjects = ({
|
export const useActivityTargetsForTargetableObjects = ({
|
||||||
targetableObjects,
|
targetableObjects,
|
||||||
skip,
|
skip,
|
||||||
|
onCompleted,
|
||||||
}: {
|
}: {
|
||||||
targetableObjects: Pick<
|
targetableObjects: Pick<
|
||||||
ActivityTargetableObject,
|
ActivityTargetableObject,
|
||||||
'id' | 'targetObjectNameSingular'
|
'id' | 'targetObjectNameSingular'
|
||||||
>[];
|
>[];
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
|
onCompleted?: (activityTargets: ActivityTarget[]) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const activityTargetsFilter = getActivityTargetsFilter({
|
const activityTargetsFilter = getActivityTargetsFilter({
|
||||||
targetableObjects: targetableObjects,
|
targetableObjects: targetableObjects,
|
||||||
@ -24,7 +25,8 @@ export const useActivityTargetsForTargetableObjects = ({
|
|||||||
|
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
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
|
// 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
|
// 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>({
|
useFindManyRecords<ActivityTarget>({
|
||||||
skip,
|
skip,
|
||||||
objectNameSingular:
|
objectNameSingular:
|
||||||
FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY.objectNameSingular,
|
FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.objectNameSingular,
|
||||||
filter: activityTargetsFilter,
|
filter: activityTargetsFilter,
|
||||||
queryFields:
|
recordGqlFields: FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.fields,
|
||||||
FIND_MANY_ACTIVITY_TARGETS_QUERY_KEY.fieldsFactory?.(
|
onCompleted,
|
||||||
objectMetadataItems,
|
|
||||||
),
|
|
||||||
onCompleted: () => {
|
|
||||||
if (!initialized) {
|
|
||||||
setInitialized(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activityTargets,
|
activityTargets,
|
||||||
loadingActivityTargets,
|
loadingActivityTargets,
|
||||||
initialized,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -34,7 +34,6 @@ export const useCreateActivityInCache = () => {
|
|||||||
const { record: currentWorkspaceMemberRecord } = useFindOneRecord({
|
const { record: currentWorkspaceMemberRecord } = useFindOneRecord({
|
||||||
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
||||||
objectRecordId: currentWorkspaceMember?.id,
|
objectRecordId: currentWorkspaceMember?.id,
|
||||||
depth: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { objectMetadataItem: objectMetadataItemActivity } =
|
const { objectMetadataItem: objectMetadataItemActivity } =
|
||||||
@ -66,6 +65,7 @@ export const useCreateActivityInCache = () => {
|
|||||||
|
|
||||||
const createdActivityInCache = createOneActivityInCache({
|
const createdActivityInCache = createOneActivityInCache({
|
||||||
id: activityId,
|
id: activityId,
|
||||||
|
__typename: 'Activity',
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
author: currentWorkspaceMemberRecord,
|
author: currentWorkspaceMemberRecord,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { isNonEmptyArray } from '@sniptt/guards';
|
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 { ActivityForEditor } from '@/activities/types/ActivityForEditor';
|
||||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
@ -9,9 +9,9 @@ import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
|||||||
|
|
||||||
export const useCreateActivityInDB = () => {
|
export const useCreateActivityInDB = () => {
|
||||||
const { createOneRecord: createOneActivity } = useCreateOneRecord({
|
const { createOneRecord: createOneActivity } = useCreateOneRecord({
|
||||||
objectNameSingular: CREATE_ONE_ACTIVITY_QUERY_KEY.objectNameSingular,
|
objectNameSingular:
|
||||||
queryFields: CREATE_ONE_ACTIVITY_QUERY_KEY.fields,
|
CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.objectNameSingular,
|
||||||
depth: CREATE_ONE_ACTIVITY_QUERY_KEY.depth,
|
recordGqlFields: CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.fields,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createManyRecords: createManyActivityTargets } =
|
const { createManyRecords: createManyActivityTargets } =
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
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 { Activity } from '@/activities/types/Activity';
|
||||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
@ -104,15 +104,16 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
|||||||
return a.createdAt > b.createdAt ? -1 : 1;
|
return a.createdAt > b.createdAt ? -1 : 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
|
||||||
|
findActivitiesOperationSignatureFactory({ objectMetadataItems });
|
||||||
|
|
||||||
upsertFindManyActivitiesInCache({
|
upsertFindManyActivitiesInCache({
|
||||||
objectRecordsToOverwrite: filteredActivities,
|
objectRecordsToOverwrite: filteredActivities,
|
||||||
queryVariables: {
|
queryVariables: {
|
||||||
...nextFindManyActivitiesQueryFilter,
|
...nextFindManyActivitiesQueryFilter,
|
||||||
orderBy: { createdAt: 'DescNullsFirst' },
|
orderBy: { createdAt: 'DescNullsFirst' },
|
||||||
},
|
},
|
||||||
depth: FIND_MANY_ACTIVITIES_QUERY_KEY.depth,
|
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
|
||||||
queryFields:
|
|
||||||
FIND_MANY_ACTIVITIES_QUERY_KEY.fieldsFactory?.(objectMetadataItems),
|
|
||||||
computeReferences: true,
|
computeReferences: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -27,14 +27,10 @@ export const Notes = ({
|
|||||||
}: {
|
}: {
|
||||||
targetableObject: ActivityTargetableObject;
|
targetableObject: ActivityTargetableObject;
|
||||||
}) => {
|
}) => {
|
||||||
const { notes, initialized } = useNotes(targetableObject);
|
const { notes } = useNotes(targetableObject);
|
||||||
|
|
||||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notes?.length === 0) {
|
if (notes?.length === 0) {
|
||||||
return (
|
return (
|
||||||
<AnimatedPlaceholderEmptyContainer>
|
<AnimatedPlaceholderEmptyContainer>
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableE
|
|||||||
jest.mock('@/activities/hooks/useActivities', () => ({
|
jest.mock('@/activities/hooks/useActivities', () => ({
|
||||||
useActivities: jest.fn(() => ({
|
useActivities: jest.fn(() => ({
|
||||||
activities: [{ id: '1', content: 'Example Note' }],
|
activities: [{ id: '1', content: 'Example Note' }],
|
||||||
initialized: true,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
@ -29,7 +28,7 @@ jest.mock('recoil', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('useNotes', () => {
|
describe('useNotes', () => {
|
||||||
it('should return notes, initialized, and loading as expected', () => {
|
it('should return notes, and loading as expected', () => {
|
||||||
const mockTargetableObject: ActivityTargetableObject = {
|
const mockTargetableObject: ActivityTargetableObject = {
|
||||||
id: '1',
|
id: '1',
|
||||||
targetObjectNameSingular: 'Example Target',
|
targetObjectNameSingular: 'Example Target',
|
||||||
@ -39,7 +38,6 @@ describe('useNotes', () => {
|
|||||||
expect(result.current.notes).toEqual([
|
expect(result.current.notes).toEqual([
|
||||||
{ id: '1', content: 'Example Note' },
|
{ id: '1', content: 'Example Note' },
|
||||||
]);
|
]);
|
||||||
expect(result.current.initialized).toBe(true);
|
|
||||||
expect(result.current.loading).toBe(false);
|
expect(result.current.loading).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useActivities } from '@/activities/hooks/useActivities';
|
|||||||
import { currentNotesQueryVariablesState } from '@/activities/notes/states/currentNotesQueryVariablesState';
|
import { currentNotesQueryVariablesState } from '@/activities/notes/states/currentNotesQueryVariablesState';
|
||||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/constants/FindManyTimelineActivitiesOrderBy';
|
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/constants/FindManyTimelineActivitiesOrderBy';
|
||||||
import { Note } from '@/activities/types/Note';
|
import { Note } from '@/activities/types/Note';
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
import { ActivityTargetableObject } from '../../types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '../../types/ActivityTargetableEntity';
|
||||||
@ -18,11 +18,11 @@ export const useNotes = (targetableObject: ActivityTargetableObject) => {
|
|||||||
type: { eq: 'Note' },
|
type: { eq: 'Note' },
|
||||||
},
|
},
|
||||||
orderBy: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
orderBy: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
||||||
}) as ObjectRecordQueryVariables,
|
}) as RecordGqlOperationVariables,
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { activities, initialized, loading } = useActivities({
|
const { activities, loading } = useActivities({
|
||||||
activitiesFilters: notesQueryVariables.filter ?? {},
|
activitiesFilters: notesQueryVariables.filter ?? {},
|
||||||
activitiesOrderByVariables: notesQueryVariables.orderBy ?? {},
|
activitiesOrderByVariables: notesQueryVariables.orderBy ?? {},
|
||||||
targetableObjects: [targetableObject],
|
targetableObjects: [targetableObject],
|
||||||
@ -44,7 +44,6 @@ export const useNotes = (targetableObject: ActivityTargetableObject) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
notes: activities as Note[],
|
notes: activities as Note[],
|
||||||
initialized,
|
|
||||||
loading,
|
loading,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { atom } from 'recoil';
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
|
|
||||||
export const currentNotesQueryVariablesState =
|
export const currentNotesQueryVariablesState =
|
||||||
atom<ObjectRecordQueryVariables | null>({
|
atom<RecordGqlOperationVariables | null>({
|
||||||
default: null,
|
default: null,
|
||||||
key: 'currentNotesQueryVariablesState',
|
key: 'currentNotesQueryVariablesState',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { QueryKey } from '@/object-record/query-keys/types/QueryKey';
|
|
||||||
|
|
||||||
export const CREATE_ONE_ACTIVITY_QUERY_KEY: QueryKey = {
|
|
||||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
|
||||||
variables: {},
|
|
||||||
fields: {
|
|
||||||
id: true,
|
|
||||||
__typename: true,
|
|
||||||
createdAt: true,
|
|
||||||
updatedAt: true,
|
|
||||||
author: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
__typename: true,
|
|
||||||
},
|
|
||||||
authorId: true,
|
|
||||||
assigneeId: true,
|
|
||||||
assignee: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
__typename: true,
|
|
||||||
},
|
|
||||||
comments: true,
|
|
||||||
attachments: true,
|
|
||||||
body: true,
|
|
||||||
title: true,
|
|
||||||
completedAt: true,
|
|
||||||
dueAt: true,
|
|
||||||
reminderAt: true,
|
|
||||||
type: true,
|
|
||||||
},
|
|
||||||
depth: 1,
|
|
||||||
};
|
|
||||||
@ -19,7 +19,6 @@ import { useDeleteRecordFromCache } from '@/object-record/cache/hooks/useDeleteR
|
|||||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { getChildRelationArray } from '@/object-record/utils/getChildRelationArray';
|
|
||||||
import { mapToRecordId } from '@/object-record/utils/mapToObjectId';
|
import { mapToRecordId } from '@/object-record/utils/mapToObjectId';
|
||||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||||
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
|
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
|
||||||
@ -85,10 +84,6 @@ export const ActivityActionBar = () => {
|
|||||||
.getLoadable(recordStoreFamilyState(activityIdInDrawer))
|
.getLoadable(recordStoreFamilyState(activityIdInDrawer))
|
||||||
.getValue() as Activity;
|
.getValue() as Activity;
|
||||||
|
|
||||||
const activityTargets = getChildRelationArray({
|
|
||||||
childRelation: activity.activityTargets,
|
|
||||||
});
|
|
||||||
|
|
||||||
setIsRightDrawerOpen(false);
|
setIsRightDrawerOpen(false);
|
||||||
|
|
||||||
if (!isNonEmptyString(viewableActivityId)) {
|
if (!isNonEmptyString(viewableActivityId)) {
|
||||||
@ -103,10 +98,10 @@ export const ActivityActionBar = () => {
|
|||||||
|
|
||||||
if (isNonEmptyString(activityIdInDrawer)) {
|
if (isNonEmptyString(activityIdInDrawer)) {
|
||||||
const activityTargetIdsToDelete: string[] =
|
const activityTargetIdsToDelete: string[] =
|
||||||
activityTargets.map(mapToRecordId) ?? [];
|
activity.activityTargets.map(mapToRecordId) ?? [];
|
||||||
|
|
||||||
deleteActivityFromCache(activity);
|
deleteActivityFromCache(activity);
|
||||||
activityTargets.forEach((activityTarget: ActivityTarget) => {
|
activity.activityTargets.forEach((activityTarget: ActivityTarget) => {
|
||||||
deleteActivityTargetFromCache(activityTarget);
|
deleteActivityTargetFromCache(activityTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@ export const CurrentUserDueTaskCountEffect = () => {
|
|||||||
|
|
||||||
const { records: tasks } = useFindManyRecords<Activity>({
|
const { records: tasks } = useFindManyRecords<Activity>({
|
||||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
depth: 0,
|
|
||||||
filter: {
|
filter: {
|
||||||
type: { eq: 'Task' },
|
type: { eq: 'Task' },
|
||||||
completedAt: { is: 'NULL' },
|
completedAt: { is: 'NULL' },
|
||||||
|
|||||||
@ -40,7 +40,6 @@ export const TaskGroups = ({
|
|||||||
upcomingTasks,
|
upcomingTasks,
|
||||||
unscheduledTasks,
|
unscheduledTasks,
|
||||||
completedTasks,
|
completedTasks,
|
||||||
initialized,
|
|
||||||
} = useTasks({
|
} = useTasks({
|
||||||
filterDropdownId: filterDropdownId,
|
filterDropdownId: filterDropdownId,
|
||||||
targetableObjects: targetableObjects ?? [],
|
targetableObjects: targetableObjects ?? [],
|
||||||
@ -51,10 +50,6 @@ export const TaskGroups = ({
|
|||||||
const { activeTabIdState } = useTabList(TASKS_TAB_LIST_COMPONENT_ID);
|
const { activeTabIdState } = useTabList(TASKS_TAB_LIST_COMPONENT_ID);
|
||||||
const activeTabId = useRecoilValue(activeTabIdState);
|
const activeTabId = useRecoilValue(activeTabIdState);
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(activeTabId !== 'done' &&
|
(activeTabId !== 'done' &&
|
||||||
todayOrPreviousTasks?.length === 0 &&
|
todayOrPreviousTasks?.length === 0 &&
|
||||||
|
|||||||
@ -49,7 +49,6 @@ const useActivitiesMock = jest.fn(
|
|||||||
activities: isCompletedFilter
|
activities: isCompletedFilter
|
||||||
? completedTasks
|
? completedTasks
|
||||||
: [...todayOrPreviousTasks, ...unscheduledTasks],
|
: [...todayOrPreviousTasks, ...unscheduledTasks],
|
||||||
initialized: true,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -79,7 +78,6 @@ describe('useTasks', () => {
|
|||||||
upcomingTasks: [],
|
upcomingTasks: [],
|
||||||
unscheduledTasks,
|
unscheduledTasks,
|
||||||
completedTasks,
|
completedTasks,
|
||||||
initialized: true,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { isNonEmptyArray } from '@sniptt/guards';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
@ -9,8 +8,8 @@ import { currentIncompleteTaskQueryVariablesState } from '@/activities/tasks/sta
|
|||||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/constants/FindManyTimelineActivitiesOrderBy';
|
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/constants/FindManyTimelineActivitiesOrderBy';
|
||||||
import { Activity } from '@/activities/types/Activity';
|
import { Activity } from '@/activities/types/Activity';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
|
||||||
import { parseDate } from '~/utils/date-utils';
|
import { parseDate } from '~/utils/date-utils';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
@ -41,8 +40,6 @@ export const useTasks = ({
|
|||||||
[selectedFilter],
|
[selectedFilter],
|
||||||
);
|
);
|
||||||
|
|
||||||
const skipActivityTargets = !isNonEmptyArray(targetableObjects);
|
|
||||||
|
|
||||||
const completedQueryVariables = useMemo(
|
const completedQueryVariables = useMemo(
|
||||||
() =>
|
() =>
|
||||||
({
|
({
|
||||||
@ -52,7 +49,7 @@ export const useTasks = ({
|
|||||||
...assigneeIdFilter,
|
...assigneeIdFilter,
|
||||||
},
|
},
|
||||||
orderBy: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
orderBy: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
||||||
}) as ObjectRecordQueryVariables,
|
}) as RecordGqlOperationVariables,
|
||||||
[assigneeIdFilter],
|
[assigneeIdFilter],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -65,7 +62,7 @@ export const useTasks = ({
|
|||||||
...assigneeIdFilter,
|
...assigneeIdFilter,
|
||||||
},
|
},
|
||||||
orderBy: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
orderBy: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
||||||
}) as ObjectRecordQueryVariables,
|
}) as RecordGqlOperationVariables,
|
||||||
[assigneeIdFilter],
|
[assigneeIdFilter],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -110,24 +107,16 @@ export const useTasks = ({
|
|||||||
setCurrentIncompleteTaskQueryVariables,
|
setCurrentIncompleteTaskQueryVariables,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const {
|
const { activities: completeTasksData } = useActivities({
|
||||||
activities: completeTasksData,
|
|
||||||
initialized: initializedCompleteTasks,
|
|
||||||
} = useActivities({
|
|
||||||
targetableObjects,
|
targetableObjects,
|
||||||
activitiesFilters: completedQueryVariables.filter ?? {},
|
activitiesFilters: completedQueryVariables.filter ?? {},
|
||||||
activitiesOrderByVariables: completedQueryVariables.orderBy ?? {},
|
activitiesOrderByVariables: completedQueryVariables.orderBy ?? {},
|
||||||
skipActivityTargets,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const { activities: incompleteTaskData } = useActivities({
|
||||||
activities: incompleteTaskData,
|
|
||||||
initialized: initializedIncompleteTasks,
|
|
||||||
} = useActivities({
|
|
||||||
targetableObjects,
|
targetableObjects,
|
||||||
activitiesFilters: incompleteQueryVariables.filter ?? {},
|
activitiesFilters: incompleteQueryVariables.filter ?? {},
|
||||||
activitiesOrderByVariables: incompleteQueryVariables.orderBy ?? {},
|
activitiesOrderByVariables: incompleteQueryVariables.orderBy ?? {},
|
||||||
skipActivityTargets,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const todayOrPreviousTasks = incompleteTaskData?.filter((task) => {
|
const todayOrPreviousTasks = incompleteTaskData?.filter((task) => {
|
||||||
@ -159,6 +148,5 @@ export const useTasks = ({
|
|||||||
upcomingTasks: (upcomingTasks ?? []) as Activity[],
|
upcomingTasks: (upcomingTasks ?? []) as Activity[],
|
||||||
unscheduledTasks: (unscheduledTasks ?? []) as Activity[],
|
unscheduledTasks: (unscheduledTasks ?? []) as Activity[],
|
||||||
completedTasks: (completedTasks ?? []) as Activity[],
|
completedTasks: (completedTasks ?? []) as Activity[],
|
||||||
initialized: initializedCompleteTasks && initializedIncompleteTasks,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { atom } from 'recoil';
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
|
|
||||||
export const currentCompletedTaskQueryVariablesState =
|
export const currentCompletedTaskQueryVariablesState =
|
||||||
atom<ObjectRecordQueryVariables | null>({
|
atom<RecordGqlOperationVariables | null>({
|
||||||
default: null,
|
default: null,
|
||||||
key: 'currentCompletedTaskQueryVariablesState',
|
key: 'currentCompletedTaskQueryVariablesState',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { atom } from 'recoil';
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
|
|
||||||
export const currentIncompleteTaskQueryVariablesState =
|
export const currentIncompleteTaskQueryVariablesState =
|
||||||
atom<ObjectRecordQueryVariables | null>({
|
atom<RecordGqlOperationVariables | null>({
|
||||||
default: null,
|
default: null,
|
||||||
key: 'currentIncompleteTaskQueryVariablesState',
|
key: 'currentIncompleteTaskQueryVariablesState',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { TimelineCreateButtonGroup } from '@/activities/timeline/components/TimelineCreateButtonGroup';
|
import { TimelineCreateButtonGroup } from '@/activities/timeline/components/TimelineCreateButtonGroup';
|
||||||
import { timelineActivitiesNetworkingState } from '@/activities/timeline/states/timelineActivitiesNetworkingState';
|
import { timelineActivitiesForGroupState } from '@/activities/timeline/states/timelineActivitiesForGroupState';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||||
import {
|
import {
|
||||||
@ -32,20 +32,11 @@ export const Timeline = ({
|
|||||||
}: {
|
}: {
|
||||||
targetableObject: ActivityTargetableObject;
|
targetableObject: ActivityTargetableObject;
|
||||||
}) => {
|
}) => {
|
||||||
const { initialized, noActivities } = useRecoilValue(
|
const timelineActivitiesForGroup = useRecoilValue(
|
||||||
timelineActivitiesNetworkingState,
|
timelineActivitiesForGroupState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const showEmptyState = noActivities;
|
if (timelineActivitiesForGroup.length === 0) {
|
||||||
|
|
||||||
const showLoadingState = !initialized;
|
|
||||||
|
|
||||||
if (showLoadingState) {
|
|
||||||
// TODO: Display a beautiful loading page
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showEmptyState) {
|
|
||||||
return (
|
return (
|
||||||
<AnimatedPlaceholderEmptyContainer>
|
<AnimatedPlaceholderEmptyContainer>
|
||||||
<AnimatedPlaceholder type="emptyTimeline" />
|
<AnimatedPlaceholder type="emptyTimeline" />
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/co
|
|||||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline/states/objectShowPageTargetableObjectIdState';
|
import { objectShowPageTargetableObjectState } from '@/activities/timeline/states/objectShowPageTargetableObjectIdState';
|
||||||
import { timelineActivitiesFammilyState } from '@/activities/timeline/states/timelineActivitiesFamilyState';
|
import { timelineActivitiesFammilyState } from '@/activities/timeline/states/timelineActivitiesFamilyState';
|
||||||
import { timelineActivitiesForGroupState } from '@/activities/timeline/states/timelineActivitiesForGroupState';
|
import { timelineActivitiesForGroupState } from '@/activities/timeline/states/timelineActivitiesForGroupState';
|
||||||
import { timelineActivitiesNetworkingState } from '@/activities/timeline/states/timelineActivitiesNetworkingState';
|
|
||||||
import { timelineActivityWithoutTargetsFamilyState } from '@/activities/timeline/states/timelineActivityWithoutTargetsFamilyState';
|
import { timelineActivityWithoutTargetsFamilyState } from '@/activities/timeline/states/timelineActivityWithoutTargetsFamilyState';
|
||||||
import { Activity } from '@/activities/types/Activity';
|
import { Activity } from '@/activities/types/Activity';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
@ -27,16 +26,13 @@ export const TimelineQueryEffect = ({
|
|||||||
setTimelineTargetableObject(targetableObject);
|
setTimelineTargetableObject(targetableObject);
|
||||||
}, [targetableObject, setTimelineTargetableObject]);
|
}, [targetableObject, setTimelineTargetableObject]);
|
||||||
|
|
||||||
const { activities, initialized, noActivities } = useActivities({
|
const { activities } = useActivities({
|
||||||
targetableObjects: [targetableObject],
|
targetableObjects: [targetableObject],
|
||||||
activitiesFilters: {},
|
activitiesFilters: {},
|
||||||
activitiesOrderByVariables: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
activitiesOrderByVariables: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,
|
||||||
skip: !isDefined(targetableObject),
|
skip: !isDefined(targetableObject),
|
||||||
});
|
});
|
||||||
|
|
||||||
const [timelineActivitiesNetworking, setTimelineActivitiesNetworking] =
|
|
||||||
useRecoilState(timelineActivitiesNetworkingState);
|
|
||||||
|
|
||||||
const [timelineActivitiesForGroup, setTimelineActivitiesForGroup] =
|
const [timelineActivitiesForGroup, setTimelineActivitiesForGroup] =
|
||||||
useRecoilState(timelineActivitiesForGroupState);
|
useRecoilState(timelineActivitiesForGroupState);
|
||||||
|
|
||||||
@ -49,6 +45,7 @@ export const TimelineQueryEffect = ({
|
|||||||
...activities.map((activity) => ({
|
...activities.map((activity) => ({
|
||||||
id: activity.id,
|
id: activity.id,
|
||||||
createdAt: activity.createdAt,
|
createdAt: activity.createdAt,
|
||||||
|
__typename: activity.__typename,
|
||||||
})),
|
})),
|
||||||
].sort(sortObjectRecordByDateField('createdAt', 'DescNullsLast'));
|
].sort(sortObjectRecordByDateField('createdAt', 'DescNullsLast'));
|
||||||
|
|
||||||
@ -59,23 +56,9 @@ export const TimelineQueryEffect = ({
|
|||||||
if (!isDeeplyEqual(activitiesForGroup, timelineActivitiesForGroupSorted)) {
|
if (!isDeeplyEqual(activitiesForGroup, timelineActivitiesForGroupSorted)) {
|
||||||
setTimelineActivitiesForGroup(activitiesForGroup);
|
setTimelineActivitiesForGroup(activitiesForGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
!isDeeplyEqual(timelineActivitiesNetworking.initialized, initialized) ||
|
|
||||||
!isDeeplyEqual(timelineActivitiesNetworking.noActivities, noActivities)
|
|
||||||
) {
|
|
||||||
setTimelineActivitiesNetworking({
|
|
||||||
initialized,
|
|
||||||
noActivities,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [
|
}, [
|
||||||
activities,
|
activities,
|
||||||
initialized,
|
|
||||||
noActivities,
|
|
||||||
setTimelineActivitiesNetworking,
|
|
||||||
targetableObject,
|
targetableObject,
|
||||||
timelineActivitiesNetworking,
|
|
||||||
timelineActivitiesForGroup,
|
timelineActivitiesForGroup,
|
||||||
setTimelineActivitiesForGroup,
|
setTimelineActivitiesForGroup,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||||
|
|
||||||
export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: OrderByField = {
|
export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: RecordGqlOperationOrderBy =
|
||||||
createdAt: 'DescNullsFirst',
|
{
|
||||||
};
|
createdAt: 'DescNullsFirst',
|
||||||
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
||||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||||
|
|
||||||
@ -28,15 +28,10 @@ export const useTimelineActivities = ({
|
|||||||
}
|
}
|
||||||
}, [targetableObject, setObjectShowPageTargetableObject]);
|
}, [targetableObject, setObjectShowPageTargetableObject]);
|
||||||
|
|
||||||
const {
|
const { activityTargets, loadingActivityTargets } =
|
||||||
activityTargets,
|
useActivityTargetsForTargetableObject({
|
||||||
loadingActivityTargets,
|
targetableObject,
|
||||||
initialized: initializedActivityTargets,
|
});
|
||||||
} = useActivityTargetsForTargetableObject({
|
|
||||||
targetableObject,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState(false);
|
|
||||||
|
|
||||||
const activityIds = Array.from(
|
const activityIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
@ -65,33 +60,18 @@ export const useTimelineActivities = ({
|
|||||||
onCompleted: useRecoilCallback(
|
onCompleted: useRecoilCallback(
|
||||||
({ set }) =>
|
({ set }) =>
|
||||||
(activities) => {
|
(activities) => {
|
||||||
if (!initialized) {
|
|
||||||
setInitialized(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const activity of activities) {
|
for (const activity of activities) {
|
||||||
set(recordStoreFamilyState(activity.id), activity);
|
set(recordStoreFamilyState(activity.id), activity);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[initialized],
|
[],
|
||||||
),
|
),
|
||||||
depth: 3,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const noActivityTargets =
|
|
||||||
initializedActivityTargets && !isNonEmptyArray(activityTargets);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (noActivityTargets) {
|
|
||||||
setInitialized(true);
|
|
||||||
}
|
|
||||||
}, [noActivityTargets]);
|
|
||||||
|
|
||||||
const loading = loadingActivities || loadingActivityTargets;
|
const loading = loadingActivities || loadingActivityTargets;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activities,
|
activities,
|
||||||
loading,
|
loading,
|
||||||
initialized,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import { createState } from 'twenty-ui';
|
|
||||||
|
|
||||||
export const timelineActivitiesNetworkingState = createState<{
|
|
||||||
initialized: boolean;
|
|
||||||
noActivities: boolean;
|
|
||||||
}>({
|
|
||||||
key: 'timelineActivitiesNetworkingState',
|
|
||||||
defaultValue: {
|
|
||||||
initialized: false,
|
|
||||||
noActivities: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -1,7 +1,10 @@
|
|||||||
import { Activity } from '@/activities/types/Activity';
|
import { Activity } from '@/activities/types/Activity';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export type ActivityForActivityGroup = Pick<Activity, 'id' | 'createdAt'>;
|
export type ActivityForActivityGroup = Pick<
|
||||||
|
Activity,
|
||||||
|
'id' | 'createdAt' | '__typename'
|
||||||
|
>;
|
||||||
|
|
||||||
export type ActivityGroup = {
|
export type ActivityGroup = {
|
||||||
month: number;
|
month: number;
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||||
|
|
||||||
|
// Todo: this should be replace by the operationSignatureFactory pattern
|
||||||
export const makeTimelineActivitiesQueryVariables = ({
|
export const makeTimelineActivitiesQueryVariables = ({
|
||||||
activityIds,
|
activityIds,
|
||||||
}: {
|
}: {
|
||||||
activityIds: string[];
|
activityIds: string[];
|
||||||
}): ObjectRecordQueryVariables => {
|
}): RecordGqlOperationVariables => {
|
||||||
return {
|
return {
|
||||||
filter: {
|
filter: {
|
||||||
id: {
|
id: {
|
||||||
|
|||||||
@ -12,4 +12,5 @@ export type TimelineActivity = {
|
|||||||
linkedRecordCachedName: string;
|
linkedRecordCachedName: string;
|
||||||
linkedRecordId: string;
|
linkedRecordId: string;
|
||||||
linkedObjectMetadataId: string;
|
linkedObjectMetadataId: string;
|
||||||
|
__typename: 'TimelineActivity';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,14 @@ import { WorkspaceMember } from '~/generated-metadata/graphql';
|
|||||||
|
|
||||||
export type ActivityForEditor = Pick<
|
export type ActivityForEditor = Pick<
|
||||||
Activity,
|
Activity,
|
||||||
'id' | 'title' | 'body' | 'type' | 'completedAt' | 'dueAt' | 'updatedAt'
|
| 'id'
|
||||||
|
| 'title'
|
||||||
|
| 'body'
|
||||||
|
| 'type'
|
||||||
|
| 'completedAt'
|
||||||
|
| 'dueAt'
|
||||||
|
| 'updatedAt'
|
||||||
|
| '__typename'
|
||||||
> & {
|
> & {
|
||||||
comments?: Comment[];
|
comments?: Comment[];
|
||||||
} & {
|
} & {
|
||||||
|
|||||||
@ -12,4 +12,5 @@ export type ActivityTarget = {
|
|||||||
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
|
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
|
||||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
__typename: 'ActivityTarget';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,4 +7,5 @@ export type Comment = {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
activityId: string;
|
activityId: string;
|
||||||
author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
|
author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
|
||||||
|
__typename: 'Comment';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { Reference, StoreObject } from '@apollo/client';
|
import { Reference, StoreObject } from '@apollo/client';
|
||||||
import { ReadFieldFunction } from '@apollo/client/cache/core/types/common';
|
import { ReadFieldFunction } from '@apollo/client/cache/core/types/common';
|
||||||
|
|
||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
|
||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||||
|
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { sortAsc, sortDesc, sortNullsFirst, sortNullsLast } from '~/utils/sort';
|
import { sortAsc, sortDesc, sortNullsFirst, sortNullsLast } from '~/utils/sort';
|
||||||
|
|
||||||
@ -12,8 +12,8 @@ export const sortCachedObjectEdges = ({
|
|||||||
orderBy,
|
orderBy,
|
||||||
readCacheField,
|
readCacheField,
|
||||||
}: {
|
}: {
|
||||||
edges: CachedObjectRecordEdge[];
|
edges: RecordGqlRefEdge[];
|
||||||
orderBy: OrderByField;
|
orderBy: RecordGqlOperationOrderBy;
|
||||||
readCacheField: ReadFieldFunction;
|
readCacheField: ReadFieldFunction;
|
||||||
}) => {
|
}) => {
|
||||||
const [orderByFieldName, orderByFieldValue] = Object.entries(orderBy)[0];
|
const [orderByFieldName, orderByFieldValue] = Object.entries(orderBy)[0];
|
||||||
@ -23,7 +23,7 @@ export const sortCachedObjectEdges = ({
|
|||||||
: Object.entries(orderByFieldValue)[0];
|
: Object.entries(orderByFieldValue)[0];
|
||||||
|
|
||||||
const readFieldValueToSort = (
|
const readFieldValueToSort = (
|
||||||
edge: CachedObjectRecordEdge,
|
edge: RecordGqlRefEdge,
|
||||||
): string | number | null => {
|
): string | number | null => {
|
||||||
const recordFromCache = edge.node;
|
const recordFromCache = edge.node;
|
||||||
const fieldValue =
|
const fieldValue =
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { ApolloCache, StoreObject } from '@apollo/client';
|
import { ApolloCache, StoreObject } from '@apollo/client';
|
||||||
|
|
||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||||
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -48,7 +48,7 @@ export const triggerAttachRelationOptimisticEffect = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fieldValueisObjectRecordConnectionWithRefs) {
|
if (fieldValueisObjectRecordConnectionWithRefs) {
|
||||||
const nextEdges: CachedObjectRecordEdge[] = [
|
const nextEdges: RecordGqlRefEdge[] = [
|
||||||
...targetRecordFieldValue.edges,
|
...targetRecordFieldValue.edges,
|
||||||
{
|
{
|
||||||
__typename: `${sourceRecordTypeName}Edge`,
|
__typename: `${sourceRecordTypeName}Edge`,
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import { ApolloCache, StoreObject } from '@apollo/client';
|
|||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
|
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
|
||||||
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
|
||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||||
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
||||||
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: for now new records are added to all cached record lists, no matter what the variables (filters, orderBy, etc.) are.
|
TODO: for now new records are added to all cached record lists, no matter what the variables (filters, orderBy, etc.) are.
|
||||||
@ -21,7 +21,7 @@ export const triggerCreateRecordsOptimisticEffect = ({
|
|||||||
}: {
|
}: {
|
||||||
cache: ApolloCache<unknown>;
|
cache: ApolloCache<unknown>;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
recordsToCreate: CachedObjectRecord[];
|
recordsToCreate: RecordGqlNode[];
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
}) => {
|
}) => {
|
||||||
recordsToCreate.forEach((record) =>
|
recordsToCreate.forEach((record) =>
|
||||||
@ -56,7 +56,7 @@ export const triggerCreateRecordsOptimisticEffect = ({
|
|||||||
|
|
||||||
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
||||||
|
|
||||||
const rootQueryCachedRecordEdges = readField<CachedObjectRecordEdge[]>(
|
const rootQueryCachedRecordEdges = readField<RecordGqlRefEdge[]>(
|
||||||
'edges',
|
'edges',
|
||||||
rootQueryCachedObjectRecordConnection,
|
rootQueryCachedObjectRecordConnection,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { ApolloCache, StoreObject } from '@apollo/client';
|
import { ApolloCache, StoreObject } from '@apollo/client';
|
||||||
|
|
||||||
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
|
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
|
||||||
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
|
||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
|
||||||
import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRecordQueryVariables';
|
import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRecordQueryVariables';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||||
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
|
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
|||||||
}: {
|
}: {
|
||||||
cache: ApolloCache<unknown>;
|
cache: ApolloCache<unknown>;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
recordsToDelete: CachedObjectRecord[];
|
recordsToDelete: RecordGqlNode[];
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
}) => {
|
}) => {
|
||||||
cache.modify<StoreObject>({
|
cache.modify<StoreObject>({
|
||||||
@ -45,7 +45,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
|||||||
|
|
||||||
const recordIdsToDelete = recordsToDelete.map(({ id }) => id);
|
const recordIdsToDelete = recordsToDelete.map(({ id }) => id);
|
||||||
|
|
||||||
const cachedEdges = readField<CachedObjectRecordEdge[]>(
|
const cachedEdges = readField<RecordGqlRefEdge[]>(
|
||||||
'edges',
|
'edges',
|
||||||
rootQueryCachedObjectRecordConnection,
|
rootQueryCachedObjectRecordConnection,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import { ApolloCache, StoreObject } from '@apollo/client';
|
|||||||
|
|
||||||
import { sortCachedObjectEdges } from '@/apollo/optimistic-effect/utils/sortCachedObjectEdges';
|
import { sortCachedObjectEdges } from '@/apollo/optimistic-effect/utils/sortCachedObjectEdges';
|
||||||
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
|
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
|
||||||
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
|
||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
|
||||||
import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRecordQueryVariables';
|
import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRecordQueryVariables';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||||
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
||||||
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';
|
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
|
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
|
||||||
@ -23,8 +23,8 @@ export const triggerUpdateRecordOptimisticEffect = ({
|
|||||||
}: {
|
}: {
|
||||||
cache: ApolloCache<unknown>;
|
cache: ApolloCache<unknown>;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
currentRecord: CachedObjectRecord;
|
currentRecord: RecordGqlNode;
|
||||||
updatedRecord: CachedObjectRecord;
|
updatedRecord: RecordGqlNode;
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
}) => {
|
}) => {
|
||||||
triggerUpdateRelationsOptimisticEffect({
|
triggerUpdateRelationsOptimisticEffect({
|
||||||
@ -58,8 +58,7 @@ export const triggerUpdateRecordOptimisticEffect = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const rootQueryCurrentEdges =
|
const rootQueryCurrentEdges =
|
||||||
readField<CachedObjectRecordEdge[]>('edges', rootQueryConnection) ??
|
readField<RecordGqlRefEdge[]>('edges', rootQueryConnection) ?? [];
|
||||||
[];
|
|
||||||
|
|
||||||
let rootQueryNextEdges = [...rootQueryCurrentEdges];
|
let rootQueryNextEdges = [...rootQueryCurrentEdges];
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,13 @@ import { getRelationDefinition } from '@/apollo/optimistic-effect/utils/getRelat
|
|||||||
import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerAttachRelationOptimisticEffect';
|
import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerAttachRelationOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
||||||
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
||||||
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
|
||||||
import { CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH } from '@/apollo/types/coreObjectNamesToDeleteOnRelationDetach';
|
import { CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH } from '@/apollo/types/coreObjectNamesToDeleteOnRelationDetach';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { isObjectRecordConnection } from '@/object-record/cache/utils/isObjectRecordConnection';
|
import { isObjectRecordConnection } from '@/object-record/cache/utils/isObjectRecordConnection';
|
||||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
@ -23,8 +24,8 @@ export const triggerUpdateRelationsOptimisticEffect = ({
|
|||||||
}: {
|
}: {
|
||||||
cache: ApolloCache<unknown>;
|
cache: ApolloCache<unknown>;
|
||||||
sourceObjectMetadataItem: ObjectMetadataItem;
|
sourceObjectMetadataItem: ObjectMetadataItem;
|
||||||
currentSourceRecord: CachedObjectRecord | null;
|
currentSourceRecord: ObjectRecord | null;
|
||||||
updatedSourceRecord: CachedObjectRecord | null;
|
updatedSourceRecord: ObjectRecord | null;
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
}) => {
|
}) => {
|
||||||
return sourceObjectMetadataItem.fields.forEach(
|
return sourceObjectMetadataItem.fields.forEach(
|
||||||
@ -56,13 +57,13 @@ export const triggerUpdateRelationsOptimisticEffect = ({
|
|||||||
relationDefinition;
|
relationDefinition;
|
||||||
|
|
||||||
const currentFieldValueOnSourceRecord:
|
const currentFieldValueOnSourceRecord:
|
||||||
| ObjectRecordConnection
|
| RecordGqlConnection
|
||||||
| CachedObjectRecord
|
| RecordGqlNode
|
||||||
| null = currentSourceRecord?.[fieldMetadataItemOnSourceRecord.name];
|
| null = currentSourceRecord?.[fieldMetadataItemOnSourceRecord.name];
|
||||||
|
|
||||||
const updatedFieldValueOnSourceRecord:
|
const updatedFieldValueOnSourceRecord:
|
||||||
| ObjectRecordConnection
|
| RecordGqlConnection
|
||||||
| CachedObjectRecord
|
| RecordGqlNode
|
||||||
| null = updatedSourceRecord?.[fieldMetadataItemOnSourceRecord.name];
|
| null = updatedSourceRecord?.[fieldMetadataItemOnSourceRecord.name];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -85,7 +86,7 @@ export const triggerUpdateRelationsOptimisticEffect = ({
|
|||||||
const targetRecordsToDetachFrom =
|
const targetRecordsToDetachFrom =
|
||||||
currentFieldValueOnSourceRecordIsARecordConnection
|
currentFieldValueOnSourceRecordIsARecordConnection
|
||||||
? currentFieldValueOnSourceRecord.edges.map(
|
? currentFieldValueOnSourceRecord.edges.map(
|
||||||
({ node }) => node as CachedObjectRecord,
|
({ node }) => node as RecordGqlNode,
|
||||||
)
|
)
|
||||||
: [currentFieldValueOnSourceRecord].filter(isDefined);
|
: [currentFieldValueOnSourceRecord].filter(isDefined);
|
||||||
|
|
||||||
@ -98,7 +99,7 @@ export const triggerUpdateRelationsOptimisticEffect = ({
|
|||||||
const targetRecordsToAttachTo =
|
const targetRecordsToAttachTo =
|
||||||
updatedFieldValueOnSourceRecordIsARecordConnection
|
updatedFieldValueOnSourceRecordIsARecordConnection
|
||||||
? updatedFieldValueOnSourceRecord.edges.map(
|
? updatedFieldValueOnSourceRecord.edges.map(
|
||||||
({ node }) => node as CachedObjectRecord,
|
({ node }) => node as RecordGqlNode,
|
||||||
)
|
)
|
||||||
: [updatedFieldValueOnSourceRecord].filter(isDefined);
|
: [updatedFieldValueOnSourceRecord].filter(isDefined);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
||||||
|
|
||||||
export type CachedObjectRecord<T extends ObjectRecord = ObjectRecord> = T & {
|
|
||||||
__typename: string;
|
|
||||||
};
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
|
||||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
|
||||||
|
|
||||||
export type CachedObjectRecordConnection = Omit<
|
|
||||||
ObjectRecordConnection,
|
|
||||||
'edges'
|
|
||||||
> & {
|
|
||||||
edges: CachedObjectRecordEdge[];
|
|
||||||
};
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import { Reference } from '@apollo/client';
|
|
||||||
|
|
||||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
|
||||||
|
|
||||||
export type CachedObjectRecordEdge = Omit<ObjectRecordEdge, 'node'> & {
|
|
||||||
node: Reference;
|
|
||||||
};
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
|
|
||||||
export type CachedObjectRecordQueryVariables = Omit<
|
export type CachedObjectRecordQueryVariables = Omit<
|
||||||
ObjectRecordQueryVariables,
|
RecordGqlOperationVariables,
|
||||||
'limit'
|
'limit'
|
||||||
> & { first?: ObjectRecordQueryVariables['limit'] };
|
> & { first?: RecordGqlOperationVariables['limit'] };
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
export type Attachment = {
|
|
||||||
id: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
deletedAt: string | null;
|
|
||||||
};
|
|
||||||
@ -9,4 +9,5 @@ export type Favorite = {
|
|||||||
avatarType: AvatarType;
|
avatarType: AvatarType;
|
||||||
link: string;
|
link: string;
|
||||||
recordId: string;
|
recordId: string;
|
||||||
|
__typename: 'Favorite';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
|||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
import { currentUserState } from '@/auth/states/currentUserState';
|
||||||
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
|
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
|
|
||||||
@ -19,10 +20,18 @@ export const ObjectMetadataItemsLoadEffect = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isDeeplyEqual(objectMetadataItems, newObjectMetadataItems)) {
|
const toSetObjectMetadataItems = isUndefinedOrNull(currentUser)
|
||||||
setObjectMetadataItems(newObjectMetadataItems);
|
? getObjectMetadataItemsMock()
|
||||||
|
: newObjectMetadataItems;
|
||||||
|
if (!isDeeplyEqual(objectMetadataItems, toSetObjectMetadataItems)) {
|
||||||
|
setObjectMetadataItems(toSetObjectMetadataItems);
|
||||||
}
|
}
|
||||||
}, [newObjectMetadataItems, objectMetadataItems, setObjectMetadataItems]);
|
}, [
|
||||||
|
currentUser,
|
||||||
|
newObjectMetadataItems,
|
||||||
|
objectMetadataItems,
|
||||||
|
setObjectMetadataItems,
|
||||||
|
]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
||||||
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
|
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
|
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
|
||||||
@ -10,10 +9,8 @@ export const ObjectMetadataItemsProvider = ({
|
|||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren) => {
|
}: React.PropsWithChildren) => {
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
|
||||||
|
|
||||||
const shouldDisplayChildren =
|
const shouldDisplayChildren = objectMetadataItems.length > 0;
|
||||||
objectMetadataItems.length > 0 || !currentWorkspaceMember;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilte
|
|||||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { View } from '@/views/types/View';
|
||||||
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
|
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
|
||||||
|
|
||||||
export const ObjectMetadataNavItems = () => {
|
export const ObjectMetadataNavItems = () => {
|
||||||
@ -14,9 +14,7 @@ export const ObjectMetadataNavItems = () => {
|
|||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const currentPath = useLocation().pathname;
|
const currentPath = useLocation().pathname;
|
||||||
|
|
||||||
const { records: views } = usePrefetchedData<GraphQLView>(
|
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||||
PrefetchKey.AllViews,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
|
||||||
import { getOrderByFieldForObjectMetadataItem } from '@/object-metadata/utils/getObjectOrderByField';
|
import { getOrderByFieldForObjectMetadataItem } from '@/object-metadata/utils/getObjectOrderByField';
|
||||||
|
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||||
|
|
||||||
export const useGetObjectOrderByField = ({
|
export const useGetObjectOrderByField = ({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
@ -12,7 +12,9 @@ export const useGetObjectOrderByField = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getObjectOrderByField = (orderBy: OrderBy): OrderByField => {
|
const getObjectOrderByField = (
|
||||||
|
orderBy: OrderBy,
|
||||||
|
): RecordGqlOperationOrderBy => {
|
||||||
return getOrderByFieldForObjectMetadataItem(objectMetadataItem, orderBy);
|
return getOrderByFieldForObjectMetadataItem(objectMetadataItem, orderBy);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export const useObjectMetadataItem = ({
|
|||||||
}: ObjectMetadataItemIdentifier) => {
|
}: ObjectMetadataItemIdentifier) => {
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
|
|
||||||
|
// Todo: deprecate this logic as mocked objectMetadataItems are laod in ObjectMetadataItemsLoadEffect anyway
|
||||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||||
|
|
||||||
let objectMetadataItem = useRecoilValue(
|
let objectMetadataItem = useRecoilValue(
|
||||||
|
|||||||
@ -37,21 +37,10 @@ describe('mapFieldMetadataToGraphQLQuery', () => {
|
|||||||
lastName
|
lastName
|
||||||
}`);
|
}`);
|
||||||
});
|
});
|
||||||
it('should not return relation if depth is < 1', async () => {
|
|
||||||
const res = mapFieldMetadataToGraphQLQuery({
|
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
|
||||||
depth: 0,
|
|
||||||
field: personObjectMetadataItem.fields.find(
|
|
||||||
(field) => field.name === 'company',
|
|
||||||
)!,
|
|
||||||
});
|
|
||||||
expect(formatGQLString(res)).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return relation if it matches depth', async () => {
|
it('should return non relation subFields if relation', async () => {
|
||||||
const res = mapFieldMetadataToGraphQLQuery({
|
const res = mapFieldMetadataToGraphQLQuery({
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
objectMetadataItems: mockObjectMetadataItems,
|
||||||
depth: 1,
|
|
||||||
field: personObjectMetadataItem.fields.find(
|
field: personObjectMetadataItem.fields.find(
|
||||||
(field) => field.name === 'company',
|
(field) => field.name === 'company',
|
||||||
)!,
|
)!,
|
||||||
@ -83,168 +72,14 @@ accountOwnerId
|
|||||||
employees
|
employees
|
||||||
id
|
id
|
||||||
idealCustomerProfile
|
idealCustomerProfile
|
||||||
}`);
|
|
||||||
});
|
|
||||||
it('should return relation with all sub relations if it matches depth', async () => {
|
|
||||||
const res = mapFieldMetadataToGraphQLQuery({
|
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
|
||||||
depth: 2,
|
|
||||||
field: personObjectMetadataItem.fields.find(
|
|
||||||
(field) => field.name === 'company',
|
|
||||||
)!,
|
|
||||||
});
|
|
||||||
expect(formatGQLString(res)).toEqual(`company
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
xLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
accountOwner
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
colorScheme
|
|
||||||
name
|
|
||||||
{
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
locale
|
|
||||||
userId
|
|
||||||
avatarUrl
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
id
|
|
||||||
}
|
|
||||||
linkedinLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
attachments
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
updatedAt
|
|
||||||
createdAt
|
|
||||||
name
|
|
||||||
personId
|
|
||||||
activityId
|
|
||||||
companyId
|
|
||||||
id
|
|
||||||
authorId
|
|
||||||
type
|
|
||||||
fullPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
domainName
|
|
||||||
opportunities
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
personId
|
|
||||||
pointOfContactId
|
|
||||||
updatedAt
|
|
||||||
companyId
|
|
||||||
probability
|
|
||||||
closeDate
|
|
||||||
amount
|
|
||||||
{
|
|
||||||
amountMicros
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
annualRecurringRevenue
|
|
||||||
{
|
|
||||||
amountMicros
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
address
|
|
||||||
updatedAt
|
|
||||||
activityTargets
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
updatedAt
|
|
||||||
createdAt
|
|
||||||
personId
|
|
||||||
activityId
|
|
||||||
companyId
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
favorites
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
companyId
|
|
||||||
createdAt
|
|
||||||
personId
|
|
||||||
position
|
|
||||||
workspaceMemberId
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
people
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
xLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
city
|
|
||||||
email
|
|
||||||
jobTitle
|
|
||||||
name
|
|
||||||
{
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
phone
|
|
||||||
linkedinLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
updatedAt
|
|
||||||
avatarUrl
|
|
||||||
companyId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
name
|
|
||||||
accountOwnerId
|
|
||||||
employees
|
|
||||||
id
|
|
||||||
idealCustomerProfile
|
|
||||||
}`);
|
}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return GraphQL fields based on queryFields', async () => {
|
it('should return only return relation subFields that are in recordGqlFields', async () => {
|
||||||
const res = mapFieldMetadataToGraphQLQuery({
|
const res = mapFieldMetadataToGraphQLQuery({
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
objectMetadataItems: mockObjectMetadataItems,
|
||||||
depth: 2,
|
relationrecordFields: {
|
||||||
queryFields: {
|
accountOwner: { id: true, name: true },
|
||||||
accountOwner: true,
|
|
||||||
people: true,
|
people: true,
|
||||||
xLink: true,
|
xLink: true,
|
||||||
linkedinLink: true,
|
linkedinLink: true,
|
||||||
@ -274,17 +109,11 @@ xLink
|
|||||||
accountOwner
|
accountOwner
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
colorScheme
|
|
||||||
name
|
name
|
||||||
{
|
{
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
}
|
}
|
||||||
locale
|
|
||||||
userId
|
|
||||||
avatarUrl
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
linkedinLink
|
linkedinLink
|
||||||
|
|||||||
@ -15,209 +15,11 @@ if (!personObjectMetadataItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('mapObjectMetadataToGraphQLQuery', () => {
|
describe('mapObjectMetadataToGraphQLQuery', () => {
|
||||||
it('should return typename if depth < 0', async () => {
|
it('should query only specified recordGqlFields', async () => {
|
||||||
const res = mapObjectMetadataToGraphQLQuery({
|
const res = mapObjectMetadataToGraphQLQuery({
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
objectMetadataItems: mockObjectMetadataItems,
|
||||||
objectMetadataItem: personObjectMetadataItem,
|
objectMetadataItem: personObjectMetadataItem,
|
||||||
depth: -1,
|
recordGqlFields: {
|
||||||
});
|
|
||||||
expect(formatGQLString(res)).toEqual(`{
|
|
||||||
__typename
|
|
||||||
}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return depth 0 if depth = 0', async () => {
|
|
||||||
const res = mapObjectMetadataToGraphQLQuery({
|
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
|
||||||
objectMetadataItem: personObjectMetadataItem,
|
|
||||||
depth: 0,
|
|
||||||
});
|
|
||||||
expect(formatGQLString(res)).toEqual(`{
|
|
||||||
__typename
|
|
||||||
xLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
city
|
|
||||||
email
|
|
||||||
jobTitle
|
|
||||||
name
|
|
||||||
{
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
phone
|
|
||||||
linkedinLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
updatedAt
|
|
||||||
avatarUrl
|
|
||||||
companyId
|
|
||||||
}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return depth 1 if depth = 1', async () => {
|
|
||||||
const res = mapObjectMetadataToGraphQLQuery({
|
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
|
||||||
objectMetadataItem: personObjectMetadataItem,
|
|
||||||
depth: 1,
|
|
||||||
});
|
|
||||||
expect(formatGQLString(res)).toEqual(`{
|
|
||||||
__typename
|
|
||||||
opportunities
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
personId
|
|
||||||
pointOfContactId
|
|
||||||
updatedAt
|
|
||||||
companyId
|
|
||||||
probability
|
|
||||||
closeDate
|
|
||||||
amount
|
|
||||||
{
|
|
||||||
amountMicros
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
id
|
|
||||||
pointOfContactForOpportunities
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
personId
|
|
||||||
pointOfContactId
|
|
||||||
updatedAt
|
|
||||||
companyId
|
|
||||||
probability
|
|
||||||
closeDate
|
|
||||||
amount
|
|
||||||
{
|
|
||||||
amountMicros
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
company
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
xLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
linkedinLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
domainName
|
|
||||||
annualRecurringRevenue
|
|
||||||
{
|
|
||||||
amountMicros
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
address
|
|
||||||
updatedAt
|
|
||||||
name
|
|
||||||
accountOwnerId
|
|
||||||
employees
|
|
||||||
id
|
|
||||||
idealCustomerProfile
|
|
||||||
}
|
|
||||||
city
|
|
||||||
email
|
|
||||||
activityTargets
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
updatedAt
|
|
||||||
createdAt
|
|
||||||
personId
|
|
||||||
activityId
|
|
||||||
companyId
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jobTitle
|
|
||||||
favorites
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
companyId
|
|
||||||
createdAt
|
|
||||||
personId
|
|
||||||
position
|
|
||||||
workspaceMemberId
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attachments
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
updatedAt
|
|
||||||
createdAt
|
|
||||||
name
|
|
||||||
personId
|
|
||||||
activityId
|
|
||||||
companyId
|
|
||||||
id
|
|
||||||
authorId
|
|
||||||
type
|
|
||||||
fullPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
name
|
|
||||||
{
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
phone
|
|
||||||
linkedinLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
updatedAt
|
|
||||||
avatarUrl
|
|
||||||
companyId
|
|
||||||
}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should query only specified queryFields', async () => {
|
|
||||||
const res = mapObjectMetadataToGraphQLQuery({
|
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
|
||||||
objectMetadataItem: personObjectMetadataItem,
|
|
||||||
queryFields: {
|
|
||||||
company: true,
|
company: true,
|
||||||
xLink: true,
|
xLink: true,
|
||||||
id: true,
|
id: true,
|
||||||
@ -232,7 +34,6 @@ companyId
|
|||||||
avatarUrl: true,
|
avatarUrl: true,
|
||||||
companyId: true,
|
companyId: true,
|
||||||
},
|
},
|
||||||
depth: 1,
|
|
||||||
});
|
});
|
||||||
expect(formatGQLString(res)).toEqual(`{
|
expect(formatGQLString(res)).toEqual(`{
|
||||||
__typename
|
__typename
|
||||||
@ -291,12 +92,11 @@ companyId
|
|||||||
}`);
|
}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load only specified query fields', async () => {
|
it('should load only specified operation fields nested', async () => {
|
||||||
const res = mapObjectMetadataToGraphQLQuery({
|
const res = mapObjectMetadataToGraphQLQuery({
|
||||||
objectMetadataItems: mockObjectMetadataItems,
|
objectMetadataItems: mockObjectMetadataItems,
|
||||||
objectMetadataItem: personObjectMetadataItem,
|
objectMetadataItem: personObjectMetadataItem,
|
||||||
queryFields: { company: true, id: true, name: true },
|
recordGqlFields: { company: { id: true }, id: true, name: true },
|
||||||
depth: 1,
|
|
||||||
});
|
});
|
||||||
expect(formatGQLString(res)).toEqual(`{
|
expect(formatGQLString(res)).toEqual(`{
|
||||||
__typename
|
__typename
|
||||||
@ -304,30 +104,7 @@ id
|
|||||||
company
|
company
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
xLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
linkedinLink
|
|
||||||
{
|
|
||||||
label
|
|
||||||
url
|
|
||||||
}
|
|
||||||
domainName
|
|
||||||
annualRecurringRevenue
|
|
||||||
{
|
|
||||||
amountMicros
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
address
|
|
||||||
updatedAt
|
|
||||||
name
|
|
||||||
accountOwnerId
|
|
||||||
employees
|
|
||||||
id
|
id
|
||||||
idealCustomerProfile
|
|
||||||
}
|
}
|
||||||
name
|
name
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,113 +2,50 @@ import { shouldFieldBeQueried } from '@/object-metadata/utils/shouldFieldBeQueri
|
|||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
describe('shouldFieldBeQueried', () => {
|
describe('shouldFieldBeQueried', () => {
|
||||||
describe('if field is not relation', () => {
|
describe('if recordGqlFields is absent, we query all except relations', () => {
|
||||||
it('should be queried if depth is undefined', () => {
|
it('should be queried if the field is not a relation', () => {
|
||||||
const res = shouldFieldBeQueried({
|
const res = shouldFieldBeQueried({
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Boolean },
|
field: { name: 'fieldName', type: FieldMetadataType.Boolean },
|
||||||
});
|
});
|
||||||
expect(res).toBe(true);
|
expect(res).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be queried depth = 0', () => {
|
it('should not be queried if the field is a relation', () => {
|
||||||
const res = shouldFieldBeQueried({
|
const res = shouldFieldBeQueried({
|
||||||
depth: 0,
|
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Boolean },
|
|
||||||
});
|
|
||||||
expect(res).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be queried depth > 0', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: 1,
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Boolean },
|
|
||||||
});
|
|
||||||
expect(res).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT be queried depth < 0', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: -1,
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Boolean },
|
|
||||||
});
|
});
|
||||||
expect(res).toBe(false);
|
expect(res).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not depends on queryFields', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: 0,
|
|
||||||
queryFields: {
|
|
||||||
fieldName: true,
|
|
||||||
},
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Boolean },
|
|
||||||
});
|
|
||||||
expect(res).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('if field is relation', () => {
|
describe('if recordGqlFields is present, we respect it', () => {
|
||||||
it('should be queried if queryFields and depth are undefined', () => {
|
it('should be queried if true', () => {
|
||||||
const res = shouldFieldBeQueried({
|
const res = shouldFieldBeQueried({
|
||||||
|
recordGqlFields: { fieldName: true },
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
||||||
});
|
});
|
||||||
expect(res).toBe(true);
|
expect(res).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be queried if queryFields is undefined and depth = 1', () => {
|
it('should be queried if object', () => {
|
||||||
const res = shouldFieldBeQueried({
|
const res = shouldFieldBeQueried({
|
||||||
depth: 1,
|
recordGqlFields: { fieldName: { subFieldName: false } },
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
||||||
});
|
});
|
||||||
expect(res).toBe(true);
|
expect(res).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be queried if queryFields is undefined and depth > 1', () => {
|
it('should not be queried if false', () => {
|
||||||
const res = shouldFieldBeQueried({
|
const res = shouldFieldBeQueried({
|
||||||
depth: 2,
|
recordGqlFields: { fieldName: false },
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
|
||||||
});
|
|
||||||
expect(res).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT be queried if queryFields is undefined and depth < 1', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: 0,
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
||||||
});
|
});
|
||||||
expect(res).toBe(false);
|
expect(res).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be queried if queryFields is matching and depth > 1', () => {
|
it('should not be queried if absent', () => {
|
||||||
const res = shouldFieldBeQueried({
|
const res = shouldFieldBeQueried({
|
||||||
depth: 1,
|
recordGqlFields: { otherFieldName: false },
|
||||||
queryFields: { fieldName: true },
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
|
||||||
});
|
|
||||||
expect(res).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT be queried if queryFields is matching and depth < 1', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: 0,
|
|
||||||
queryFields: { fieldName: true },
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
|
||||||
});
|
|
||||||
expect(res).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT be queried if queryFields is not matching (falsy) and depth < 1', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: 1,
|
|
||||||
queryFields: { fieldName: false },
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
|
||||||
});
|
|
||||||
expect(res).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT be queried if queryFields is not matching and depth < 1', () => {
|
|
||||||
const res = shouldFieldBeQueried({
|
|
||||||
depth: 0,
|
|
||||||
queryFields: { anotherFieldName: true },
|
|
||||||
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
field: { name: 'fieldName', type: FieldMetadataType.Relation },
|
||||||
});
|
});
|
||||||
expect(res).toBe(false);
|
expect(res).toBe(false);
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
import { OrderByField } from '@/object-metadata/types/OrderByField';
|
|
||||||
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
||||||
|
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const getOrderByFieldForObjectMetadataItem = (
|
export const getOrderByFieldForObjectMetadataItem = (
|
||||||
objectMetadataItem: ObjectMetadataItem,
|
objectMetadataItem: ObjectMetadataItem,
|
||||||
orderBy?: OrderBy | null,
|
orderBy?: OrderBy | null,
|
||||||
): OrderByField => {
|
): RecordGqlOperationOrderBy => {
|
||||||
const labelIdentifierFieldMetadata =
|
const labelIdentifierFieldMetadata =
|
||||||
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
|
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,7 @@ import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
|||||||
export const mapFieldMetadataToGraphQLQuery = ({
|
export const mapFieldMetadataToGraphQLQuery = ({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
field,
|
field,
|
||||||
depth = 0,
|
relationrecordFields,
|
||||||
queryFields,
|
|
||||||
computeReferences = false,
|
computeReferences = false,
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
@ -19,8 +18,7 @@ export const mapFieldMetadataToGraphQLQuery = ({
|
|||||||
FieldMetadataItem,
|
FieldMetadataItem,
|
||||||
'name' | 'type' | 'toRelationMetadata' | 'fromRelationMetadata'
|
'name' | 'type' | 'toRelationMetadata' | 'fromRelationMetadata'
|
||||||
>;
|
>;
|
||||||
depth?: number;
|
relationrecordFields?: Record<string, any>;
|
||||||
queryFields?: Record<string, any>;
|
|
||||||
computeReferences?: boolean;
|
computeReferences?: boolean;
|
||||||
}): any => {
|
}): any => {
|
||||||
const fieldType = field.type;
|
const fieldType = field.type;
|
||||||
@ -47,8 +45,7 @@ export const mapFieldMetadataToGraphQLQuery = ({
|
|||||||
return field.name;
|
return field.name;
|
||||||
} else if (
|
} else if (
|
||||||
fieldType === 'RELATION' &&
|
fieldType === 'RELATION' &&
|
||||||
field.toRelationMetadata?.relationType === 'ONE_TO_MANY' &&
|
field.toRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||||
depth > 0
|
|
||||||
) {
|
) {
|
||||||
const relationMetadataItem = objectMetadataItems.find(
|
const relationMetadataItem = objectMetadataItems.find(
|
||||||
(objectMetadataItem) =>
|
(objectMetadataItem) =>
|
||||||
@ -64,15 +61,13 @@ export const mapFieldMetadataToGraphQLQuery = ({
|
|||||||
${mapObjectMetadataToGraphQLQuery({
|
${mapObjectMetadataToGraphQLQuery({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem: relationMetadataItem,
|
objectMetadataItem: relationMetadataItem,
|
||||||
depth: depth - 1,
|
recordGqlFields: relationrecordFields,
|
||||||
queryFields,
|
|
||||||
computeReferences: computeReferences,
|
computeReferences: computeReferences,
|
||||||
isRootLevel: false,
|
isRootLevel: false,
|
||||||
})}`;
|
})}`;
|
||||||
} else if (
|
} else if (
|
||||||
fieldType === 'RELATION' &&
|
fieldType === 'RELATION' &&
|
||||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY' &&
|
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||||
depth > 0
|
|
||||||
) {
|
) {
|
||||||
const relationMetadataItem = objectMetadataItems.find(
|
const relationMetadataItem = objectMetadataItems.find(
|
||||||
(objectMetadataItem) =>
|
(objectMetadataItem) =>
|
||||||
@ -90,8 +85,7 @@ ${mapObjectMetadataToGraphQLQuery({
|
|||||||
node ${mapObjectMetadataToGraphQLQuery({
|
node ${mapObjectMetadataToGraphQLQuery({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem: relationMetadataItem,
|
objectMetadataItem: relationMetadataItem,
|
||||||
depth: depth - 1,
|
recordGqlFields: relationrecordFields,
|
||||||
queryFields,
|
|
||||||
computeReferences,
|
computeReferences,
|
||||||
isRootLevel: false,
|
isRootLevel: false,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -5,15 +5,13 @@ import { shouldFieldBeQueried } from '@/object-metadata/utils/shouldFieldBeQueri
|
|||||||
export const mapObjectMetadataToGraphQLQuery = ({
|
export const mapObjectMetadataToGraphQLQuery = ({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
depth = 1,
|
recordGqlFields,
|
||||||
queryFields,
|
|
||||||
computeReferences = false,
|
computeReferences = false,
|
||||||
isRootLevel = true,
|
isRootLevel = true,
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
objectMetadataItem: Pick<ObjectMetadataItem, 'nameSingular' | 'fields'>;
|
objectMetadataItem: Pick<ObjectMetadataItem, 'nameSingular' | 'fields'>;
|
||||||
depth?: number;
|
recordGqlFields?: Record<string, any>;
|
||||||
queryFields?: Record<string, any>;
|
|
||||||
computeReferences?: boolean;
|
computeReferences?: boolean;
|
||||||
isRootLevel?: boolean;
|
isRootLevel?: boolean;
|
||||||
}): any => {
|
}): any => {
|
||||||
@ -23,8 +21,7 @@ export const mapObjectMetadataToGraphQLQuery = ({
|
|||||||
.filter((field) =>
|
.filter((field) =>
|
||||||
shouldFieldBeQueried({
|
shouldFieldBeQueried({
|
||||||
field,
|
field,
|
||||||
depth,
|
recordGqlFields,
|
||||||
queryFields,
|
|
||||||
}),
|
}),
|
||||||
) ?? [];
|
) ?? [];
|
||||||
|
|
||||||
@ -37,18 +34,17 @@ export const mapObjectMetadataToGraphQLQuery = ({
|
|||||||
return `{
|
return `{
|
||||||
__typename
|
__typename
|
||||||
${fieldsThatShouldBeQueried
|
${fieldsThatShouldBeQueried
|
||||||
.map((field) =>
|
.map((field) => {
|
||||||
mapFieldMetadataToGraphQLQuery({
|
return mapFieldMetadataToGraphQLQuery({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
field,
|
field,
|
||||||
depth,
|
relationrecordFields:
|
||||||
queryFields:
|
typeof recordGqlFields?.[field.name] === 'boolean'
|
||||||
typeof queryFields?.[field.name] === 'boolean'
|
|
||||||
? undefined
|
? undefined
|
||||||
: queryFields?.[field.name],
|
: recordGqlFields?.[field.name],
|
||||||
computeReferences,
|
computeReferences,
|
||||||
}),
|
});
|
||||||
)
|
})
|
||||||
.join('\n')}
|
.join('\n')}
|
||||||
}`;
|
}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,36 +1,32 @@
|
|||||||
import { isUndefined } from '@sniptt/guards';
|
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||||
|
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
|
|
||||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||||
|
|
||||||
export const shouldFieldBeQueried = ({
|
export const shouldFieldBeQueried = ({
|
||||||
field,
|
field,
|
||||||
depth,
|
recordGqlFields,
|
||||||
queryFields,
|
|
||||||
}: {
|
}: {
|
||||||
field: Pick<FieldMetadataItem, 'name' | 'type'>;
|
field: Pick<FieldMetadataItem, 'name' | 'type'>;
|
||||||
depth?: number;
|
|
||||||
objectRecord?: ObjectRecord;
|
objectRecord?: ObjectRecord;
|
||||||
queryFields?: Record<string, any>;
|
recordGqlFields?: RecordGqlOperationGqlRecordFields;
|
||||||
}): any => {
|
}): any => {
|
||||||
if (!isUndefined(depth) && depth < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isUndefined(depth) &&
|
isUndefinedOrNull(recordGqlFields) &&
|
||||||
depth < 1 &&
|
field.type !== FieldMetadataType.Relation
|
||||||
field.type === FieldMetadataType.Relation
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
isDefined(recordGqlFields) &&
|
||||||
|
isDefined(recordGqlFields[field.name]) &&
|
||||||
|
recordGqlFields[field.name] !== false
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDefined(queryFields) && !queryFields[field.name]) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export const MAX_QUERY_DEPTH_FOR_CACHE_INJECTION = 1;
|
|
||||||
@ -7,6 +7,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|||||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { prefillRecord } from '@/object-record/utils/prefillRecord';
|
import { prefillRecord } from '@/object-record/utils/prefillRecord';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -32,13 +33,15 @@ export const useCreateOneRecordInCache = <T extends ObjectRecord>({
|
|||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
computeReferences: true,
|
computeReferences: true,
|
||||||
|
recordGqlFields: generateDepthOneRecordGqlFields({
|
||||||
|
objectMetadataItem,
|
||||||
|
}),
|
||||||
})}
|
})}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const prefilledRecord = prefillRecord({
|
const prefilledRecord = prefillRecord({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
input: record,
|
input: record,
|
||||||
depth: 1,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const recordToCreateWithNestedConnections = getRecordNodeFromRecord({
|
const recordToCreateWithNestedConnections = getRecordNodeFromRecord({
|
||||||
|
|||||||
@ -5,33 +5,46 @@ import { useRecoilValue } from 'recoil';
|
|||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache';
|
import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache';
|
||||||
|
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||||
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
export const useGetRecordFromCache = ({
|
export const useGetRecordFromCache = ({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
|
recordGqlFields,
|
||||||
}: {
|
}: {
|
||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
|
recordGqlFields?: RecordGqlFields;
|
||||||
}) => {
|
}) => {
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const appliedRecordGqlFields =
|
||||||
|
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
|
||||||
|
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
const apolloClient = useApolloClient();
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
<CachedObjectRecord extends ObjectRecord = ObjectRecord>(
|
<T extends ObjectRecord = ObjectRecord>(
|
||||||
recordId: string,
|
recordId: string,
|
||||||
cache = apolloClient.cache,
|
cache = apolloClient.cache,
|
||||||
) => {
|
) => {
|
||||||
return getRecordFromCache<CachedObjectRecord>({
|
return getRecordFromCache<T>({
|
||||||
cache,
|
cache,
|
||||||
recordId,
|
recordId,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
|
recordGqlFields: appliedRecordGqlFields,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[objectMetadataItem, objectMetadataItems, apolloClient],
|
[
|
||||||
|
apolloClient.cache,
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
appliedRecordGqlFields,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import { useApolloClient } from '@apollo/client';
|
|||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||||
|
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
|
||||||
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordQueryResult } from '@/object-record/types/ObjectRecordQueryResult';
|
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
|
||||||
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
@ -22,32 +22,28 @@ export const useReadFindManyRecordsQueryInCache = ({
|
|||||||
T extends ObjectRecord = ObjectRecord,
|
T extends ObjectRecord = ObjectRecord,
|
||||||
>({
|
>({
|
||||||
queryVariables,
|
queryVariables,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
depth,
|
|
||||||
}: {
|
}: {
|
||||||
queryVariables: ObjectRecordQueryVariables;
|
queryVariables: RecordGqlOperationVariables;
|
||||||
queryFields?: Record<string, any>;
|
recordGqlFields?: Record<string, any>;
|
||||||
depth?: number;
|
|
||||||
}) => {
|
}) => {
|
||||||
const findManyRecordsQueryForCacheRead = generateFindManyRecordsQuery({
|
const findManyRecordsQueryForCacheRead = generateFindManyRecordsQuery({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
depth,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const existingRecordsQueryResult = apolloClient.readQuery<
|
const existingRecordsQueryResult =
|
||||||
ObjectRecordQueryResult<T>
|
apolloClient.readQuery<RecordGqlOperationFindManyResult>({
|
||||||
>({
|
query: findManyRecordsQueryForCacheRead,
|
||||||
query: findManyRecordsQueryForCacheRead,
|
variables: queryVariables,
|
||||||
variables: queryVariables,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const existingRecordConnection =
|
const existingRecordConnection =
|
||||||
existingRecordsQueryResult?.[objectMetadataItem.namePlural];
|
existingRecordsQueryResult?.[objectMetadataItem.namePlural];
|
||||||
|
|
||||||
const existingObjectRecords = isDefined(existingRecordConnection)
|
const existingObjectRecords = isDefined(existingRecordConnection)
|
||||||
? getRecordsFromRecordConnection({
|
? getRecordsFromRecordConnection<T>({
|
||||||
recordConnection: existingRecordConnection,
|
recordConnection: existingRecordConnection,
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|||||||
@ -3,10 +3,9 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { MAX_QUERY_DEPTH_FOR_CACHE_INJECTION } from '@/object-record/cache/constants/MaxQueryDepthForCacheInjection';
|
|
||||||
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
||||||
|
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
|
||||||
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
||||||
|
|
||||||
export const useUpsertFindManyRecordsQueryInCache = ({
|
export const useUpsertFindManyRecordsQueryInCache = ({
|
||||||
@ -22,22 +21,19 @@ export const useUpsertFindManyRecordsQueryInCache = ({
|
|||||||
T extends ObjectRecord = ObjectRecord,
|
T extends ObjectRecord = ObjectRecord,
|
||||||
>({
|
>({
|
||||||
queryVariables,
|
queryVariables,
|
||||||
depth = MAX_QUERY_DEPTH_FOR_CACHE_INJECTION,
|
|
||||||
objectRecordsToOverwrite,
|
objectRecordsToOverwrite,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
computeReferences = false,
|
computeReferences = false,
|
||||||
}: {
|
}: {
|
||||||
queryVariables: ObjectRecordQueryVariables;
|
queryVariables: RecordGqlOperationVariables;
|
||||||
depth?: number;
|
|
||||||
objectRecordsToOverwrite: T[];
|
objectRecordsToOverwrite: T[];
|
||||||
queryFields?: Record<string, any>;
|
recordGqlFields?: Record<string, any>;
|
||||||
computeReferences?: boolean;
|
computeReferences?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const findManyRecordsQueryForCacheOverwrite = generateFindManyRecordsQuery({
|
const findManyRecordsQueryForCacheOverwrite = generateFindManyRecordsQuery({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
depth,
|
recordGqlFields,
|
||||||
queryFields,
|
|
||||||
computeReferences,
|
computeReferences,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -45,7 +41,7 @@ export const useUpsertFindManyRecordsQueryInCache = ({
|
|||||||
objectMetadataItems: objectMetadataItems,
|
objectMetadataItems: objectMetadataItems,
|
||||||
objectMetadataItem: objectMetadataItem,
|
objectMetadataItem: objectMetadataItem,
|
||||||
records: objectRecordsToOverwrite,
|
records: objectRecordsToOverwrite,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
computeReferences,
|
computeReferences,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
6
packages/twenty-front/src/modules/object-record/cache/types/RecordGqlRefConnection.ts
vendored
Normal file
6
packages/twenty-front/src/modules/object-record/cache/types/RecordGqlRefConnection.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||||
|
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||||
|
|
||||||
|
export type RecordGqlRefConnection = Omit<RecordGqlConnection, 'edges'> & {
|
||||||
|
edges: RecordGqlRefEdge[];
|
||||||
|
};
|
||||||
6
packages/twenty-front/src/modules/object-record/cache/types/RecordGqlRefEdge.ts
vendored
Normal file
6
packages/twenty-front/src/modules/object-record/cache/types/RecordGqlRefEdge.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { RecordGqlRefNode } from '@/object-record/cache/types/RecordGqlRefNode';
|
||||||
|
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
|
||||||
|
|
||||||
|
export type RecordGqlRefEdge = Omit<RecordGqlEdge, 'node'> & {
|
||||||
|
node: RecordGqlRefNode;
|
||||||
|
};
|
||||||
3
packages/twenty-front/src/modules/object-record/cache/types/RecordGqlRefNode.ts
vendored
Normal file
3
packages/twenty-front/src/modules/object-record/cache/types/RecordGqlRefNode.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Reference } from '@apollo/client';
|
||||||
|
|
||||||
|
export type RecordGqlRefNode = Reference;
|
||||||
@ -2,18 +2,18 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|||||||
import { getConnectionTypename } from '@/object-record/cache/utils/getConnectionTypename';
|
import { getConnectionTypename } from '@/object-record/cache/utils/getConnectionTypename';
|
||||||
import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo';
|
import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo';
|
||||||
import { getRecordEdgeFromRecord } from '@/object-record/cache/utils/getRecordEdgeFromRecord';
|
import { getRecordEdgeFromRecord } from '@/object-record/cache/utils/getRecordEdgeFromRecord';
|
||||||
|
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||||
|
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
|
||||||
|
|
||||||
export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
|
export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
records,
|
records,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
withPageInfo = true,
|
withPageInfo = true,
|
||||||
computeReferences = false,
|
computeReferences = false,
|
||||||
isRootLevel = true,
|
isRootLevel = true,
|
||||||
depth = 1,
|
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
objectMetadataItem: Pick<
|
objectMetadataItem: Pick<
|
||||||
@ -21,11 +21,10 @@ export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
|
|||||||
'fields' | 'namePlural' | 'nameSingular'
|
'fields' | 'namePlural' | 'nameSingular'
|
||||||
>;
|
>;
|
||||||
records: T[];
|
records: T[];
|
||||||
queryFields?: Record<string, any>;
|
recordGqlFields?: RecordGqlOperationGqlRecordFields;
|
||||||
withPageInfo?: boolean;
|
withPageInfo?: boolean;
|
||||||
isRootLevel?: boolean;
|
isRootLevel?: boolean;
|
||||||
computeReferences?: boolean;
|
computeReferences?: boolean;
|
||||||
depth?: number;
|
|
||||||
}) => {
|
}) => {
|
||||||
return {
|
return {
|
||||||
__typename: getConnectionTypename(objectMetadataItem.nameSingular),
|
__typename: getConnectionTypename(objectMetadataItem.nameSingular),
|
||||||
@ -33,14 +32,13 @@ export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
|
|||||||
return getRecordEdgeFromRecord({
|
return getRecordEdgeFromRecord({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
record,
|
record,
|
||||||
isRootLevel,
|
isRootLevel,
|
||||||
computeReferences,
|
computeReferences,
|
||||||
depth,
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
...(withPageInfo && { pageInfo: getEmptyPageInfo() }),
|
...(withPageInfo && { pageInfo: getEmptyPageInfo() }),
|
||||||
...(withPageInfo && { totalCount: records.length }),
|
...(withPageInfo && { totalCount: records.length }),
|
||||||
} as ObjectRecordConnection<T>;
|
} as RecordGqlConnection;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
||||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
|
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
|
||||||
|
|
||||||
export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
|
export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
record,
|
record,
|
||||||
computeReferences = false,
|
computeReferences = false,
|
||||||
isRootLevel = false,
|
isRootLevel = false,
|
||||||
@ -17,10 +17,9 @@ export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
|
|||||||
ObjectMetadataItem,
|
ObjectMetadataItem,
|
||||||
'fields' | 'namePlural' | 'nameSingular'
|
'fields' | 'namePlural' | 'nameSingular'
|
||||||
>;
|
>;
|
||||||
queryFields?: Record<string, any>;
|
recordGqlFields?: Record<string, any>;
|
||||||
computeReferences?: boolean;
|
computeReferences?: boolean;
|
||||||
isRootLevel?: boolean;
|
isRootLevel?: boolean;
|
||||||
depth?: number;
|
|
||||||
record: T;
|
record: T;
|
||||||
}) => {
|
}) => {
|
||||||
return {
|
return {
|
||||||
@ -29,13 +28,12 @@ export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
|
|||||||
...getRecordNodeFromRecord({
|
...getRecordNodeFromRecord({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
record,
|
record,
|
||||||
computeReferences,
|
computeReferences,
|
||||||
isRootLevel,
|
isRootLevel,
|
||||||
depth: 1,
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
cursor: '',
|
cursor: '',
|
||||||
} as ObjectRecordEdge<T>;
|
} as RecordGqlEdge;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { ApolloCache, gql } from '@apollo/client';
|
import { ApolloCache, gql } from '@apollo/client';
|
||||||
|
|
||||||
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||||
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
||||||
|
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||||
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -13,16 +14,21 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
|||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
cache,
|
cache,
|
||||||
recordId,
|
recordId,
|
||||||
|
recordGqlFields,
|
||||||
}: {
|
}: {
|
||||||
cache: ApolloCache<object>;
|
cache: ApolloCache<object>;
|
||||||
recordId: string;
|
recordId: string;
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
recordGqlFields?: RecordGqlFields;
|
||||||
}) => {
|
}) => {
|
||||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const appliedRecordGqlFields =
|
||||||
|
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
|
||||||
|
|
||||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||||
|
|
||||||
const cacheReadFragment = gql`
|
const cacheReadFragment = gql`
|
||||||
@ -30,6 +36,7 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
|||||||
{
|
{
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
|
recordGqlFields: appliedRecordGqlFields,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
@ -49,7 +56,7 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getRecordFromRecordNode({
|
return getRecordFromRecordNode<T>({
|
||||||
recordNode: record,
|
recordNode: record,
|
||||||
}) as CachedObjectRecord<T>;
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
@ -6,7 +7,7 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
|||||||
export const getRecordFromRecordNode = <T extends ObjectRecord>({
|
export const getRecordFromRecordNode = <T extends ObjectRecord>({
|
||||||
recordNode,
|
recordNode,
|
||||||
}: {
|
}: {
|
||||||
recordNode: T;
|
recordNode: RecordGqlNode;
|
||||||
}): T => {
|
}): T => {
|
||||||
return {
|
return {
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
@ -32,5 +33,6 @@ export const getRecordFromRecordNode = <T extends ObjectRecord>({
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
id: recordNode.id,
|
id: recordNode.id,
|
||||||
|
__typename: recordNode.__typename,
|
||||||
} as T;
|
} as T;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { isNull, isUndefined } from '@sniptt/guards';
|
import { isNull, isUndefined } from '@sniptt/guards';
|
||||||
|
|
||||||
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { getNodeTypename } from '@/object-record/cache/utils/getNodeTypename';
|
import { getNodeTypename } from '@/object-record/cache/utils/getNodeTypename';
|
||||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||||
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import {
|
import {
|
||||||
FieldMetadataType,
|
FieldMetadataType,
|
||||||
@ -16,22 +16,20 @@ import { lowerAndCapitalize } from '~/utils/string/lowerAndCapitalize';
|
|||||||
export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
queryFields,
|
recordGqlFields,
|
||||||
record,
|
record,
|
||||||
computeReferences = true,
|
computeReferences = true,
|
||||||
isRootLevel = true,
|
isRootLevel = true,
|
||||||
depth = 1,
|
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
objectMetadataItem: Pick<
|
objectMetadataItem: Pick<
|
||||||
ObjectMetadataItem,
|
ObjectMetadataItem,
|
||||||
'fields' | 'namePlural' | 'nameSingular'
|
'fields' | 'namePlural' | 'nameSingular'
|
||||||
>;
|
>;
|
||||||
queryFields?: Record<string, any>;
|
recordGqlFields?: Record<string, any>;
|
||||||
computeReferences?: boolean;
|
computeReferences?: boolean;
|
||||||
isRootLevel?: boolean;
|
isRootLevel?: boolean;
|
||||||
record: T | null;
|
record: T | null;
|
||||||
depth?: number;
|
|
||||||
}) => {
|
}) => {
|
||||||
if (isNull(record)) {
|
if (isNull(record)) {
|
||||||
return null;
|
return null;
|
||||||
@ -42,13 +40,13 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
|||||||
if (!isRootLevel && computeReferences) {
|
if (!isRootLevel && computeReferences) {
|
||||||
return {
|
return {
|
||||||
__ref: `${nodeTypeName}:${record.id}`,
|
__ref: `${nodeTypeName}:${record.id}`,
|
||||||
} as unknown as CachedObjectRecord<T>; // Todo Fix typing
|
} as unknown as RecordGqlNode; // Fix typing: we want a Reference in computeReferences mode
|
||||||
}
|
}
|
||||||
|
|
||||||
const nestedRecord = Object.fromEntries(
|
const nestedRecord = Object.fromEntries(
|
||||||
Object.entries(record)
|
Object.entries(record)
|
||||||
.map(([fieldName, value]) => {
|
.map(([fieldName, value]) => {
|
||||||
if (isDefined(queryFields) && !queryFields[fieldName]) {
|
if (isDefined(recordGqlFields) && !recordGqlFields[fieldName]) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,14 +58,6 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
!isUndefined(depth) &&
|
|
||||||
depth < 1 &&
|
|
||||||
field.type === FieldMetadataType.Relation
|
|
||||||
) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
field.type === FieldMetadataType.Relation &&
|
field.type === FieldMetadataType.Relation &&
|
||||||
field.relationDefinition?.direction ===
|
field.relationDefinition?.direction ===
|
||||||
@ -87,15 +77,14 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
|||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem: oneToManyObjectMetadataItem,
|
objectMetadataItem: oneToManyObjectMetadataItem,
|
||||||
records: value as ObjectRecord[],
|
records: value as ObjectRecord[],
|
||||||
queryFields:
|
recordGqlFields:
|
||||||
queryFields?.[fieldName] === true ||
|
recordGqlFields?.[fieldName] === true ||
|
||||||
isUndefined(queryFields?.[fieldName])
|
isUndefined(recordGqlFields?.[fieldName])
|
||||||
? undefined
|
? undefined
|
||||||
: queryFields?.[fieldName],
|
: recordGqlFields?.[fieldName],
|
||||||
withPageInfo: false,
|
withPageInfo: false,
|
||||||
isRootLevel: false,
|
isRootLevel: false,
|
||||||
computeReferences,
|
computeReferences,
|
||||||
depth: depth - 1,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -159,8 +148,5 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
|||||||
.filter(isDefined),
|
.filter(isDefined),
|
||||||
) as T; // Todo fix typing once we have investigated apollo edges / nodes removal
|
) as T; // Todo fix typing once we have investigated apollo edges / nodes removal
|
||||||
|
|
||||||
return {
|
return { ...nestedRecord, __typename: nodeTypeName };
|
||||||
__typename: getNodeTypename(objectMetadataItem.nameSingular),
|
|
||||||
...nestedRecord,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
||||||
|
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
|
||||||
|
|
||||||
export const getRecordsFromRecordConnection = <T extends ObjectRecord>({
|
export const getRecordsFromRecordConnection = <T extends ObjectRecord>({
|
||||||
recordConnection,
|
recordConnection,
|
||||||
}: {
|
}: {
|
||||||
recordConnection: ObjectRecordConnection<T>;
|
recordConnection: RecordGqlConnection;
|
||||||
}): T[] => {
|
}): T[] => {
|
||||||
return recordConnection.edges.map((edge) =>
|
return recordConnection.edges.map((edge) =>
|
||||||
getRecordFromRecordNode<T>({ recordNode: edge.node }),
|
getRecordFromRecordNode<T>({ recordNode: edge.node }),
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const isObjectRecordConnection = (
|
export const isObjectRecordConnection = (
|
||||||
objectNameSingular: string,
|
objectNameSingular: string,
|
||||||
value: unknown,
|
value: unknown,
|
||||||
): value is ObjectRecordConnection => {
|
): value is RecordGqlConnection => {
|
||||||
const objectConnectionTypeName = `${capitalize(
|
const objectConnectionTypeName = `${capitalize(
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
)}Connection`;
|
)}Connection`;
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { StoreValue } from '@apollo/client';
|
import { StoreValue } from '@apollo/client';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { CachedObjectRecordConnection } from '@/apollo/types/CachedObjectRecordConnection';
|
import { RecordGqlRefConnection } from '@/object-record/cache/types/RecordGqlRefConnection';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const isObjectRecordConnectionWithRefs = (
|
export const isObjectRecordConnectionWithRefs = (
|
||||||
objectNameSingular: string,
|
objectNameSingular: string,
|
||||||
storeValue: StoreValue,
|
storeValue: StoreValue,
|
||||||
): storeValue is CachedObjectRecordConnection => {
|
): storeValue is RecordGqlRefConnection => {
|
||||||
const objectConnectionTypeName = `${capitalize(
|
const objectConnectionTypeName = `${capitalize(
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
)}Connection`;
|
)}Connection`;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import gql from 'graphql-tag';
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -44,14 +45,13 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
|||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
record,
|
record,
|
||||||
depth: 1,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isUndefinedOrNull(recordWithConnection)) {
|
if (isUndefinedOrNull(recordWithConnection)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.writeFragment<T & { __typename: string }>({
|
cache.writeFragment<RecordGqlNode>({
|
||||||
id: cachedRecordId,
|
id: cachedRecordId,
|
||||||
fragment: cacheWriteFragment,
|
fragment: cacheWriteFragment,
|
||||||
data: recordWithConnection,
|
data: recordWithConnection,
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { Nullable } from 'twenty-ui';
|
import { Nullable } from 'twenty-ui';
|
||||||
|
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
|
||||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
|
||||||
|
|
||||||
export type ObjectRecordConnection<T extends ObjectRecord = ObjectRecord> = {
|
export type RecordGqlConnection = {
|
||||||
__typename?: string;
|
__typename?: string;
|
||||||
edges: ObjectRecordEdge<T>[];
|
edges: RecordGqlEdge[];
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
hasNextPage?: boolean;
|
hasNextPage?: boolean;
|
||||||
hasPreviousPage?: boolean;
|
hasPreviousPage?: boolean;
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
|
|
||||||
|
export type RecordGqlEdge = {
|
||||||
|
__typename: string;
|
||||||
|
node: RecordGqlNode;
|
||||||
|
cursor: string;
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export type RecordGqlFields = Record<string, any>;
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export type RecordGqlNode = {
|
||||||
|
id: string;
|
||||||
|
[key: string]: any;
|
||||||
|
__typename: string;
|
||||||
|
};
|
||||||
@ -93,22 +93,22 @@ export type LeafFilter =
|
|||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
export type AndObjectRecordFilter = {
|
export type AndObjectRecordFilter = {
|
||||||
and?: ObjectRecordQueryFilter[];
|
and?: RecordGqlOperationFilter[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OrObjectRecordFilter = {
|
export type OrObjectRecordFilter = {
|
||||||
or?: ObjectRecordQueryFilter[] | ObjectRecordQueryFilter;
|
or?: RecordGqlOperationFilter[] | RecordGqlOperationFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NotObjectRecordFilter = {
|
export type NotObjectRecordFilter = {
|
||||||
not?: ObjectRecordQueryFilter;
|
not?: RecordGqlOperationFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LeafObjectRecordFilter = {
|
export type LeafObjectRecordFilter = {
|
||||||
[fieldName: string]: LeafFilter;
|
[fieldName: string]: LeafFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ObjectRecordQueryFilter =
|
export type RecordGqlOperationFilter =
|
||||||
| LeafObjectRecordFilter
|
| LeafObjectRecordFilter
|
||||||
| AndObjectRecordFilter
|
| AndObjectRecordFilter
|
||||||
| OrObjectRecordFilter
|
| OrObjectRecordFilter
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||||
|
|
||||||
|
export type RecordGqlOperationFindManyResult = {
|
||||||
|
[objectNamePlural: string]: RecordGqlConnection;
|
||||||
|
};
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
|
|
||||||
|
export type RecordGqlOperationFindOneResult = {
|
||||||
|
[objectNameSingular: string]: RecordGqlNode;
|
||||||
|
};
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||||
|
|
||||||
|
export type RecordGqlOperationGqlRecordFields = RecordGqlFields;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
|
|
||||||
export type OrderByField = {
|
export type RecordGqlOperationOrderBy = {
|
||||||
[fieldName: string]: OrderBy | { [subFieldName: string]: OrderBy };
|
[fieldName: string]: OrderBy | { [subFieldName: string]: OrderBy };
|
||||||
};
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user