Enable the Emails / Calendar reader on custom objects (the read path already looks generic) #26415
Closed
tolandmike
started this conversation in
Ideas
Replies: 1 comment
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
What I'm trying to do
We run a construction business on Twenty. Our real unit of work is a Project (a custom object) that a deal converts into. We'd love the Emails tab — the actual inline thread reader — to appear on a Project record the way it does on People, Companies, and Opportunities, so the correspondence for a job is readable on the job.
Today custom objects don't get the Emails/Calendar tabs. From reading the code, that looks deliberate rather than missing (see #19544, "Skip email/calendar tab creation for custom object record page layouts"), and I completely understand why it was scoped out. But the surprising thing is how close the rest of the stack already is to supporting it, so I wanted to raise it as an idea and offer to help.
What I found in the code (main)
The read path is almost entirely object-agnostic already:
EmailsCard(packages/twenty-front/.../activities/emails/components/EmailsCard.tsx) resolves the current record viauseTargetRecord()and asks the backend for that record's threads — no per-object special-casing.getTabsRenderableForTargetObject.tsdeliberately exempts Emails/Calendar from the "must have a relation field" rule (per the comment inWidgetTypesRequiringRelationField.ts).timeline-messaging.service.tsfilters threads withinnerJoin('messageThread.messageThreadTargets', …).where('messageThreadTargets.<fieldName> = :recordId')— it takes whatever target field it's handed.IS_MESSAGE_CALENDAR_TARGET_READ_ENABLED(Read Calendar and Email timelines from target junctions #24782).The one hard gate is a single ~15-line util,
get-target-field-name-for-object-record.util.ts:nullmakesMessageCalendarTargetReadinessServicetreat the object as "not ready," so the reader shows nothing. Alongside it,MessageThreadTargetWorkspaceEntity/CalendarEventTargetWorkspaceEntityhardcode the threetarget*relations.Proposed direction (open to your preference)
target<Object>Idcolumn that the thread/calendar target relation actually exposes, and return it for any object that has one. Widen theTargetFieldNametype accordingly.target<CustomObject>relation on the junction and having the resolver honor it.The part you deliberately deferred: population
I suspect the reason custom objects were skipped is that there's no generic way to populate the links — email auto-matching is intentionally people/companies-by-address, and opportunities are populated out of band. That's fair. From a user's side, two population paths already cover us and don't need core changes:
So this request is really about the read/display side working once the links exist; the auto-match behavior can stay exactly as it is.
Why it matters
For anyone modeling their real workflow with custom objects (projects, matters, cases, deals-after-close), the email history is the point — and right now it dead-ends on the object people actually live in. Given the target-junction read path is already generic, this feels like a small, high-leverage addition rather than a rewrite.
Happy to open a PR along the lines above if the team is open to the direction — wanted to check appetite and preferred approach first.
(Code references verified against
github.com/twentyhq/twentymain, Sept 2026.)All reactions