Activity as standard object (#6219)
In this PR I layout the first steps to migrate Activity to a traditional Standard objects Since this is a big transition, I'd rather split it into several deployments / PRs <img width="1512" alt="image" src="https://github.com/user-attachments/assets/012e2bbf-9d1b-4723-aaf6-269ef588b050"> --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: bosiraphael <71827178+bosiraphael@users.noreply.github.com> Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: Faisal-imtiyaz123 <142205282+Faisal-imtiyaz123@users.noreply.github.com> Co-authored-by: Prateek Jain <prateekj1171998@gmail.com>
This commit is contained in:
@ -6,6 +6,7 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useActivities } from '@/activities/hooks/useActivities';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||
|
||||
@ -20,16 +21,14 @@ const mockActivityTarget = {
|
||||
};
|
||||
|
||||
const mockActivity = {
|
||||
__typename: 'Activity',
|
||||
__typename: 'Note',
|
||||
updatedAt: '2021-08-03T19:20:06.000Z',
|
||||
createdAt: '2021-08-03T19:20:06.000Z',
|
||||
completedAt: '2021-08-03T19:20:06.000Z',
|
||||
status: 'DONE',
|
||||
reminderAt: '2021-08-03T19:20:06.000Z',
|
||||
title: 'title',
|
||||
authorId: '1',
|
||||
body: 'body',
|
||||
dueAt: '2021-08-03T19:20:06.000Z',
|
||||
type: 'type',
|
||||
assigneeId: '1',
|
||||
id: '234',
|
||||
};
|
||||
@ -102,13 +101,13 @@ const mocks: MockedResponse[] = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
query FindManyActivities(
|
||||
$filter: ActivityFilterInput
|
||||
$orderBy: [ActivityOrderByInput]
|
||||
query FindManyTasks(
|
||||
$filter: TaskFilterInput
|
||||
$orderBy: [TaskOrderByInput]
|
||||
$lastCursor: String
|
||||
$limit: Int
|
||||
) {
|
||||
activities(
|
||||
tasks(
|
||||
filter: $filter
|
||||
orderBy: $orderBy
|
||||
first: $limit
|
||||
@ -117,17 +116,13 @@ const mocks: MockedResponse[] = [
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
createdAt
|
||||
reminderAt
|
||||
authorId
|
||||
title
|
||||
completedAt
|
||||
updatedAt
|
||||
body
|
||||
dueAt
|
||||
type
|
||||
id
|
||||
assigneeId
|
||||
updatedAt
|
||||
createdAt
|
||||
body
|
||||
status
|
||||
dueAt
|
||||
}
|
||||
cursor
|
||||
}
|
||||
@ -178,6 +173,7 @@ describe('useActivities', () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useActivities({
|
||||
objectNameSingular: CoreObjectNameSingular.Task,
|
||||
targetableObjects: [],
|
||||
activitiesFilters: {},
|
||||
activitiesOrderByVariables: [{}],
|
||||
@ -200,6 +196,7 @@ describe('useActivities', () => {
|
||||
);
|
||||
|
||||
const activities = useActivities({
|
||||
objectNameSingular: CoreObjectNameSingular.Task,
|
||||
targetableObjects: [
|
||||
{ targetObjectNameSingular: 'company', id: '123' },
|
||||
],
|
||||
|
||||
@ -7,8 +7,8 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||
@ -16,108 +16,95 @@ const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
const cache = new InMemoryCache();
|
||||
|
||||
const activityNode = {
|
||||
id: '3ecaa1be-aac7-463a-a38e-64078dd451d5',
|
||||
const taskTarget = {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb300',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
reminderAt: null,
|
||||
title: 'My very first note',
|
||||
type: 'Note',
|
||||
body: '',
|
||||
dueAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
completedAt: null,
|
||||
author: null,
|
||||
assignee: null,
|
||||
assigneeId: null,
|
||||
authorId: null,
|
||||
comments: {
|
||||
edges: [],
|
||||
companyId: null,
|
||||
company: null,
|
||||
personId: '89bb825c-171e-4bcc-9cf7-43448d6fb280',
|
||||
person: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb280',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
city: 'City',
|
||||
name: {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
__typename: 'Person',
|
||||
},
|
||||
activityTargets: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb300',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
personId: null,
|
||||
companyId: '89bb825c-171e-4bcc-9cf7-43448d6fb280',
|
||||
company: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb280',
|
||||
name: 'Airbnb',
|
||||
domainName: {
|
||||
primaryLinkUrl: 'https://www.airbnb.com',
|
||||
primaryLinkLabel: '',
|
||||
secondaryLinks: null,
|
||||
},
|
||||
},
|
||||
person: null,
|
||||
activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
activity: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
},
|
||||
__typename: 'ActivityTarget',
|
||||
},
|
||||
__typename: 'ActivityTargetEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'ActivityTargetConnection',
|
||||
taskId: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
task: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
dueAt: null,
|
||||
body: '{}',
|
||||
title: 'Task title',
|
||||
assigneeId: null,
|
||||
__typename: 'Task',
|
||||
},
|
||||
__typename: 'Activity' as const,
|
||||
__typename: 'TaskTarget',
|
||||
};
|
||||
|
||||
cache.writeFragment({
|
||||
fragment: gql`
|
||||
fragment CreateOneActivityInCache on Activity {
|
||||
id
|
||||
createdAt
|
||||
fragment TaskTargetFragment on TaskTarget {
|
||||
__typename
|
||||
updatedAt
|
||||
reminderAt
|
||||
title
|
||||
body
|
||||
dueAt
|
||||
completedAt
|
||||
author
|
||||
assignee
|
||||
assigneeId
|
||||
authorId
|
||||
activityTargets {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
personId
|
||||
companyId
|
||||
company {
|
||||
id
|
||||
name
|
||||
domainName {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
secondaryLinks
|
||||
}
|
||||
}
|
||||
person
|
||||
activityId
|
||||
activity {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
__typename
|
||||
}
|
||||
createdAt
|
||||
personId
|
||||
taskId
|
||||
companyId
|
||||
id
|
||||
task {
|
||||
__typename
|
||||
createdAt
|
||||
title
|
||||
updatedAt
|
||||
body
|
||||
dueAt
|
||||
id
|
||||
assigneeId
|
||||
}
|
||||
person {
|
||||
__typename
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
city
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
__typename
|
||||
company {
|
||||
__typename
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`,
|
||||
id: activityNode.id,
|
||||
data: activityNode,
|
||||
id: `TaskTarget:${taskTarget.id}`,
|
||||
data: taskTarget,
|
||||
});
|
||||
|
||||
const task = {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
title: 'Task title',
|
||||
body: null,
|
||||
assigneeId: null,
|
||||
status: null,
|
||||
dueAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
assignee: null,
|
||||
__typename: 'Task' as any,
|
||||
taskTargets: [taskTarget],
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MockedProvider cache={cache}>
|
||||
@ -142,7 +129,8 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
);
|
||||
|
||||
const { activityTargetObjectRecords } = useActivityTargetObjectRecords(
|
||||
getRecordFromRecordNode({ recordNode: activityNode as any }),
|
||||
task,
|
||||
CoreObjectNameSingular.Task,
|
||||
);
|
||||
|
||||
return {
|
||||
@ -158,18 +146,17 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||
result.current.setObjectMetadataItems(mockObjectMetadataItems);
|
||||
});
|
||||
|
||||
const activityTargetObjectRecords =
|
||||
result.current.activityTargetObjectRecords;
|
||||
|
||||
expect(activityTargetObjectRecords).toHaveLength(1);
|
||||
expect(activityTargetObjectRecords[0].activityTarget).toEqual(
|
||||
activityNode.activityTargets.edges[0].node,
|
||||
);
|
||||
expect(activityTargetObjectRecords[0].activityTarget).toEqual(taskTarget);
|
||||
expect(activityTargetObjectRecords[0].targetObject).toEqual(
|
||||
activityNode.activityTargets.edges[0].node.company,
|
||||
taskTarget.person,
|
||||
);
|
||||
expect(
|
||||
activityTargetObjectRecords[0].targetObjectMetadataItem.nameSingular,
|
||||
).toEqual('company');
|
||||
).toEqual('person');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import gql from 'graphql-tag';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useActivityTargetsForTargetableObject } from '@/activities/hooks/useActivityTargetsForTargetableObject';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||
|
||||
const mockActivityTarget = {
|
||||
__typename: 'ActivityTarget',
|
||||
updatedAt: '2021-08-03T19:20:06.000Z',
|
||||
createdAt: '2021-08-03T19:20:06.000Z',
|
||||
personId: '1',
|
||||
activityId: '234',
|
||||
companyId: '1',
|
||||
id: '123',
|
||||
};
|
||||
|
||||
const defaultResponseData = {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
startCursor: '',
|
||||
endCursor: '',
|
||||
},
|
||||
totalCount: 1,
|
||||
};
|
||||
|
||||
const mocks: MockedResponse[] = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
query FindManyActivityTargets(
|
||||
$filter: ActivityTargetFilterInput
|
||||
$orderBy: [ActivityTargetOrderByInput]
|
||||
$lastCursor: String
|
||||
$limit: Int
|
||||
) {
|
||||
activityTargets(
|
||||
filter: $filter
|
||||
orderBy: $orderBy
|
||||
first: $limit
|
||||
after: $lastCursor
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
updatedAt
|
||||
createdAt
|
||||
personId
|
||||
activityId
|
||||
companyId
|
||||
id
|
||||
}
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
filter: { personId: { eq: '1234' } },
|
||||
orderBy: undefined,
|
||||
lastCursor: undefined,
|
||||
limit: undefined,
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
activityTargets: {
|
||||
...defaultResponseData,
|
||||
edges: [
|
||||
{
|
||||
node: mockActivityTarget,
|
||||
cursor: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||
{children}
|
||||
</SnackBarProviderScope>
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useActivityTargetsForTargetableObject', () => {
|
||||
it('works as expected', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const res = useActivityTargetsForTargetableObject({
|
||||
targetableObject: {
|
||||
id: '1234',
|
||||
targetObjectNameSingular: 'person',
|
||||
},
|
||||
});
|
||||
return { ...res, setCurrentWorkspaceMember };
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||
});
|
||||
|
||||
expect(result.current.loadingActivityTargets).toBe(true);
|
||||
|
||||
await waitFor(() => !result.current.loadingActivityTargets);
|
||||
|
||||
expect(result.current.activityTargets).toEqual([mockActivityTarget]);
|
||||
});
|
||||
});
|
||||
@ -1,106 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import gql from 'graphql-tag';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useCreateActivityInCache } from '@/activities/hooks/useCreateActivityInCache';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||
|
||||
const mocks: MockedResponse[] = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
query FindOneWorkspaceMember($objectRecordId: ID!) {
|
||||
workspaceMember(filter: { id: { eq: $objectRecordId } }) {
|
||||
__typename
|
||||
colorScheme
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
locale
|
||||
userId
|
||||
avatarUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { objectRecordId: '20202020-1553-45c6-a028-5a9064cce07f' },
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
workspaceMember: mockWorkspaceMembers[0],
|
||||
},
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||
{children}
|
||||
</SnackBarProviderScope>
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
describe('useCreateActivityInCache', () => {
|
||||
it('Should create activity in cache', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
const setRecordStore = useSetRecoilState(
|
||||
recordStoreFamilyState('1234'),
|
||||
);
|
||||
|
||||
const res = useCreateActivityInCache();
|
||||
return {
|
||||
...res,
|
||||
setCurrentWorkspaceMember,
|
||||
setObjectMetadataItems,
|
||||
setRecordStore,
|
||||
};
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setRecordStore({
|
||||
id: '1234',
|
||||
__typename: 'Person',
|
||||
});
|
||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||
result.current.setObjectMetadataItems(mockObjectMetadataItems);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
const res = result.current.createActivityInCache({
|
||||
type: 'Note',
|
||||
targetObject: {
|
||||
targetObjectNameSingular: 'person',
|
||||
id: '1234',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.createdActivityInCache).toHaveProperty('id');
|
||||
expect(res.createdActivityInCache).toHaveProperty('__typename');
|
||||
expect(res.createdActivityInCache).toHaveProperty('activityTargets');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -6,22 +6,16 @@ import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useCreateActivityInDB } from '@/activities/hooks/useCreateActivityInDB';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
import { mockedActivities } from '~/testing/mock-data/activities';
|
||||
import { mockedTasks } from '~/testing/mock-data/tasks';
|
||||
|
||||
const mockedDate = '2024-03-15T12:00:00.000Z';
|
||||
const toISOStringMock = jest.fn(() => mockedDate);
|
||||
global.Date.prototype.toISOString = toISOStringMock;
|
||||
|
||||
const mockedActivity = {
|
||||
...pick(mockedActivities[0], [
|
||||
'id',
|
||||
'title',
|
||||
'body',
|
||||
'type',
|
||||
'completedAt',
|
||||
'dueAt',
|
||||
]),
|
||||
...pick(mockedTasks[0], ['id', 'title', 'body', 'type', 'status', 'dueAt']),
|
||||
updatedAt: mockedDate,
|
||||
};
|
||||
|
||||
@ -36,7 +30,7 @@ const mocks: MockedResponse[] = [
|
||||
reminderAt
|
||||
authorId
|
||||
title
|
||||
completedAt
|
||||
status
|
||||
updatedAt
|
||||
body
|
||||
dueAt
|
||||
@ -77,14 +71,19 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
|
||||
describe('useCreateActivityInDB', () => {
|
||||
it('Should create activity in DB', async () => {
|
||||
const { result } = renderHook(() => useCreateActivityInDB(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useCreateActivityInDB({
|
||||
activityObjectNameSingular: CoreObjectNameSingular.Task,
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.createActivityInDB({
|
||||
...mockedActivity,
|
||||
__typename: 'Activity',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
@ -17,12 +17,12 @@ describe('useOpenActivityRightDrawer', () => {
|
||||
it('works as expected', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer({
|
||||
objectNameSingular: CoreObjectNameSingular.Task,
|
||||
});
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
|
||||
return {
|
||||
openActivityRightDrawer,
|
||||
activityIdInDrawer,
|
||||
viewableRecordId,
|
||||
};
|
||||
},
|
||||
@ -31,12 +31,10 @@ describe('useOpenActivityRightDrawer', () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.activityIdInDrawer).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
act(() => {
|
||||
result.current.openActivityRightDrawer('123');
|
||||
});
|
||||
expect(result.current.activityIdInDrawer).toBe('123');
|
||||
expect(result.current.viewableRecordId).toBe('123');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,23 +1,71 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import gql from 'graphql-tag';
|
||||
import pick from 'lodash.pick';
|
||||
import { mockedTasks } from '~/testing/mock-data/tasks';
|
||||
|
||||
const mockUUID = '37873e04-2f83-4468-9ab7-3f87da6cafad';
|
||||
const mockedDate = '2024-03-15T12:00:00.000Z';
|
||||
const toISOStringMock = jest.fn(() => mockedDate);
|
||||
global.Date.prototype.toISOString = toISOStringMock;
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: () => mockUUID,
|
||||
}));
|
||||
const mockedActivity = {
|
||||
...pick(mockedTasks[0], ['id', 'title', 'body', 'type', 'status', 'dueAt']),
|
||||
updatedAt: mockedDate,
|
||||
};
|
||||
|
||||
const mocks: MockedResponse[] = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
mutation CreateOneActivity($input: ActivityCreateInput!) {
|
||||
createActivity(data: $input) {
|
||||
__typename
|
||||
createdAt
|
||||
reminderAt
|
||||
authorId
|
||||
title
|
||||
status
|
||||
updatedAt
|
||||
body
|
||||
dueAt
|
||||
type
|
||||
id
|
||||
assigneeId
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: mockedActivity,
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: {
|
||||
createActivity: {
|
||||
...mockedActivity,
|
||||
__typename: 'Activity',
|
||||
assigneeId: '',
|
||||
authorId: '1',
|
||||
reminderAt: null,
|
||||
createdAt: mockedDate,
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MockedProvider addTypename={false}>{children}</MockedProvider>
|
||||
<MockedProvider addTypename={false} mocks={mocks}>
|
||||
{children}
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
@ -27,15 +75,15 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
it('works as expected', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const openActivityRightDrawer = useOpenCreateActivityDrawer();
|
||||
const openActivityRightDrawer = useOpenCreateActivityDrawer({
|
||||
activityObjectNameSingular: CoreObjectNameSingular.Note,
|
||||
});
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
return {
|
||||
openActivityRightDrawer,
|
||||
activityIdInDrawer,
|
||||
viewableRecordId,
|
||||
setObjectMetadataItems,
|
||||
};
|
||||
@ -49,15 +97,11 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
result.current.setObjectMetadataItems(mockObjectMetadataItems);
|
||||
});
|
||||
|
||||
expect(result.current.activityIdInDrawer).toBeNull();
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
await act(async () => {
|
||||
result.current.openActivityRightDrawer({
|
||||
type: 'Note',
|
||||
targetableObjects: [],
|
||||
});
|
||||
});
|
||||
expect(result.current.activityIdInDrawer).toBe(mockUUID);
|
||||
expect(result.current.viewableRecordId).toBe(mockUUID);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,21 +3,25 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { findActivitiesOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory';
|
||||
import { useActivityTargetsForTargetableObjects } from '@/activities/hooks/useActivityTargetsForTargetableObjects';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||
|
||||
export const useActivities = ({
|
||||
export const useActivities = <T extends Task | Note>({
|
||||
objectNameSingular,
|
||||
targetableObjects,
|
||||
activitiesFilters,
|
||||
activitiesOrderByVariables,
|
||||
skip,
|
||||
}: {
|
||||
objectNameSingular: CoreObjectNameSingular;
|
||||
targetableObjects: ActivityTargetableObject[];
|
||||
activitiesFilters: RecordGqlOperationFilter;
|
||||
activitiesOrderByVariables: RecordGqlOperationOrderBy;
|
||||
@ -27,6 +31,7 @@ export const useActivities = ({
|
||||
|
||||
const { activityTargets, loadingActivityTargets } =
|
||||
useActivityTargetsForTargetableObjects({
|
||||
objectNameSingular,
|
||||
targetableObjects,
|
||||
skip: skip,
|
||||
});
|
||||
@ -36,7 +41,10 @@ export const useActivities = ({
|
||||
activityTargets
|
||||
? [
|
||||
...activityTargets
|
||||
.map((activityTarget) => activityTarget.activityId)
|
||||
.map(
|
||||
(activityTarget) =>
|
||||
activityTarget.taskId ?? activityTarget.noteId,
|
||||
)
|
||||
.filter(isNonEmptyString),
|
||||
].sort(sortByAscString)
|
||||
: [],
|
||||
@ -54,10 +62,13 @@ export const useActivities = ({
|
||||
};
|
||||
|
||||
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
|
||||
findActivitiesOperationSignatureFactory({ objectMetadataItems });
|
||||
findActivitiesOperationSignatureFactory({
|
||||
objectMetadataItems,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { records: activities, loading: loadingActivities } =
|
||||
useFindManyRecords<Activity>({
|
||||
useFindManyRecords<Task | Note>({
|
||||
skip: skip || loadingActivityTargets,
|
||||
objectNameSingular:
|
||||
FIND_ACTIVITIES_OPERATION_SIGNATURE.objectNameSingular,
|
||||
@ -76,7 +87,7 @@ export const useActivities = ({
|
||||
});
|
||||
|
||||
return {
|
||||
activities,
|
||||
activities: activities as T[],
|
||||
loading: loadingActivities || loadingActivityTargets,
|
||||
};
|
||||
};
|
||||
|
||||
@ -2,31 +2,41 @@ import { useApolloClient } from '@apollo/client';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Nullable } from 'twenty-ui';
|
||||
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useActivityTargetObjectRecords = (activity: Activity) => {
|
||||
export const useActivityTargetObjectRecords = (
|
||||
activity: Task | Note,
|
||||
objectNameSingular: CoreObjectNameSingular,
|
||||
) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const activityTargets = activity.activityTargets ?? [];
|
||||
const activityTargets =
|
||||
'noteTargets' in activity && activity.noteTargets
|
||||
? activity.noteTargets
|
||||
: 'taskTargets' in activity && activity.taskTargets
|
||||
? activity.taskTargets
|
||||
: [];
|
||||
|
||||
const getRecordFromCache = useGetRecordFromCache({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
objectNameSingular: getJoinObjectNameSingular(objectNameSingular),
|
||||
});
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const activityTargetObjectRecords = activityTargets
|
||||
.map<Nullable<ActivityTargetWithTargetRecord>>((activityTarget) => {
|
||||
const activityTargetFromCache = getRecordFromCache<ActivityTarget>(
|
||||
activityTarget.id,
|
||||
apolloClient.cache,
|
||||
);
|
||||
const activityTargetFromCache = getRecordFromCache<
|
||||
NoteTarget | TaskTarget
|
||||
>(activityTarget.id, apolloClient.cache);
|
||||
|
||||
if (!isDefined(activityTargetFromCache)) {
|
||||
throw new Error(
|
||||
@ -37,7 +47,9 @@ export const useActivityTargetObjectRecords = (activity: Activity) => {
|
||||
const correspondingObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
isDefined(activityTargetFromCache[objectMetadataItem.nameSingular]) &&
|
||||
!objectMetadataItem.isSystem,
|
||||
![CoreObjectNameSingular.Note, CoreObjectNameSingular.Task].includes(
|
||||
objectMetadataItem.nameSingular as CoreObjectNameSingular,
|
||||
),
|
||||
);
|
||||
|
||||
if (!correspondingObjectMetadataItem) {
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
|
||||
export const useActivityTargetsForTargetableObject = ({
|
||||
targetableObject,
|
||||
}: {
|
||||
targetableObject: ActivityTargetableObject;
|
||||
}) => {
|
||||
const targetObjectFieldName = getActivityTargetObjectFieldIdName({
|
||||
nameSingular: targetableObject.targetObjectNameSingular,
|
||||
});
|
||||
|
||||
const targetableObjectId = targetableObject.id;
|
||||
|
||||
const skipRequest = !isNonEmptyString(targetableObjectId);
|
||||
|
||||
// TODO: We want to optimistically remove from this request
|
||||
// If we are on a show page and we remove the current show page object corresponding activity target
|
||||
// See also if we need to update useTimelineActivities
|
||||
const { records: activityTargets, loading: loadingActivityTargets } =
|
||||
useFindManyRecords<ActivityTarget>({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
skip: skipRequest,
|
||||
filter: {
|
||||
[targetObjectFieldName]: {
|
||||
eq: targetableObject.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
activityTargets,
|
||||
loadingActivityTargets,
|
||||
};
|
||||
};
|
||||
@ -1,23 +1,27 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { findActivityTargetsOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getActivityTargetsFilter } from '@/activities/utils/getActivityTargetsFilter';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
|
||||
export const useActivityTargetsForTargetableObjects = ({
|
||||
objectNameSingular,
|
||||
targetableObjects,
|
||||
skip,
|
||||
onCompleted,
|
||||
}: {
|
||||
objectNameSingular: CoreObjectNameSingular;
|
||||
targetableObjects: Pick<
|
||||
ActivityTargetableObject,
|
||||
'id' | 'targetObjectNameSingular'
|
||||
>[];
|
||||
skip?: boolean;
|
||||
onCompleted?: (activityTargets: ActivityTarget[]) => void;
|
||||
onCompleted?: (activityTargets: (TaskTarget | NoteTarget)[]) => void;
|
||||
}) => {
|
||||
const activityTargetsFilter = getActivityTargetsFilter({
|
||||
targetableObjects: targetableObjects,
|
||||
@ -26,13 +30,16 @@ export const useActivityTargetsForTargetableObjects = ({
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE =
|
||||
findActivityTargetsOperationSignatureFactory({ objectMetadataItems });
|
||||
findActivityTargetsOperationSignatureFactory({
|
||||
objectNameSingular,
|
||||
objectMetadataItems,
|
||||
});
|
||||
|
||||
// TODO: We want to optimistically remove from this request
|
||||
// If we are on a show page and we remove the current show page object corresponding activity target
|
||||
// See also if we need to update useTimelineActivities
|
||||
const { records: activityTargets, loading: loadingActivityTargets } =
|
||||
useFindManyRecords<ActivityTarget>({
|
||||
useFindManyRecords<TaskTarget | NoteTarget>({
|
||||
skip,
|
||||
objectNameSingular:
|
||||
FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE.objectNameSingular,
|
||||
|
||||
@ -1,188 +0,0 @@
|
||||
import { Reference, useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { Activity, ActivityType } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { makeActivityTargetsToCreateFromTargetableObjects } from '@/activities/utils/getActivityTargetsToCreateFromTargetableObjects';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateManyRecordsInCache } from '@/object-record/cache/hooks/useCreateManyRecordsInCache';
|
||||
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
|
||||
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
||||
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateActivityInCache = () => {
|
||||
const { createManyRecordsInCache: createManyActivityTargetsInCache } =
|
||||
useCreateManyRecordsInCache<ActivityTarget>({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
});
|
||||
|
||||
const cache = useApolloClient().cache;
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { record: currentWorkspaceMemberRecord } = useFindOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
||||
objectRecordId: currentWorkspaceMember?.id,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: objectMetadataItemActivity } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: objectMetadataItemActivityTarget } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
});
|
||||
|
||||
const createOneActivityInCache = useCreateOneRecordInCache<Activity>({
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
const createActivityInCache = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
({
|
||||
type,
|
||||
targetObject,
|
||||
customAssignee,
|
||||
}: {
|
||||
type: ActivityType;
|
||||
targetObject?: ActivityTargetableObject;
|
||||
customAssignee?: WorkspaceMember;
|
||||
}) => {
|
||||
const activityId = v4();
|
||||
|
||||
const createdActivityInCache = createOneActivityInCache({
|
||||
id: activityId,
|
||||
__typename: 'Activity',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
author: currentWorkspaceMemberRecord,
|
||||
authorId: currentWorkspaceMemberRecord?.id,
|
||||
assignee: customAssignee ?? currentWorkspaceMemberRecord,
|
||||
assigneeId: customAssignee?.id ?? currentWorkspaceMemberRecord?.id,
|
||||
type,
|
||||
});
|
||||
|
||||
if (isUndefinedOrNull(createdActivityInCache)) {
|
||||
throw new Error('Failed to create activity in cache');
|
||||
}
|
||||
|
||||
if (isUndefinedOrNull(targetObject)) {
|
||||
set(recordStoreFamilyState(activityId), {
|
||||
...createdActivityInCache,
|
||||
activityTargets: [],
|
||||
comments: [],
|
||||
});
|
||||
|
||||
return {
|
||||
createdActivityInCache: {
|
||||
...createdActivityInCache,
|
||||
activityTargets: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const targetObjectRecord = snapshot
|
||||
.getLoadable(recordStoreFamilyState(targetObject.id))
|
||||
.getValue();
|
||||
|
||||
if (isUndefinedOrNull(targetObjectRecord)) {
|
||||
throw new Error('Failed to find target object record');
|
||||
}
|
||||
|
||||
const activityTargetsToCreate =
|
||||
makeActivityTargetsToCreateFromTargetableObjects({
|
||||
activity: createdActivityInCache,
|
||||
targetableObjects: [targetObject],
|
||||
targetObjectRecords: [targetObjectRecord],
|
||||
});
|
||||
|
||||
const createdActivityTargetsInCache = createManyActivityTargetsInCache(
|
||||
activityTargetsToCreate,
|
||||
);
|
||||
|
||||
const activityTargetsConnection = getRecordConnectionFromRecords({
|
||||
objectMetadataItems: objectMetadataItems,
|
||||
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
records: createdActivityTargetsInCache,
|
||||
withPageInfo: false,
|
||||
computeReferences: true,
|
||||
isRootLevel: false,
|
||||
});
|
||||
|
||||
modifyRecordFromCache({
|
||||
recordId: createdActivityInCache.id,
|
||||
cache,
|
||||
fieldModifiers: {
|
||||
activityTargets: () => activityTargetsConnection,
|
||||
},
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
const targetObjectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.nameSingular === targetObject.targetObjectNameSingular,
|
||||
);
|
||||
|
||||
if (isDefined(targetObjectMetadataItem)) {
|
||||
modifyRecordFromCache({
|
||||
cache,
|
||||
objectMetadataItem: targetObjectMetadataItem,
|
||||
recordId: targetObject.id,
|
||||
fieldModifiers: {
|
||||
activityTargets: (activityTargetsRef, { readField }) => {
|
||||
const edges = readField<{ node: Reference }[]>(
|
||||
'edges',
|
||||
activityTargetsRef,
|
||||
);
|
||||
|
||||
if (!edges) return activityTargetsRef;
|
||||
|
||||
return {
|
||||
...activityTargetsRef,
|
||||
edges: [...edges, ...activityTargetsConnection.edges],
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
set(recordStoreFamilyState(activityId), {
|
||||
...createdActivityInCache,
|
||||
activityTargets: createdActivityTargetsInCache,
|
||||
comments: [],
|
||||
});
|
||||
|
||||
return {
|
||||
createdActivityInCache: {
|
||||
...createdActivityInCache,
|
||||
activityTargets: createdActivityTargetsInCache,
|
||||
},
|
||||
};
|
||||
},
|
||||
[
|
||||
createOneActivityInCache,
|
||||
currentWorkspaceMemberRecord,
|
||||
createManyActivityTargetsInCache,
|
||||
objectMetadataItems,
|
||||
objectMetadataItemActivityTarget,
|
||||
cache,
|
||||
objectMetadataItemActivity,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
createActivityInCache,
|
||||
};
|
||||
};
|
||||
@ -1,8 +1,6 @@
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
|
||||
import { CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE } from '@/activities/graphql/operation-signatures/CreateOneActivityOperationSignature';
|
||||
import { ActivityForEditor } from '@/activities/types/ActivityForEditor';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@ -13,33 +11,48 @@ import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { createOneActivityOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/createOneActivityOperationSignatureFactory';
|
||||
import { NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useCreateActivityInDB = () => {
|
||||
export const useCreateActivityInDB = ({
|
||||
activityObjectNameSingular,
|
||||
}: {
|
||||
activityObjectNameSingular:
|
||||
| CoreObjectNameSingular.Task
|
||||
| CoreObjectNameSingular.Note;
|
||||
}) => {
|
||||
const createOneActivityOperationSignature =
|
||||
createOneActivityOperationSignatureFactory({
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const { createOneRecord: createOneActivity } = useCreateOneRecord({
|
||||
objectNameSingular:
|
||||
CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.objectNameSingular,
|
||||
recordGqlFields: CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.fields,
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
recordGqlFields: createOneActivityOperationSignature.fields,
|
||||
shouldMatchRootQueryFilter: true,
|
||||
});
|
||||
|
||||
const { createManyRecords: createManyActivityTargets } =
|
||||
useCreateManyRecords<ActivityTarget>({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
shouldMatchRootQueryFilter: true,
|
||||
});
|
||||
const { createManyRecords: createManyActivityTargets } = useCreateManyRecords<
|
||||
TaskTarget | NoteTarget
|
||||
>({
|
||||
objectNameSingular: getJoinObjectNameSingular(activityObjectNameSingular),
|
||||
shouldMatchRootQueryFilter: true,
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectMetadataItem: objectMetadataItemActivityTarget } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
objectNameSingular: getJoinObjectNameSingular(activityObjectNameSingular),
|
||||
});
|
||||
|
||||
const { objectMetadataItem: objectMetadataItemActivity } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const cache = useApolloClient().cache;
|
||||
@ -52,7 +65,8 @@ export const useCreateActivityInDB = () => {
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const activityTargetsToCreate = activityToCreate.activityTargets ?? [];
|
||||
const activityTargetsToCreate =
|
||||
activityToCreate.noteTargets ?? activityToCreate.taskTargets ?? [];
|
||||
|
||||
if (isNonEmptyArray(activityTargetsToCreate)) {
|
||||
await createManyActivityTargets(activityTargetsToCreate);
|
||||
|
||||
@ -1,25 +1,34 @@
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
export const useOpenActivityRightDrawer = () => {
|
||||
export const useOpenActivityRightDrawer = ({
|
||||
objectNameSingular,
|
||||
}: {
|
||||
objectNameSingular: CoreObjectNameSingular;
|
||||
}) => {
|
||||
const { openRightDrawer, isRightDrawerOpen, rightDrawerPage } =
|
||||
useRightDrawer();
|
||||
const [viewableRecordId, setViewableRecordId] = useRecoilState(
|
||||
viewableRecordIdState,
|
||||
);
|
||||
const setActivityIdInDrawer = useSetRecoilState(activityIdInDrawerState);
|
||||
|
||||
const setViewableRecordNameSingular = useSetRecoilState(
|
||||
viewableRecordNameSingularState,
|
||||
);
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
return (activityId: string) => {
|
||||
if (
|
||||
isRightDrawerOpen &&
|
||||
rightDrawerPage === RightDrawerPages.EditActivity &&
|
||||
rightDrawerPage === RightDrawerPages.ViewRecord &&
|
||||
viewableRecordId === activityId
|
||||
) {
|
||||
return;
|
||||
@ -27,7 +36,7 @@ export const useOpenActivityRightDrawer = () => {
|
||||
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableRecordId(activityId);
|
||||
setActivityIdInDrawer(activityId);
|
||||
openRightDrawer(RightDrawerPages.EditActivity);
|
||||
setViewableRecordNameSingular(objectNameSingular);
|
||||
openRightDrawer(RightDrawerPages.ViewRecord);
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useCreateActivityInCache } from '@/activities/hooks/useCreateActivityInCache';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { activityTargetableEntityArrayState } from '@/activities/states/activityTargetableEntityArrayState';
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { temporaryActivityForEditorState } from '@/activities/states/temporaryActivityForEditorState';
|
||||
import { ActivityType } from '@/activities/types/Activity';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||
@ -14,54 +9,84 @@ import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPage
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
||||
import { ActivityTargetableObject } from '../types/ActivityTargetableEntity';
|
||||
|
||||
export const useOpenCreateActivityDrawer = () => {
|
||||
export const useOpenCreateActivityDrawer = ({
|
||||
activityObjectNameSingular,
|
||||
}: {
|
||||
activityObjectNameSingular:
|
||||
| CoreObjectNameSingular.Note
|
||||
| CoreObjectNameSingular.Task;
|
||||
}) => {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
const { createActivityInCache } = useCreateActivityInCache();
|
||||
const { createOneRecord: createOneActivity } = useCreateOneRecord<
|
||||
Task | Note
|
||||
>({
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const { createOneRecord: createOneActivityTarget } = useCreateOneRecord<
|
||||
TaskTarget | NoteTarget
|
||||
>({
|
||||
objectNameSingular:
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Task
|
||||
? CoreObjectNameSingular.TaskTarget
|
||||
: CoreObjectNameSingular.NoteTarget,
|
||||
});
|
||||
|
||||
const setActivityTargetableEntityArray = useSetRecoilState(
|
||||
activityTargetableEntityArrayState,
|
||||
);
|
||||
const setViewableRecordId = useSetRecoilState(viewableRecordIdState);
|
||||
|
||||
const setIsCreatingActivity = useSetRecoilState(isActivityInCreateModeState);
|
||||
|
||||
const setTemporaryActivityForEditor = useSetRecoilState(
|
||||
temporaryActivityForEditorState,
|
||||
const setViewableRecordNameSingular = useSetRecoilState(
|
||||
viewableRecordNameSingularState,
|
||||
);
|
||||
|
||||
const setActivityIdInDrawer = useSetRecoilState(activityIdInDrawerState);
|
||||
|
||||
const [, setIsUpsertingActivityInDB] = useRecoilState(
|
||||
const setIsUpsertingActivityInDB = useSetRecoilState(
|
||||
isUpsertingActivityInDBState,
|
||||
);
|
||||
|
||||
const openCreateActivityDrawer = async ({
|
||||
type,
|
||||
targetableObjects,
|
||||
customAssignee,
|
||||
}: {
|
||||
type: ActivityType;
|
||||
targetableObjects: ActivityTargetableObject[];
|
||||
customAssignee?: WorkspaceMember;
|
||||
}) => {
|
||||
const { createdActivityInCache } = createActivityInCache({
|
||||
type,
|
||||
targetObject: targetableObjects[0],
|
||||
customAssignee,
|
||||
const activity = await createOneActivity({
|
||||
assigneeId: customAssignee?.id,
|
||||
});
|
||||
|
||||
const targetableObjectRelationIdName = `${targetableObjects[0].targetObjectNameSingular}Id`;
|
||||
|
||||
await createOneActivityTarget({
|
||||
taskId:
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Task
|
||||
? activity.id
|
||||
: undefined,
|
||||
noteId:
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Note
|
||||
? activity.id
|
||||
: undefined,
|
||||
[targetableObjectRelationIdName]: targetableObjects[0].id,
|
||||
});
|
||||
|
||||
setActivityIdInDrawer(createdActivityInCache.id);
|
||||
setTemporaryActivityForEditor(createdActivityInCache);
|
||||
setIsCreatingActivity(true);
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableRecordId(createdActivityInCache.id);
|
||||
setViewableRecordId(activity.id);
|
||||
setViewableRecordNameSingular(activityObjectNameSingular);
|
||||
setActivityTargetableEntityArray(targetableObjects ?? []);
|
||||
openRightDrawer(RightDrawerPages.CreateActivity);
|
||||
|
||||
openRightDrawer(RightDrawerPages.ViewRecord);
|
||||
setIsUpsertingActivityInDB(false);
|
||||
};
|
||||
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { findActivitiesOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@ -14,14 +16,18 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const usePrepareFindManyActivitiesQuery = () => {
|
||||
export const usePrepareFindManyActivitiesQuery = ({
|
||||
activityObjectNameSingular,
|
||||
}: {
|
||||
activityObjectNameSingular: CoreObjectNameSingular;
|
||||
}) => {
|
||||
const { objectMetadataItem: objectMetadataItemActivity } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const getActivityFromCache = useGetRecordFromCache({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const cache = useApolloClient().cache;
|
||||
@ -39,7 +45,7 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
||||
}: {
|
||||
additionalFilter?: Record<string, unknown>;
|
||||
targetableObject: ActivityTargetableObject;
|
||||
shouldActivityBeExcluded?: (activityTarget: Activity) => boolean;
|
||||
shouldActivityBeExcluded?: (activityTarget: Task | Note) => boolean;
|
||||
}) => {
|
||||
const targetableObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
@ -60,8 +66,10 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
||||
cache,
|
||||
});
|
||||
|
||||
const activityTargets: ActivityTarget[] =
|
||||
targetableObjectRecord?.activityTargets ?? [];
|
||||
const activityTargets: (TaskTarget | NoteTarget)[] =
|
||||
targetableObjectRecord?.taskTargets ??
|
||||
targetableObjectRecord?.noteTargets ??
|
||||
[];
|
||||
|
||||
const activityTargetIds = [
|
||||
...new Set(
|
||||
@ -71,7 +79,7 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
||||
),
|
||||
];
|
||||
|
||||
const activities: Activity[] = activityTargetIds
|
||||
const activities: (Task | Note)[] = activityTargetIds
|
||||
.map((activityTargetId) => {
|
||||
const activityTarget = activityTargets.find(
|
||||
(activityTarget) => activityTarget.id === activityTargetId,
|
||||
@ -81,7 +89,7 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getActivityFromCache<Activity>(activityTarget.activityId);
|
||||
return getActivityFromCache<Task | Note>(activityTarget.activityId);
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
@ -103,7 +111,10 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
||||
});
|
||||
|
||||
const FIND_ACTIVITIES_OPERATION_SIGNATURE =
|
||||
findActivitiesOperationSignatureFactory({ objectMetadataItems });
|
||||
findActivitiesOperationSignatureFactory({
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
objectMetadataItems,
|
||||
});
|
||||
|
||||
upsertFindManyActivitiesInCache({
|
||||
objectRecordsToOverwrite: filteredActivities,
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { usePrepareFindManyActivitiesQuery } from '@/activities/hooks/usePrepareFindManyActivitiesQuery';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline/states/objectShowPageTargetableObjectIdState';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timelineActivities/states/objectShowPageTargetableObjectIdState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
// This hook should only be executed if the normalized cache is up-to-date
|
||||
// It will take a targetableObject and prepare the queries for the activities
|
||||
// based on the activityTargets of the targetableObject
|
||||
export const useRefreshShowPageFindManyActivitiesQueries = () => {
|
||||
export const useRefreshShowPageFindManyActivitiesQueries = ({
|
||||
activityObjectNameSingular,
|
||||
}: {
|
||||
activityObjectNameSingular: CoreObjectNameSingular;
|
||||
}) => {
|
||||
const objectShowPageTargetableObject = useRecoilValue(
|
||||
objectShowPageTargetableObjectState,
|
||||
);
|
||||
|
||||
const { prepareFindManyActivitiesQuery } =
|
||||
usePrepareFindManyActivitiesQuery();
|
||||
const { prepareFindManyActivitiesQuery } = usePrepareFindManyActivitiesQuery({
|
||||
activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const refreshShowPageFindManyActivitiesQueries = () => {
|
||||
if (isDefined(objectShowPageTargetableObject)) {
|
||||
@ -24,21 +29,12 @@ export const useRefreshShowPageFindManyActivitiesQueries = () => {
|
||||
prepareFindManyActivitiesQuery({
|
||||
targetableObject: objectShowPageTargetableObject,
|
||||
additionalFilter: {
|
||||
completedAt: { is: 'NULL' },
|
||||
type: { eq: 'Task' },
|
||||
},
|
||||
shouldActivityBeExcluded: (activity: Activity) => {
|
||||
return activity.type !== 'Task';
|
||||
status: { eq: 'TODO' },
|
||||
},
|
||||
});
|
||||
prepareFindManyActivitiesQuery({
|
||||
targetableObject: objectShowPageTargetableObject,
|
||||
additionalFilter: {
|
||||
type: { eq: 'Note' },
|
||||
},
|
||||
shouldActivityBeExcluded: (activity: Activity) => {
|
||||
return activity.type !== 'Note';
|
||||
},
|
||||
additionalFilter: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,50 +1,58 @@
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useCreateActivityInDB } from '@/activities/hooks/useCreateActivityInDB';
|
||||
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
|
||||
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline/states/objectShowPageTargetableObjectIdState';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timelineActivities/states/objectShowPageTargetableObjectIdState';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useUpsertActivity = () => {
|
||||
const [isActivityInCreateMode, setIsActivityInCreateMode] = useRecoilState(
|
||||
isActivityInCreateModeState,
|
||||
);
|
||||
export const useUpsertActivity = ({
|
||||
activityObjectNameSingular,
|
||||
}: {
|
||||
activityObjectNameSingular:
|
||||
| CoreObjectNameSingular.Task
|
||||
| CoreObjectNameSingular.Note;
|
||||
}) => {
|
||||
const [isActivityInCreateMode] = useRecoilState(isActivityInCreateModeState);
|
||||
|
||||
const { updateOneRecord: updateOneActivity } = useUpdateOneRecord<Activity>({
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
const { updateOneRecord: updateOneActivity } = useUpdateOneRecord<
|
||||
Task | Note
|
||||
>({
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const { createActivityInDB } = useCreateActivityInDB();
|
||||
const { createActivityInDB } = useCreateActivityInDB({
|
||||
activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const [, setIsUpsertingActivityInDB] = useRecoilState(
|
||||
isUpsertingActivityInDBState,
|
||||
);
|
||||
|
||||
const setActivityIdInDrawer = useSetRecoilState(activityIdInDrawerState);
|
||||
|
||||
const objectShowPageTargetableObject = useRecoilValue(
|
||||
objectShowPageTargetableObjectState,
|
||||
);
|
||||
|
||||
const { refreshShowPageFindManyActivitiesQueries } =
|
||||
useRefreshShowPageFindManyActivitiesQueries();
|
||||
useRefreshShowPageFindManyActivitiesQueries({
|
||||
activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const upsertActivity = async ({
|
||||
activity,
|
||||
input,
|
||||
}: {
|
||||
activity: Activity;
|
||||
input: Partial<Activity>;
|
||||
activity: Task | Note;
|
||||
input: Partial<Task | Note>;
|
||||
}) => {
|
||||
setIsUpsertingActivityInDB(true);
|
||||
if (isActivityInCreateMode) {
|
||||
const activityToCreate: Activity = {
|
||||
const activityToCreate: Partial<Task | Note> = {
|
||||
...activity,
|
||||
...input,
|
||||
};
|
||||
@ -54,9 +62,6 @@ export const useUpsertActivity = () => {
|
||||
}
|
||||
|
||||
await createActivityInDB(activityToCreate);
|
||||
|
||||
setActivityIdInDrawer(activityToCreate.id);
|
||||
setIsActivityInCreateMode(false);
|
||||
} else {
|
||||
await updateOneActivity?.({
|
||||
idToUpdate: activity.id,
|
||||
|
||||
Reference in New Issue
Block a user