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:
Félix Malfait
2024-07-31 15:36:11 +02:00
committed by GitHub
parent defcee2a02
commit 80c0fc7ff1
239 changed files with 18418 additions and 8671 deletions

View File

@ -1,9 +1,9 @@
import { useContext } from 'react';
import styled from '@emotion/styled';
import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { useLinkedObject } from '@/activities/timeline/hooks/useLinkedObject';
import { TimelineActivityContext } from '@/activities/timelineActivities/contexts/TimelineActivityContext';
import { useLinkedObject } from '@/activities/timelineActivities/hooks/useLinkedObject';
import { EventIconDynamicComponent } from '@/activities/timelineActivities/rows/components/EventIconDynamicComponent';
import { EventRowDynamicComponent } from '@/activities/timelineActivities/rows/components/EventRowDynamicComponent';
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';

View File

@ -2,8 +2,8 @@ import styled from '@emotion/styled';
import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader';
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
import { TimelineCreateButtonGroup } from '@/activities/timeline/components/TimelineCreateButtonGroup';
import { EventList } from '@/activities/timelineActivities/components/EventList';
import { TimelineCreateButtonGroup } from '@/activities/timelineActivities/components/TimelineCreateButtonGroup';
import { useTimelineActivities } from '@/activities/timelineActivities/hooks/useTimelineActivities';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
@ -35,8 +35,10 @@ const StyledMainContainer = styled.div`
export const TimelineActivities = ({
targetableObject,
isInRightDrawer = false,
}: {
targetableObject: ActivityTargetableObject;
isInRightDrawer?: boolean;
}) => {
const { timelineActivities, loading, fetchMoreRecords } =
useTimelineActivities(targetableObject);
@ -63,7 +65,7 @@ export const TimelineActivities = ({
There are no activities associated with this record.{' '}
</AnimatedPlaceholderEmptySubTitle>
</AnimatedPlaceholderEmptyTextContainer>
<TimelineCreateButtonGroup />
<TimelineCreateButtonGroup isInRightDrawer={isInRightDrawer} />
</AnimatedPlaceholderEmptyContainer>
);
}

View File

@ -1,6 +1,7 @@
import { useActivities } from '@/activities/hooks/useActivities';
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline/constants/FindManyTimelineActivitiesOrderBy';
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timelineActivities/constants/FindManyTimelineActivitiesOrderBy';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { isDefined } from '~/utils/isDefined';
export const TimelineActivitiesQueryEffect = ({
@ -9,6 +10,8 @@ export const TimelineActivitiesQueryEffect = ({
targetableObject: ActivityTargetableObject;
}) => {
useActivities({
objectNameSingular:
targetableObject.targetObjectNameSingular as CoreObjectNameSingular,
targetableObjects: [targetableObject],
activitiesFilters: {},
activitiesOrderByVariables: FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY,

View File

@ -0,0 +1,42 @@
import { useSetRecoilState } from 'recoil';
import { IconCheckbox, IconNotes, IconPaperclip } from 'twenty-ui';
import { Button } from '@/ui/input/button/components/Button';
import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
import { TAB_LIST_COMPONENT_ID } from '@/ui/layout/show-page/components/ShowPageRightContainer';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
export const TimelineCreateButtonGroup = ({
isInRightDrawer = false,
}: {
isInRightDrawer?: boolean;
}) => {
const { activeTabIdState } = useTabList(
`${TAB_LIST_COMPONENT_ID}-${isInRightDrawer}`,
);
const setActiveTabId = useSetRecoilState(activeTabIdState);
return (
<ButtonGroup variant={'secondary'}>
<Button
Icon={IconNotes}
title="Note"
onClick={() => {
setActiveTabId('notes');
}}
/>
<Button
Icon={IconCheckbox}
title="Task"
onClick={() => {
setActiveTabId('tasks');
}}
/>
<Button
Icon={IconPaperclip}
title="File"
onClick={() => setActiveTabId('files')}
/>
</ButtonGroup>
);
};

View File

@ -0,0 +1,8 @@
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: RecordGqlOperationOrderBy =
[
{
createdAt: 'DescNullsFirst',
},
];

View File

@ -0,0 +1,16 @@
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export const useLinkedObject = (id: string) => {
const objectMetadataItems: ObjectMetadataItem[] = useRecoilValue(
objectMetadataItemsState,
);
return (
objectMetadataItems.find(
(objectMetadataItem) => objectMetadataItem.id === id,
) ?? null
);
};

View File

@ -1,7 +1,9 @@
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
// do we need to test this?
@ -12,6 +14,10 @@ export const useTimelineActivities = (
nameSingular: targetableObject.targetObjectNameSingular,
});
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.TimelineActivity,
});
const {
records: TimelineActivities,
loading,
@ -28,18 +34,7 @@ export const useTimelineActivities = (
createdAt: 'DescNullsFirst',
},
],
recordGqlFields: {
id: true,
createdAt: true,
linkedObjectMetadataId: true,
linkedRecordCachedName: true,
linkedRecordId: true,
name: true,
properties: true,
happensAt: true,
workspaceMember: true,
person: true,
},
recordGqlFields: generateDepthOneRecordGqlFields({ objectMetadataItem }),
fetchPolicy: 'cache-and-network',
});

View File

@ -7,6 +7,7 @@ import {
StyledEventRowItemAction,
StyledEventRowItemColumn,
} from '@/activities/timelineActivities/rows/components/EventRowDynamicComponent';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
type EventRowActivityProps = EventRowDynamicComponentProps;
@ -19,11 +20,10 @@ const StyledLinkedActivity = styled.span`
export const EventRowActivity = ({
event,
authorFullName,
}: EventRowActivityProps) => {
objectNameSingular,
}: EventRowActivityProps & { objectNameSingular: CoreObjectNameSingular }) => {
const [, eventAction] = event.name.split('.');
const openActivityRightDrawer = useOpenActivityRightDrawer();
if (!event.linkedRecordId) {
throw new Error('Could not find linked record id for event');
}
@ -32,6 +32,10 @@ export const EventRowActivity = ({
recordStoreFamilyState(event.linkedRecordId),
);
const openActivityRightDrawer = useOpenActivityRightDrawer({
objectNameSingular,
});
return (
<>
<StyledEventRowItemColumn>{authorFullName}</StyledEventRowItemColumn>

View File

@ -5,6 +5,7 @@ import { EventRowCalendarEvent } from '@/activities/timelineActivities/rows/cale
import { EventRowMainObject } from '@/activities/timelineActivities/rows/main-object/components/EventRowMainObject';
import { EventRowMessage } from '@/activities/timelineActivities/rows/message/components/EventRowMessage';
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export interface EventRowDynamicComponentProps {
@ -57,8 +58,7 @@ export const EventRowDynamicComponent = ({
authorFullName={authorFullName}
/>
);
case 'task':
case 'note':
case 'linked-task':
return (
<EventRowActivity
labelIdentifierValue={labelIdentifierValue}
@ -66,6 +66,18 @@ export const EventRowDynamicComponent = ({
mainObjectMetadataItem={mainObjectMetadataItem}
linkedObjectMetadataItem={linkedObjectMetadataItem}
authorFullName={authorFullName}
objectNameSingular={CoreObjectNameSingular.Task}
/>
);
case 'linked-note':
return (
<EventRowActivity
labelIdentifierValue={labelIdentifierValue}
event={event}
mainObjectMetadataItem={mainObjectMetadataItem}
linkedObjectMetadataItem={linkedObjectMetadataItem}
authorFullName={authorFullName}
objectNameSingular={CoreObjectNameSingular.Note}
/>
);
case mainObjectMetadataItem?.nameSingular:

View File

@ -0,0 +1,9 @@
import { atom } from 'recoil';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
export const objectShowPageTargetableObjectState =
atom<ActivityTargetableObject | null>({
key: 'objectShowPageTargetableObjectState',
default: null,
});

View File

@ -13,4 +13,4 @@ export type TimelineActivity = {
linkedRecordId: string;
linkedObjectMetadataId: string;
__typename: 'TimelineActivity';
};
} & Record<string, any>;