From de1489aabb2723e3c839258a2af4855ab6f498a0 Mon Sep 17 00:00:00 2001
From: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
Date: Tue, 22 Apr 2025 17:17:08 +0200
Subject: [PATCH] Time line activity wrong type (#11673)
# Introduction
Closes https://github.com/twentyhq/core-team-issues/issues/874
`TimeLineActivity` fields `` and `` were typed as required whereas in
reality are nullable, resulting in the related sentry error.
Refactored the type then the related components in order to handle
nullable use case
---
.../useLinkedObjectObjectMetadataItem.ts | 5 ++++-
.../hooks/useTimelineActivities.ts | 4 +++-
.../activity/components/EventRowActivity.tsx | 22 +++++++++++++------
.../components/EventRowCalendarEvent.tsx | 9 +++++---
.../message/components/EventRowMessage.tsx | 11 ++++++----
.../types/TimelineActivity.ts | 16 ++++++++++++--
6 files changed, 49 insertions(+), 18 deletions(-)
diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useLinkedObjectObjectMetadataItem.ts b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useLinkedObjectObjectMetadataItem.ts
index 9f230bef6..3bc1e9cbe 100644
--- a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useLinkedObjectObjectMetadataItem.ts
+++ b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useLinkedObjectObjectMetadataItem.ts
@@ -3,11 +3,14 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-export const useLinkedObjectObjectMetadataItem = (id: string) => {
+export const useLinkedObjectObjectMetadataItem = (id: string | null) => {
const objectMetadataItems: ObjectMetadataItem[] = useRecoilValue(
objectMetadataItemsState,
);
+ if (id === null) {
+ return null;
+ }
return (
objectMetadataItems.find(
(objectMetadataItem) => objectMetadataItem.id === id,
diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts
index 96c00233e..679b84213 100644
--- a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts
+++ b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts
@@ -6,6 +6,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
+import { isDefined } from 'twenty-shared/utils';
// do we need to test this?
export const useTimelineActivities = (
@@ -41,7 +42,8 @@ export const useTimelineActivities = (
const activityIds = timelineActivities
.filter((timelineActivity) => timelineActivity.name.match(/note|task/i))
- .map((timelineActivity) => timelineActivity.linkedRecordId);
+ .map((timelineActivity) => timelineActivity.linkedRecordId)
+ .filter(isDefined);
const { loading: loadingLinkedObjectsTitle } =
useLinkedObjectsTitle(activityIds);
diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx
index 308efaf2e..54d72d990 100644
--- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx
+++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx
@@ -5,12 +5,13 @@ import {
StyledEventRowItemAction,
StyledEventRowItemColumn,
} from '@/activities/timeline-activities/rows/components/EventRowDynamicComponent';
+import { isTimelineActivityWithLinkedRecord } from '@/activities/timeline-activities/types/TimelineActivity';
import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
import { isNonEmptyString } from '@sniptt/guards';
-import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
+import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
type EventRowActivityProps = EventRowDynamicComponentProps;
@@ -67,7 +68,7 @@ export const EventRowActivity = ({
const eventObject = eventLinkedObject.replace('linked-', '');
- if (!event.linkedRecordId) {
+ if (!isTimelineActivityWithLinkedRecord(event)) {
throw new Error('Could not find linked record id for event');
}
@@ -81,11 +82,18 @@ export const EventRowActivity = ({
const activityInStore = getActivityFromCache(event.linkedRecordId);
- const activityTitle = isNonEmptyString(activityInStore?.title)
- ? activityInStore?.title
- : isNonEmptyString(event.linkedRecordCachedName)
- ? event.linkedRecordCachedName
- : 'Untitled';
+ const computeActivityTitle = () => {
+ if (isNonEmptyString(activityInStore?.title)) {
+ return activityInStore?.title;
+ }
+
+ if (isNonEmptyString(event.linkedRecordCachedName)) {
+ return event.linkedRecordCachedName;
+ }
+
+ return 'Untitled';
+ };
+ const activityTitle = computeActivityTitle();
const { openRecordInCommandMenu } = useOpenRecordInCommandMenu();
diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/calendar/components/EventRowCalendarEvent.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/calendar/components/EventRowCalendarEvent.tsx
index a4172b8f4..128687a6f 100644
--- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/calendar/components/EventRowCalendarEvent.tsx
+++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/calendar/components/EventRowCalendarEvent.tsx
@@ -9,6 +9,7 @@ import {
StyledEventRowItemAction,
StyledEventRowItemColumn,
} from '@/activities/timeline-activities/rows/components/EventRowDynamicComponent';
+import { isTimelineActivityWithLinkedRecord } from '@/activities/timeline-activities/types/TimelineActivity';
type EventRowCalendarEventProps = EventRowDynamicComponentProps;
@@ -45,9 +46,11 @@ export const EventRowCalendarEvent = ({
-
-
-
+ {isTimelineActivityWithLinkedRecord(event) && (
+
+
+
+ )}
);
};
diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/message/components/EventRowMessage.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/message/components/EventRowMessage.tsx
index 00bd68e93..ddd5ed274 100644
--- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/message/components/EventRowMessage.tsx
+++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/message/components/EventRowMessage.tsx
@@ -9,6 +9,7 @@ import {
StyledEventRowItemColumn,
} from '@/activities/timeline-activities/rows/components/EventRowDynamicComponent';
import { EventCardMessage } from '@/activities/timeline-activities/rows/message/components/EventCardMessage';
+import { isTimelineActivityWithLinkedRecord } from '@/activities/timeline-activities/types/TimelineActivity';
type EventRowMessageProps = EventRowDynamicComponentProps;
@@ -49,10 +50,12 @@ export const EventRowMessage = ({
-
+ {isTimelineActivityWithLinkedRecord(event) && (
+
+ )}
);
diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/types/TimelineActivity.ts b/packages/twenty-front/src/modules/activities/timeline-activities/types/TimelineActivity.ts
index b3afecd68..a3caee5f7 100644
--- a/packages/twenty-front/src/modules/activities/timeline-activities/types/TimelineActivity.ts
+++ b/packages/twenty-front/src/modules/activities/timeline-activities/types/TimelineActivity.ts
@@ -1,3 +1,4 @@
+import { isDefined } from 'twenty-shared/utils';
import { WorkspaceMember } from '~/generated/graphql';
export type TimelineActivity = {
@@ -10,7 +11,18 @@ export type TimelineActivity = {
properties: any;
name: string;
linkedRecordCachedName: string;
- linkedRecordId: string;
- linkedObjectMetadataId: string;
+ linkedRecordId: string | null;
+ linkedObjectMetadataId: string | null;
__typename: 'TimelineActivity';
} & Record;
+
+export type TimelineActivityWithRecord = TimelineActivity & {
+ linkedRecordId: string;
+ linkedObjectMetadataId: string;
+};
+
+export const isTimelineActivityWithLinkedRecord = (
+ timelineActivity: TimelineActivity,
+): timelineActivity is TimelineActivityWithRecord =>
+ isDefined(timelineActivity.linkedObjectMetadataId) &&
+ isDefined(timelineActivity.linkedRecordId);