Skip to content

Conversation

@mabdullahabaid
Copy link
Member

@mabdullahabaid mabdullahabaid commented Dec 3, 2025

Solves #16015

  • Added a check in useTimelineActivities.ts to see if the system object page we're viewing has timelineActivity being tracked before querying to get the activity history.
  • Removed @WorkspaceIsObjectUIReadOnly decorator from system objects and added @WorkspaceIsFieldUIReadOnly to standard and system fields to allow custom field edits as requested in the issue.
  • Did not add calendarEvents or other system objects to timelineActivity just yet since keeping track of timeline activity for every one of them felt counter-intuitive and bloated. In order to determine which objects need timelineActivity, I think we need to fix the broken views of system objects first, such as the one in the attached screenshots - a good number of them are broken. The check I added to useTimelineActivities.ts hook displays timeline activity as empty for the time - error would not be shown as suggested by Thomas.
image
image
  • Editing custom fields on calendar events (and other objects without position fields) crashed with TypeError: Cannot convert undefined or null to object in sortCachedObjectEdges. This happened because some cached queries had empty orderBy arrays ([]), and the optimistic effect tried to sort with them. Objects without position fields returned empty orderBy arrays when there were no sorts, while objects with position fields automatically got [{ position: 'AscNullsFirst' }]. The backend always adds { id: 'AscNullsFirst' } as a fallback, but the frontend didn't match this. So, I added { id: 'AscNullsFirst' } to the Frontend as default orderBy when there are no sorts and no position field.

@mabdullahabaid
Copy link
Member Author

@charlesBochet

  • Is anyone already looking into the broken views for system object pages?
  • What are your thoughts on catering to timelineActivity records for system objects? Shall we add independent fields for each system object or should we treat them similar to custom objects (e.g. a dynamic relation)?
image

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 3, 2025

Greptile Overview

Greptile Summary

This PR fixes two critical issues preventing custom field editing on system object detail pages: timeline activity query errors and optimistic cache sorting crashes.

Key Changes:

  • Fixed timeline activity query error by checking if TimelineActivity has the corresponding field before querying, preventing GraphQL errors on system objects without timeline tracking
  • Fixed sortCachedObjectEdges crash by ensuring turnSortsIntoOrderBy always returns a non-empty orderBy array with {id: 'AscNullsFirst'} as fallback, matching backend behavior
  • Enabled custom field editing on system objects by replacing @WorkspaceIsObjectUIReadOnly decorator with field-level @WorkspaceIsFieldUIReadOnly on all standard/system fields across 24 workspace entities
  • Refactored system object timeline tracking logic by extracting hardcoded object names into a centralized constant SYSTEM_OBJECTS_WITH_TIMELINE_ACTIVITIES
  • Removed redundant isActive check in timeline field validation since inactive fields are already filtered from metadata

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • All changes are well-designed fixes for legitimate bugs. The timeline activity check prevents errors gracefully, the orderBy fix matches backend behavior and prevents crashes, and the decorator changes are applied consistently across all system objects. The refactoring improves code maintainability without changing behavior.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts 5/5 Added check to skip timeline activity query when target object doesn't have corresponding field in TimelineActivity, preventing GraphQL errors for system objects without timeline tracking
packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts 5/5 Added default id: AscNullsFirst orderBy for objects without position field, matching backend behavior and preventing crashes in sortCachedObjectEdges
packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-event.workspace-entity.ts 5/5 Removed @WorkspaceIsObjectUIReadOnly decorator and added @WorkspaceIsFieldUIReadOnly to all standard/system fields, enabling custom field edits while protecting built-in fields
packages/twenty-server/src/modules/timeline/constants/system-objects-with-timeline-activities.constant.ts 5/5 Created constant to centralize list of system objects that support timeline activities (noteTarget, taskTarget)
packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts 5/5 Refactored to use centralized constant for checking system objects with timeline activities, improving maintainability

Sequence Diagram

sequenceDiagram
    participant User
    participant SystemObjectPage
    participant useTimelineActivities
    participant TimelineActivityMetadata
    participant GraphQLQuery
    participant RecordField
    participant turnSortsIntoOrderBy
    participant sortCachedObjectEdges

    User->>SystemObjectPage: Open calendar event details page
    SystemObjectPage->>useTimelineActivities: Request timeline activities
    useTimelineActivities->>TimelineActivityMetadata: Check if field exists for target object
    alt Field exists (noteTarget, taskTarget)
        TimelineActivityMetadata-->>useTimelineActivities: Field found
        useTimelineActivities->>GraphQLQuery: Execute query with skip=false
        GraphQLQuery-->>SystemObjectPage: Return timeline activities
    else Field doesn't exist (calendarEvent, etc)
        TimelineActivityMetadata-->>useTimelineActivities: Field not found
        useTimelineActivities->>GraphQLQuery: Skip query (skip=true)
        GraphQLQuery-->>SystemObjectPage: Return empty array (no error)
    end
    
    User->>RecordField: Edit custom field on calendar event
    RecordField->>turnSortsIntoOrderBy: Generate orderBy for cache update
    alt Object has position field
        turnSortsIntoOrderBy-->>RecordField: [...sorts, {position: AscNullsFirst}]
    else Object without position field
        turnSortsIntoOrderBy-->>RecordField: [...sorts, {id: AscNullsFirst}]
    end
    RecordField->>sortCachedObjectEdges: Sort cached edges with orderBy
    Note over sortCachedObjectEdges: orderBy[0] is guaranteed to exist
    sortCachedObjectEdges-->>RecordField: Sorted edges (no crash)
    RecordField-->>User: Field updated successfully
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@github-actions
Copy link
Contributor

github-actions bot commented Dec 3, 2025

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:6429

This environment will automatically shut down when the PR is closed or after 5 hours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants