You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[$250] Show thread comments/comment number below the support ticket threads. #104400
Support ticket previews in the Concierge chat do not show that the ticket thread has replies. Customers can't tell that a conversation with their support rep is happening inside the ticket, so they keep typing in the main Concierge chat instead.
Example
A customer created a support ticket and got good help from a support rep.
The customer started following the rep's instructions.
The customer opened Concierge in the RHP and saw the support ticket preview.
The customer typed in the main Concierge chat, thinking they were replying to the rep. This started a new thread with Concierge instead.
Concierge replied, and the customer got frustrated.
There is currently no indicator on the support ticket preview that the support rep has responded.
Solution
Show the thread replies (reply count, like regular threads) below the support ticket preview, so customers know there is a conversation in the ticket and can open it. This should reuse the existing thread-reply UI pattern.
changed the title [-]Show thread comments/comment number below the support ticket threads.[/-][+][$250] Show thread comments/comment number below the support ticket threads.[/+]on Oct 21, 2026
Please re-state the problem that we are trying to solve in this issue.
When a support ticket thread in the Concierge chat has replies from a support rep, the support ticket preview gives no indication of them. Unlike regular threads, no reply count is shown below the preview, so customers don't realize a conversation is happening inside the ticket and reply in the main Concierge chat instead, starting an unrelated exchange with Concierge.
What is the root cause of that problem?
The "replies" footer that appears under a report action with a thread is rendered by ReportActionItemThread inside ReportActionItem, gated by shouldDisplayThreadReplies:
shouldDisplayThreadReplies explicitly returns false for support ticket creation actions because of the trailing isCreatedSupportTicketReportAction exclusion:
This exclusion was added deliberately when support tickets were built (commit 187e161410c, "fix(support): hide ticket preview reply counts"), along with a unit test asserting that a parent action whose childType is a support ticket omits the replies footer. The SupportTicketPreview component itself renders no reply indicator either, so even when the ticket thread has visible replies (childVisibleActionCount and childCommenterCount are populated on the parent action, same as regular threads), nothing on the preview reflects them:
What changes do you think we should make in order to solve the problem?
In shouldDisplayThreadReplies in src/libs/ReportUtils.ts, remove the isCreatedSupportTicketReportAction exclusion so the function evaluates support ticket creation actions with exactly the same condition as regular threads (visible child actions plus commenters, and not the thread's own parent-action view). A support ticket creation action is an ADD_COMMENT action whose childType is a support ticket, so it already flows through the standard thread bookkeeping:
functionisCreatedSupportTicketReportAction(reportAction: OnyxInputOrEntry<ReportAction>): reportAction is ReportAction<typeofCONST.REPORT.ACTIONS.TYPE.ADD_COMMENT>{
With the exclusion gone, the existing ReportActionItemThread footer — reply count, replier avatars, and last-reply time — renders below the support ticket preview, and pressing it opens the ticket thread. This reuses the existing thread-reply UI pattern exactly as the issue requests; no changes to SupportTicketPreview are needed. The helper is also read by the Join thread / Leave thread context menu items, but those items (and Reply in thread) keep their own explicit isCreatedSupportTicketReportAction exclusion, so the ticket preview's context menu is unchanged. The isCreatedSupportTicketReportAction import in ReportUtils.ts stays, since canEditReportAction still uses it to keep ticket previews non-editable.
We should also update the existing unit test table for shouldDisplayThreadReplies in tests/unit/ReportUtilsTest.ts, which currently expects the footer to be omitted for the support ticket child type, so it now expects the footer to show — keeping explicit regression coverage for the new behavior:
What alternative solutions did you explore? (Optional)
Rendering a bespoke reply-count row inside the SupportTicketPreview component itself. Rejected: it would duplicate what ReportActionItemThread already does (count, avatars, last-reply time, navigation to the thread), would need its own styling and press handling, and would drift from regular threads over time. Gating the existing footer at the ReportActionItem level keeps support tickets visually and behaviorally identical to the established thread pattern.
Please re-state the problem that we are trying to solve in this issue.
When a customer submits a support ticket from the Concierge chat and a support rep replies inside that ticket's thread, the support ticket preview shown in the main Concierge chat gives no indication that a reply/conversation exists. Customers don't realize the rep already responded inside the ticket, so they type their follow-up in the main Concierge chat instead of opening the ticket thread, which kicks off an unrelated conversation with Concierge and causes frustration.
What is the root cause of that problem?
Every normal chat message that has thread replies shows a "X replies" row (avatars + reply count + timestamp) right below it when viewed in the parent chat. That row is rendered by ReportActionItemThread and is only shown when shouldDisplayThreadReplies returns true:
shouldDisplayThreadReplies itself explicitly excludes support ticket actions from ever showing the indicator, regardless of whether the ticket actually has replies:
functionisCreatedSupportTicketReportAction(reportAction: OnyxInputOrEntry<ReportAction>): reportAction is ReportAction<typeofCONST.REPORT.ACTIONS.TYPE.ADD_COMMENT>{
So even though the support ticket's ADD_COMMENT action gets childVisibleActionCount/childCommenterCount populated exactly like any other thread parent once the rep replies, the !isCreatedSupportTicketReportAction(reportAction) check always short-circuits the result to false for support tickets, and ReportActionItemThread never renders for them.
Instead, support tickets are rendered via a dedicated SupportTicketPreview card, which only shows the ticket's checkbox/avatar/name/arrow — it has no reply-count UI at all:
For comparison, regular Tasks go through the exact same shouldDisplayThreadReplies check but are not excluded from it, which is why task previews correctly show "X replies" under them when a task thread has comments:
This confirms the hard-coded support ticket exclusion in shouldDisplayThreadReplies is the actual root cause: it suppresses the exact reply indicator the issue is asking for, even though the underlying data (childVisibleActionCount, childCommenterCount, childOldestFourAccountIDs, childReportID) is already present and correct for support ticket actions.
What changes do you think we should make in order to solve the problem?
Remove the support-ticket exclusion from shouldDisplayThreadReplies so support ticket actions follow the same rule as every other thread-having action (including tasks), reusing the existing ReportActionItemThread "X replies" UI exactly as the issue requests, instead of building a new indicator:
With this change, once a support rep replies inside the ticket thread, childVisibleActionCount/childCommenterCount on the ticket's ADD_COMMENT action become truthy (the same fields SupportTicketPreview already reads childReportID from), shouldDisplayThreadReplies returns true, and ReportActionItemThread automatically renders the "X replies" row (with the rep's avatar, reply count, and last-reply timestamp) directly below the SupportTicketPreview card in the main Concierge chat — matching the mock in the issue. Tapping it uses the existing navigateToAndOpenChildReport flow to jump straight into the ticket thread, so customers can immediately see the rep replied and continue the conversation there instead of starting a new thread with Concierge.
No changes are needed to ReportActionItemThread, SupportTicketPreview, or the backend — the thread metadata needed to drive the indicator is already being sent and stored on the support ticket's ADD_COMMENT action; the indicator was simply being explicitly suppressed for that one action type.
When the replies row is tapped on support-ticket action, user should navigate directly to the existing ticket using the same method as the preview card. This avoids the generic thread handler creating a new simple non-support ticket thread if the ticket thread isn’t loaded in Onyx. Other thread replies keep their existing navigation behavior.
Please re-state the problem that we are trying to solve in this issue.
When a support representative replies inside a customer's support ticket, the ticket preview in the parent Concierge conversation does not show any reply indicator.
Because the preview looks unchanged, the customer may reply in the main Concierge conversation instead of opening the existing support-ticket conversation. This starts an unrelated conversation with Concierge and makes it appear that the support representative did not respond.
The expected behavior is for the support-ticket preview to show the same reply summary used by regular threaded messages, including the reply count, participant avatar(s), and most recent reply time.
What is the root cause of that problem?
The support-ticket preview is rendered as a report action whose child report is the support ticket:
SupportTicketPreview renders the ticket card itself, but it does not render a reply summary. The shared reply summary is rendered separately by ReportActionItemThread inside ReportActionItem.
Support-ticket creation actions are ADD_COMMENT actions with a support-ticket childType. When a support representative replies, the parent action already receives the same child-thread metadata used by regular comments and tasks. Therefore, the data needed by ReportActionItemThread exists, but the final condition always prevents the component from mounting.
The original support-ticket implementation intentionally hid this footer, which is why the current unit test expects support-ticket actions to return false. Issue #104400 reverses that product decision.
There is also a second issue that must be fixed at the same time.
ReportActionItemThread currently opens its child by calling:
navigateToAndOpenChildReport(...)
That generic flow may call createChildReport() when the child report is not currently loaded in Onyx. createChildReport() is designed for locally-created chat threads and can build an optimistic report with type: CHAT.
That behavior is unsafe for support tickets because:
A support ticket is an existing server-side report, not a new chat thread.
The parent action already contains the real childReportID.
SupportTicketPreview already opens the ticket directly using getReportRouteForCurrentContext().
If the generic fallback writes a chat-shaped optimistic report under the ticket's report ID, the application may temporarily stop recognizing it as a support ticket.
This can affect support-ticket-specific UI such as the support-ticket header, resolved footer, survey, composer rules, and context-menu restrictions.
Therefore, removing only the visibility exclusion would fix the missing UI but could create an inconsistent navigation path when the ticket report is not hydrated in Onyx.
What changes do you think we should make in order to solve the problem?
1. Allow the shared reply footer for support-ticket previews
Update shouldDisplayThreadReplies() in:
src/libs/ReportUtils.ts
The helper should use the existing thread conditions without excluding support-ticket creation actions:
Search results, because the caller still controls the existing search exclusion.
Normal chat, task, and expense threads.
The isCreatedSupportTicketReportAction import must remain if it is still used by other functions such as canEditReportAction. Only its use inside shouldDisplayThreadReplies() should be removed.
This allows the existing ReportActionItemThread component to render beneath SupportTicketPreview, reusing the established reply-count UI instead of introducing a second support-ticket-specific implementation.
2. Make reply-footer navigation ticket-safe
Update:
src/pages/inbox/report/ReportActionItemThread.tsx
Before calling the generic navigateToAndOpenChildReport() flow, detect support-ticket creation actions and navigate directly to the existing child report.
The fallback to reportAction.childReportID is important because the ticket report may not yet be available in Onyx. This preserves the existing behavior of the preview card and prevents the generic child-report flow from creating an optimistic chat under the ticket's report ID.
Normal comments and other threaded actions must continue using navigateToAndOpenChildReport() unchanged.
This is preferable to modifying createChildReport() globally for this issue because it:
Changes only the newly exposed support-ticket interaction.
Does not alter optimistic creation behavior for ordinary chat threads.
Does not change task, expense, or transaction-thread creation semantics.
Keeps support-ticket navigation policy close to the support-ticket UI behavior.
Makes the reply footer and ticket preview use the same route-resolution logic.
3. Preserve existing context-menu restrictions
No new context-menu actions should be added.
The following existing restrictions must remain unchanged:
Reply in thread remains unavailable for support-ticket actions.
The ticket's own parent action does not display a nested reply footer inside the ticket thread.
Removing the exclusion from shouldDisplayThreadReplies() does not automatically enable those actions because their context-menu entries already have separate support-ticket checks.
4. Update and extend regression coverage
Update the existing shouldDisplayThreadReplies() test in:
tests/unit/ReportUtilsTest.ts
The support-ticket case should now expect the reply footer to be displayed when reply metadata exists.
The test coverage should include:
Support-ticket action with visible replies returns true.
Support-ticket action with no visible replies returns false.
Support-ticket action with isAgentProcessingInThread === true returns true.
Support-ticket action used as the parent action inside its own thread returns false.
Normal chat actions continue returning true when they have replies.
Task actions continue returning true when they have replies.
canEditReportAction() continues returning false for support-ticket creation actions.
Search results continue to hide the footer through the existing caller-level guard.
Add or extend a focused UI/navigation test for:
src/pages/inbox/report/ReportActionItemThread.tsx
The important navigation regression case is:
The support-ticket parent action contains a valid childReportID.
The support-ticket report is absent from Onyx.
Pressing the replies row navigates to the ticket route using that ID.
No optimistic CHAT report is created under the ticket report key.
The normal thread navigation path is still used for a regular chat action.
The support-ticket context-menu test should also continue verifying that Reply in thread, Join thread, Leave thread, and reactions remain hidden where applicable.
Files to change
The implementation should be limited to these files:
src/libs/ReportUtils.ts
Remove the support-ticket exclusion from shouldDisplayThreadReplies().
Keep the support-ticket helper import if it is required elsewhere.
src/pages/inbox/report/ReportActionItemThread.tsx
Add the support-ticket-specific direct navigation branch.
Leave normal thread navigation unchanged.
tests/unit/ReportUtilsTest.ts
Update the support-ticket visibility expectation.
Add coverage for zero replies, replies, processing state, and thread-parent state.
The existing focused UI/navigation test file for ReportActionItemThread
Add coverage for an unloaded support-ticket report.
Verify that direct ticket navigation does not create an optimistic chat.
If no suitable test file exists, add a focused test beside the component according to the repository's existing test conventions.
No changes are required to:
SupportTicketPreview.tsx
ActionContentRouter.tsx
The backend response format
Onyx schema
Context-menu behavior
Mobile-Expensify or HybridApp integration
Expected result
After a support representative replies:
The Concierge support-ticket preview displays the standard reply summary.
The reply count is shown using the existing thread UI.
The support representative's avatar can appear in the reply summary.
The most recent reply time is displayed.
Pressing the summary opens the existing support ticket.
The ticket opens correctly even if it has not yet been loaded into Onyx.
No optimistic chat report is created for the ticket.
Tickets without replies remain visually unchanged.
Add a custom reply row inside SupportTicketPreview
This would solve the visual problem, but it would duplicate logic already implemented by ReportActionItemThread, including:
Reply count formatting.
Avatar rendering.
Timestamp formatting.
Accessibility labels.
Press behavior.
Hover and active states.
Cross-platform styling.
It would also create two separate implementations that could diverge later. Reusing the shared footer is smaller and matches the issue's requirement to use the existing thread-reply pattern.
Only remove the condition from shouldDisplayThreadReplies()
This fixes the missing footer but does not fully fix the user flow. If the support-ticket report is absent from Onyx, pressing the newly visible footer can enter the generic child-report creation path and temporarily represent the ticket as a normal chat.
The navigation change is therefore required for a complete fix.
Change createChildReport() globally based on childType
A broader change could prevent optimistic chat creation for every typed child report, such as tasks or expenses. However, that changes shared report-creation behavior beyond the scope of this issue and requires a wider audit of all callers and optimistic workflows.
A support-ticket-specific route branch is safer for this issue because it fixes the newly exposed interaction without changing unrelated child-report behavior.
Add an unread/bold state for support-ticket replies
Showing unread or bold styling could be useful as a separate enhancement, but it is not required by the issue. The reported problem is that customers cannot tell that a thread exists at all. Adding read-state logic would introduce additional Onyx subscriptions and behavior that are not necessary to solve the original problem.
Validation plan
Before submitting a pull request, run the repository-required checks:
npm run lint-changed
npm run typecheck
npm run react-compiler-compliance-check check-changed
npm run spell-changed
Also run the focused ReportActionItemThread UI/navigation test after adding the regression coverage.
Manual verification should cover:
Web Concierge chat with a support ticket that has no replies.
Web Concierge chat after a support representative replies.
Concierge in the right-hand panel.
Narrow/mobile web layout.
Android HybridApp.
iOS HybridApp.
The ticket report loaded in Onyx before pressing the reply row.
The ticket report absent from Onyx before pressing the reply row.
A normal chat thread to confirm its navigation is unchanged.
Support-ticket context menus to confirm Reply in thread, Join thread, and Leave thread remain unavailable.
This proposal intentionally reuses the existing reply-summary component and fixes the ticket navigation invariant at the point where the new interaction is exposed.
The Concierge ticket card is an ADD_COMMENT whose childType is SUPPORT_TICKET. That is isCreatedSupportTicketReportAction. The replies row already exists. ReportActionItem mounts ReportActionItemThread when shouldDisplayThreadReplies is true.
On main, that helper ends with a hard exclusion for isCreatedSupportTicketReportAction. The row stays hidden even when childVisibleActionCount and childCommenterCount are already set. SupportTicketPreview only draws the card. It does not need a second reply UI.
The exclusion is right for an empty "ticket created" card. It is wrong once the ticket has replies. That is why customers keep typing in the main Concierge composer.
The existing row already opens the child report with navigateToAndOpenChildReport and reportAction.childReportID. Showing the row is the missing piece, not a new navigator.
What changes do you think we should make in order to solve the problem?
Leave isCreatedSupportTicketReportAction as it is. Other callers still need it.
In shouldDisplayThreadReplies, skip that exclusion only when hasReplies is true (childVisibleActionCount greater than 0 and childCommenterCount set). Tickets with no replies stay without a replies row.
Do not add a count or avatar row inside SupportTicketPreview.
What alternative solutions did you explore?
Put the count inside SupportTicketPreview. That duplicates ReportActionItemThread and misses the shared tap path.
Remove isCreatedSupportTicketReportAction from the helper with no hasReplies guard. Empty tickets can then grow a replies row they should not have.
Change childType so the action is no longer a support ticket. That breaks ticket identification.
In src/libs/ReportUtils.ts, the shouldDisplayThreadReplies function explicitly suppresses thread reply indicators for support ticket parent actions: ypescript return (hasReplies || isAgentProcessingInThread) && !isThreadReportParentAction && !isCreatedSupportTicketReportAction(reportAction);
Because !isCreatedSupportTicketReportAction(reportAction) is hardcoded in the return condition, ReportActionItem never mounts ReportActionItemThread beneath SupportTicketPreview, even when customer support agents have actively responded inside the ticket (childVisibleActionCount > 0 and childCommenterCount > 0).
What changes do you think we should make in order to solve the problem?
Remove Support Ticket Exclusion in ReportUtils.ts:
In src/libs/ReportUtils.ts, remove && !isCreatedSupportTicketReportAction(reportAction) from shouldDisplayThreadReplies: ypescript function shouldDisplayThreadReplies(reportAction: OnyxInputOrEntry<ReportAction>, isThreadReportParentAction: boolean, isAgentProcessingInThread = false): boolean { const hasReplies = (reportAction?.childVisibleActionCount ?? 0) > 0 && !reportAction?.childCommenterCount; return (hasReplies || isAgentProcessingInThread) && !isThreadReportParentAction; }
Guard Support Ticket Navigation in
avigateToAndOpenChildReport (src/libs/actions/Report/index.ts):
When tapping the thread reply row on a support ticket action, if isCreatedSupportTicketReportAction(parentReportAction) is true and parentReportAction.childReportID exists, directly route to the ticket using getReportRouteForCurrentContext({reportID: parentReportAction.childReportID}) rather than executing createChildReport(). This prevents optimistically overriding the report's ype with a standard thread chat if childReport is not yet populated in the Onyx memory cache.
Hermetic Regression Tests:
Add test coverage in ests/unit/ReportUtilsTest.ts validating that shouldDisplayThreadReplies returns rue for support ticket report actions having visible child actions/replies, and returns alse when no replies exist.
What alternative solutions did you explore? (Optional)
Embedding a custom reply counter badge directly inside SupportTicketPreview.tsx. However, reusing the standard ReportActionItemThread component keeps the design system consistent across threads and avoids duplicate reply counter logic.
Please re-state the problem that we are trying to solve in this issue.
In Concierge chat, support ticket preview cards do not display the thread reply counter when a support representative responds. Customers have no visual cue that their conversation with support is happening inside the ticket thread, so they mistakenly continue typing into the main Concierge chat instead.
What is the root cause of that problem?
In src/libs/ReportUtils.ts, the shouldDisplayThreadReplies helper explicitly excludes support ticket creation actions:
It checks (hasReplies || isAgentProcessingInThread) && !isThreadReportParentAction && !isCreatedSupportTicketReportAction(reportAction).
Because !isCreatedSupportTicketReportAction(reportAction) is hardcoded in the return condition, ReportActionItem.tsx:698 never renders the existing ReportActionItemThread component beneath SupportTicketPreview, even when childVisibleActionCount > 0 and childCommenterCount > 0 confirm that support reps have sent replies.
In tests/unit/ReportUtilsTest.ts, line 7418 explicitly tests and expects [CONST.REPORT.TYPE.SUPPORT_TICKET, false], enforcing this omission in the test suite.
When the thread replies row is clicked, ReportActionItemThread delegates to navigateToAndOpenChildReport. If the support ticket report is not yet populated in Onyx (e.g. cold start or fresh device), navigateToAndOpenChildReport falls back to createChildReport, which builds an optimistic chat report rather than navigating to the support ticket route.
What changes do you think we should make in order to solve the problem?
Allow thread replies for support ticket actions with replies (src/libs/ReportUtils.ts):
Update shouldDisplayThreadReplies to allow isCreatedSupportTicketReportAction(reportAction) when it has active replies (hasReplies is true):
Keep !isThreadReportParentAction and maintain existing exclusions for context menu actions (joinThread, replyInThread) in src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx so sub-threads cannot be created on support tickets.
Support Ticket-Aware Navigation (src/pages/inbox/report/ReportActionItemThread.tsx):
In ReportActionItemThread.tsx, intercept support ticket creation actions to navigate directly to the ticket route via getReportRouteForCurrentContext({reportID: reportAction.childReportID}):
This guarantees that tapping the reply row directly opens the support ticket route even when childReport is not yet cached in Onyx, preventing navigateToAndOpenChildReport from falling back to createChildReport and building an optimistic chat report.
Preserve Context Menu Constraints:
Keep the existing support-ticket checks in src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx intact so that "Reply in thread", "Join thread", and "Leave thread" remain disabled for support ticket preview actions.
What specific scenarios should we cover in automated tests?
Unit tests in tests/unit/ReportUtilsTest.ts:
Update the existing test case for CONST.REPORT.TYPE.SUPPORT_TICKET to expect true when child replies exist:
describe('shouldDisplayThreadReplies',()=>{it.each([[CONST.REPORT.TYPE.SUPPORT_TICKET,true],[CONST.REPORT.TYPE.CHAT,true],[CONST.REPORT.TYPE.TASK,true],]asconst)('child report type %s shows the replies footer: %s',(childType,expected)=>{ ... });});
Verify that support ticket actions with 0 replies return false.
Verify that support ticket actions with isAgentProcessingInThread === true return true.
Verify that a support ticket parent action inside its own thread returns false.
Navigation tests:
Verify that when the support ticket report is not cached in Onyx, pressing the thread replies summary navigates to the ticket route without creating an optimistic chat report.
What alternative solutions did you explore? (Optional)
Building custom reply count UI inside SupportTicketPreview.tsx:
Explored creating a dedicated reply count badge directly inside SupportTicketPreview. We rejected this because it duplicates logic already implemented in ReportActionItemThread and does not follow the requirement to reuse the existing thread-reply UI pattern.
Leaving navigateToAndOpenChildReport unchanged:
Rejected because if the ticket report data has not yet loaded in Onyx, createChildReport would construct a generic chat report object, potentially displaying the ticket with the wrong header and missing support ticket actions.
⚠️@ADITYASINGH1206 Thanks for your interest. To be considered for this job, please post a proposal following the proposal template (note the mandatory sections). Comments claiming the job without one are not reviewed.
Please re-state the problem that we are trying to solve in this issue.
Support ticket previews in the Concierge conversation do not display the thread replies summary (reply count, avatars, timestamp) when a support representative responds. Because the ticket card looks unchanged, customers assume no reply was received and continue typing in the main Concierge room, inadvertently creating new unwanted threads.
What is the root cause of that problem?
There are two distinct root causes:
Suppression of the Thread Replies UI:
In src/libs/ReportUtils.ts, shouldDisplayThreadReplies() explicitly excludes support tickets:
Even though the backend sends childVisibleActionCount and childCommenterCount, the check forces false, preventing ReportActionItemThread from rendering under the preview.
Onyx Cache Mutation / Corrupted Optimistic Fallback (LSP Violation): ReportActionItemThread calls navigateToAndOpenChildReport. If the support ticket report is not yet hydrated in Onyx, navigateToAndOpenChildReport falls back to createChildReport(). In src/libs/actions/Report/index.ts, createChildReport() unconditionally builds an optimistic report via buildOptimisticChatReport({ ... }) (hardcoding type: CHAT) and merges it into Onyx under ${childReportID}.
Writing a generic CHAT object over an existing server-side SUPPORT_TICKET corrupts the report type in Onyx until the network fetch returns, temporarily stripping ticket-specific layout (ticket header, resolved footer, survey, and composer behavior).
What changes do you think we should make in order to solve the problem?
Applying SOLID principles (Single Responsibility & Open-Closed Principle), we must keep presentation components decoupled from report routing policies and protect Onyx integrity:
1. Enable Thread Replies in ReportUtils.ts (SRP):
Remove the !isCreatedSupportTicketReportAction(reportAction) clause from shouldDisplayThreadReplies:
This allows ReportActionItemThread to mount naturally for support ticket actions with replies, exactly as it does for comments and tasks, while preserving protections against rendering inside parent thread actions or search views.
2. Prevent Fabricated Chat Seeding for Typed Child Reports in src/libs/actions/Report/index.ts (OCP & LSP):
In createChildReport(), we should respect the parentReportAction.childType contract. When childReportID exists and parentReportAction.childType indicates an existing server-managed entity (e.g. supportTicket), we must not seed a fabricated CHAT report into Onyx.
Instead, when the child report is not in Onyx, navigateToAndOpenChildReport should navigate directly via getReportRouteForCurrentContext({reportID: childReportID}), allowing ReportFetchHandler to fetch the authentic ticket from the backend without mutating local Onyx state with an invalid type.
This approach ensures:
ReportActionItemThread.tsx remains a pure, decoupled presentation component without one-off ticket routing hacks.
Non-chat child reports (support tickets, tasks, expenses) are never overwritten by optimistic chat objects.
Normal chat threads retain their existing optimistic creation and routing lifecycle.
3. Update Unit & Regression Tests:
In tests/unit/ReportUtilsTest.ts: Update the shouldDisplayThreadReplies table test to expect true for SUPPORT_TICKET actions with replies. Add a test ensuring ticket actions without replies return false.
In tests/unit/ReportTest.ts: Verify that navigateToAndOpenChildReport with an unloaded support ticket report navigates without writing an optimistic chat object to Onyx.
What alternative solutions did you explore? (Optional)
Branching directly in ReportActionItemThread.tsx: Adding an if (isCreatedSupportTicketReportAction) inside the presentation component violates SRP and OCP, leaking domain-level report routing into a shared UI component.
Duplicating the reply row inside SupportTicketPreview.tsx: Violates DRY and duplicates avatar rendering, count formatting, and theme styles, risking UI divergence.
Activity
[-]Show thread comments/comment number below the support ticket threads.[/-][+][$250] Show thread comments/comment number below the support ticket threads.[/+]wildan-m commentedon Oct 21, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a support ticket thread in the Concierge chat has replies from a support rep, the support ticket preview gives no indication of them. Unlike regular threads, no reply count is shown below the preview, so customers don't realize a conversation is happening inside the ticket and reply in the main Concierge chat instead, starting an unrelated exchange with Concierge.
What is the root cause of that problem?
The "replies" footer that appears under a report action with a thread is rendered by
ReportActionItemThreadinsideReportActionItem, gated byshouldDisplayThreadReplies:App/src/pages/inbox/report/ReportActionItem.tsx
Lines 698 to 708 in 9d14b70
shouldDisplayThreadRepliesexplicitly returnsfalsefor support ticket creation actions because of the trailingisCreatedSupportTicketReportActionexclusion:App/src/libs/ReportUtils.ts
Lines 12531 to 12534 in 9d14b70
This exclusion was added deliberately when support tickets were built (commit
187e161410c, "fix(support): hide ticket preview reply counts"), along with a unit test asserting that a parent action whosechildTypeis a support ticket omits the replies footer. TheSupportTicketPreviewcomponent itself renders no reply indicator either, so even when the ticket thread has visible replies (childVisibleActionCountandchildCommenterCountare populated on the parent action, same as regular threads), nothing on the preview reflects them:App/src/pages/inbox/report/actionContents/ActionContentRouter.tsx
Lines 252 to 260 in 9d14b70
What changes do you think we should make in order to solve the problem?
In
shouldDisplayThreadRepliesinsrc/libs/ReportUtils.ts, remove theisCreatedSupportTicketReportActionexclusion so the function evaluates support ticket creation actions with exactly the same condition as regular threads (visible child actions plus commenters, and not the thread's own parent-action view). A support ticket creation action is anADD_COMMENTaction whosechildTypeis a support ticket, so it already flows through the standard thread bookkeeping:App/src/libs/ReportActionsUtils.ts
Lines 440 to 442 in 9d14b70
With the exclusion gone, the existing
ReportActionItemThreadfooter — reply count, replier avatars, and last-reply time — renders below the support ticket preview, and pressing it opens the ticket thread. This reuses the existing thread-reply UI pattern exactly as the issue requests; no changes toSupportTicketPrevieware needed. The helper is also read by the Join thread / Leave thread context menu items, but those items (and Reply in thread) keep their own explicitisCreatedSupportTicketReportActionexclusion, so the ticket preview's context menu is unchanged. TheisCreatedSupportTicketReportActionimport inReportUtils.tsstays, sincecanEditReportActionstill uses it to keep ticket previews non-editable.We should also update the existing unit test table for
shouldDisplayThreadRepliesintests/unit/ReportUtilsTest.ts, which currently expects the footer to be omitted for the support ticket child type, so it now expects the footer to show — keeping explicit regression coverage for the new behavior:https://github.com/Expensify/App/blob/9d14b70be07d516add0a1fe43c06aef15bec321e/tests/unit/ReportUtilsTest.ts#L7200-L7221
Compare branch: https://github.com/Expensify/App/compare/main...wildan-m:App:wildan/104400-support-ticket-thread-replies?expand=1
What alternative solutions did you explore? (Optional)
Rendering a bespoke reply-count row inside the
SupportTicketPreviewcomponent itself. Rejected: it would duplicate whatReportActionItemThreadalready does (count, avatars, last-reply time, navigation to the thread), would need its own styling and press handling, and would drift from regular threads over time. Gating the existing footer at theReportActionItemlevel keeps support tickets visually and behaviorally identical to the established thread pattern.melvin-bot commentedon Oct 21, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022113015012080613072
wildan-m commentedon Oct 21, 2026
Proposal updated — added compare branch with implementation.
abbasifaizan70 commentedon Oct 22, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a customer submits a support ticket from the Concierge chat and a support rep replies inside that ticket's thread, the support ticket preview shown in the main Concierge chat gives no indication that a reply/conversation exists. Customers don't realize the rep already responded inside the ticket, so they type their follow-up in the main Concierge chat instead of opening the ticket thread, which kicks off an unrelated conversation with Concierge and causes frustration.
What is the root cause of that problem?
Every normal chat message that has thread replies shows a "X replies" row (avatars + reply count + timestamp) right below it when viewed in the parent chat. That row is rendered by
ReportActionItemThreadand is only shown whenshouldDisplayThreadRepliesreturnstrue:App/src/pages/inbox/report/ReportActionItem.tsx
Line 541 in ec8371a
App/src/pages/inbox/report/ReportActionItem.tsx
Lines 698 to 708 in ec8371a
shouldDisplayThreadRepliesitself explicitly excludes support ticket actions from ever showing the indicator, regardless of whether the ticket actually has replies:App/src/libs/ReportUtils.ts
Lines 12616 to 12619 in ec8371a
isCreatedSupportTicketReportActionflags theADD_COMMENTaction created when a support ticket is opened (itschildTypeissupportTicket):App/src/libs/ReportActionsUtils.ts
Lines 440 to 442 in ec8371a
So even though the support ticket's
ADD_COMMENTaction getschildVisibleActionCount/childCommenterCountpopulated exactly like any other thread parent once the rep replies, the!isCreatedSupportTicketReportAction(reportAction)check always short-circuits the result tofalsefor support tickets, andReportActionItemThreadnever renders for them.Instead, support tickets are rendered via a dedicated
SupportTicketPreviewcard, which only shows the ticket's checkbox/avatar/name/arrow — it has no reply-count UI at all:App/src/pages/inbox/report/actionContents/ActionContentRouter.tsx
Lines 252 to 260 in ec8371a
App/src/components/ReportActionItem/SupportTicketPreview.tsx
Lines 74 to 122 in ec8371a
For comparison, regular Tasks go through the exact same
shouldDisplayThreadRepliescheck but are not excluded from it, which is why task previews correctly show "X replies" under them when a task thread has comments:App/src/pages/inbox/report/actionContents/ActionContentRouter.tsx
Lines 241 to 250 in ec8371a
This confirms the hard-coded support ticket exclusion in
shouldDisplayThreadRepliesis the actual root cause: it suppresses the exact reply indicator the issue is asking for, even though the underlying data (childVisibleActionCount,childCommenterCount,childOldestFourAccountIDs,childReportID) is already present and correct for support ticket actions.What changes do you think we should make in order to solve the problem?
Remove the support-ticket exclusion from
shouldDisplayThreadRepliesso support ticket actions follow the same rule as every other thread-having action (including tasks), reusing the existingReportActionItemThread"X replies" UI exactly as the issue requests, instead of building a new indicator:App/src/libs/ReportUtils.ts
Lines 12616 to 12619 in ec8371a
With this change, once a support rep replies inside the ticket thread,
childVisibleActionCount/childCommenterCounton the ticket'sADD_COMMENTaction become truthy (the same fieldsSupportTicketPreviewalready readschildReportIDfrom),shouldDisplayThreadRepliesreturnstrue, andReportActionItemThreadautomatically renders the "X replies" row (with the rep's avatar, reply count, and last-reply timestamp) directly below theSupportTicketPreviewcard in the main Concierge chat — matching the mock in the issue. Tapping it uses the existingnavigateToAndOpenChildReportflow to jump straight into the ticket thread, so customers can immediately see the rep replied and continue the conversation there instead of starting a new thread with Concierge.No changes are needed to
ReportActionItemThread,SupportTicketPreview, or the backend — the thread metadata needed to drive the indicator is already being sent and stored on the support ticket'sADD_COMMENTaction; the indicator was simply being explicitly suppressed for that one action type.43 remaining items
Add createChildReport childType regression tests (Expensify#104400)
samranahm commentedon Oct 23, 2026
PROPOSAL UPDATED
When the replies row is tapped on support-ticket action, user should navigate directly to the existing ticket using the same method as the preview card. This avoids the generic thread handler creating a new simple non-support ticket thread if the ticket thread isn’t loaded in Onyx. Other thread replies keep their existing navigation behavior.
cc: @ChavdaSachin
juni2003 commentedon Oct 23, 2026
Contributor details
Your Expensify account email: juni.xatti@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~01867ae3203c9b7815?mp_source=share
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a support representative replies inside a customer's support ticket, the ticket preview in the parent Concierge conversation does not show any reply indicator.
Because the preview looks unchanged, the customer may reply in the main Concierge conversation instead of opening the existing support-ticket conversation. This starts an unrelated conversation with Concierge and makes it appear that the support representative did not respond.
The expected behavior is for the support-ticket preview to show the same reply summary used by regular threaded messages, including the reply count, participant avatar(s), and most recent reply time.
What is the root cause of that problem?
The support-ticket preview is rendered as a report action whose child report is the support ticket:
src/pages/inbox/report/actionContents/ActionContentRouter.tsxsrc/components/ReportActionItem/SupportTicketPreview.tsxSupportTicketPreviewrenders the ticket card itself, but it does not render a reply summary. The shared reply summary is rendered separately byReportActionItemThreadinsideReportActionItem.ReportActionItemcallsshouldDisplayThreadReplies()from:src/libs/ReportUtils.tsThat helper already checks the normal thread metadata:
childVisibleActionCountchildCommenterCountisAgentProcessingInThreadisThreadReportParentActionHowever, it also contains a support-ticket exclusion:
Support-ticket creation actions are
ADD_COMMENTactions with a support-ticketchildType. When a support representative replies, the parent action already receives the same child-thread metadata used by regular comments and tasks. Therefore, the data needed byReportActionItemThreadexists, but the final condition always prevents the component from mounting.The original support-ticket implementation intentionally hid this footer, which is why the current unit test expects support-ticket actions to return
false. Issue #104400 reverses that product decision.There is also a second issue that must be fixed at the same time.
ReportActionItemThreadcurrently opens its child by calling:That generic flow may call
createChildReport()when the child report is not currently loaded in Onyx.createChildReport()is designed for locally-created chat threads and can build an optimistic report withtype: CHAT.That behavior is unsafe for support tickets because:
childReportID.SupportTicketPreviewalready opens the ticket directly usinggetReportRouteForCurrentContext().Therefore, removing only the visibility exclusion would fix the missing UI but could create an inconsistent navigation path when the ticket report is not hydrated in Onyx.
What changes do you think we should make in order to solve the problem?
1. Allow the shared reply footer for support-ticket previews
Update
shouldDisplayThreadReplies()in:src/libs/ReportUtils.tsThe helper should use the existing thread conditions without excluding support-ticket creation actions:
This preserves the existing behavior for:
The
isCreatedSupportTicketReportActionimport must remain if it is still used by other functions such ascanEditReportAction. Only its use insideshouldDisplayThreadReplies()should be removed.This allows the existing
ReportActionItemThreadcomponent to render beneathSupportTicketPreview, reusing the established reply-count UI instead of introducing a second support-ticket-specific implementation.2. Make reply-footer navigation ticket-safe
Update:
src/pages/inbox/report/ReportActionItemThread.tsxBefore calling the generic
navigateToAndOpenChildReport()flow, detect support-ticket creation actions and navigate directly to the existing child report.The relevant behavior should be equivalent to:
The ticket branch should use the same navigation helper as
SupportTicketPreview:The fallback to
reportAction.childReportIDis important because the ticket report may not yet be available in Onyx. This preserves the existing behavior of the preview card and prevents the generic child-report flow from creating an optimistic chat under the ticket's report ID.Normal comments and other threaded actions must continue using
navigateToAndOpenChildReport()unchanged.This is preferable to modifying
createChildReport()globally for this issue because it:3. Preserve existing context-menu restrictions
No new context-menu actions should be added.
The following existing restrictions must remain unchanged:
Removing the exclusion from
shouldDisplayThreadReplies()does not automatically enable those actions because their context-menu entries already have separate support-ticket checks.4. Update and extend regression coverage
Update the existing
shouldDisplayThreadReplies()test in:tests/unit/ReportUtilsTest.tsThe support-ticket case should now expect the reply footer to be displayed when reply metadata exists.
The test coverage should include:
true.false.isAgentProcessingInThread === truereturnstrue.false.truewhen they have replies.truewhen they have replies.canEditReportAction()continues returningfalsefor support-ticket creation actions.Add or extend a focused UI/navigation test for:
src/pages/inbox/report/ReportActionItemThread.tsxThe important navigation regression case is:
childReportID.CHATreport is created under the ticket report key.The support-ticket context-menu test should also continue verifying that Reply in thread, Join thread, Leave thread, and reactions remain hidden where applicable.
Files to change
The implementation should be limited to these files:
src/libs/ReportUtils.tsshouldDisplayThreadReplies().src/pages/inbox/report/ReportActionItemThread.tsxtests/unit/ReportUtilsTest.tsThe existing focused UI/navigation test file for
ReportActionItemThreadNo changes are required to:
SupportTicketPreview.tsxActionContentRouter.tsxExpected result
After a support representative replies:
What alternative solutions did you explore?
Add a custom reply row inside
SupportTicketPreviewThis would solve the visual problem, but it would duplicate logic already implemented by
ReportActionItemThread, including:It would also create two separate implementations that could diverge later. Reusing the shared footer is smaller and matches the issue's requirement to use the existing thread-reply pattern.
Only remove the condition from
shouldDisplayThreadReplies()This fixes the missing footer but does not fully fix the user flow. If the support-ticket report is absent from Onyx, pressing the newly visible footer can enter the generic child-report creation path and temporarily represent the ticket as a normal chat.
The navigation change is therefore required for a complete fix.
Change
createChildReport()globally based onchildTypeA broader change could prevent optimistic chat creation for every typed child report, such as tasks or expenses. However, that changes shared report-creation behavior beyond the scope of this issue and requires a wider audit of all callers and optimistic workflows.
A support-ticket-specific route branch is safer for this issue because it fixes the newly exposed interaction without changing unrelated child-report behavior.
Add an unread/bold state for support-ticket replies
Showing unread or bold styling could be useful as a separate enhancement, but it is not required by the issue. The reported problem is that customers cannot tell that a thread exists at all. Adding read-state logic would introduce additional Onyx subscriptions and behavior that are not necessary to solve the original problem.
Validation plan
Before submitting a pull request, run the repository-required checks:
Run the focused tests for:
Also run the focused
ReportActionItemThreadUI/navigation test after adding the regression coverage.Manual verification should cover:
This proposal intentionally reuses the existing reply-summary component and fixes the ticket navigation invariant at the point where the new interaction is exposed.
melvin-bot commentedon Oct 23, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
Hatem-tmi commentedon Oct 24, 2026
Proposal
What is the root cause of that problem?
The Concierge ticket card is an ADD_COMMENT whose childType is SUPPORT_TICKET. That is isCreatedSupportTicketReportAction. The replies row already exists. ReportActionItem mounts ReportActionItemThread when shouldDisplayThreadReplies is true.
On main, that helper ends with a hard exclusion for isCreatedSupportTicketReportAction. The row stays hidden even when childVisibleActionCount and childCommenterCount are already set. SupportTicketPreview only draws the card. It does not need a second reply UI.
The exclusion is right for an empty "ticket created" card. It is wrong once the ticket has replies. That is why customers keep typing in the main Concierge composer.
The existing row already opens the child report with navigateToAndOpenChildReport and reportAction.childReportID. Showing the row is the missing piece, not a new navigator.
What changes do you think we should make in order to solve the problem?
Leave isCreatedSupportTicketReportAction as it is. Other callers still need it.
In shouldDisplayThreadReplies, skip that exclusion only when hasReplies is true (childVisibleActionCount greater than 0 and childCommenterCount set). Tickets with no replies stay without a replies row.
Do not add a count or avatar row inside SupportTicketPreview.
What alternative solutions did you explore?
Put the count inside SupportTicketPreview. That duplicates ReportActionItemThread and misses the shared tap path.
Remove isCreatedSupportTicketReportAction from the helper with no hasReplies guard. Empty tickets can then grow a replies row they should not have.
Change childType so the action is no longer a support ticket. That breaks ticket identification.
Contributor details
Your Expensify account email: hatem.tmi@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~0103158c5f0816abbe
melvin-bot commentedon Oct 24, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
dhaulagani commentedon Oct 24, 2026
Proposal
What is the root cause of that problem?
In src/libs/ReportUtils.ts, the shouldDisplayThreadReplies function explicitly suppresses thread reply indicators for support ticket parent actions:
ypescript return (hasReplies || isAgentProcessingInThread) && !isThreadReportParentAction && !isCreatedSupportTicketReportAction(reportAction);Because !isCreatedSupportTicketReportAction(reportAction) is hardcoded in the return condition, ReportActionItem never mounts ReportActionItemThread beneath SupportTicketPreview, even when customer support agents have actively responded inside the ticket (childVisibleActionCount > 0 and childCommenterCount > 0).
What changes do you think we should make in order to solve the problem?
Remove Support Ticket Exclusion in ReportUtils.ts:
In src/libs/ReportUtils.ts, remove && !isCreatedSupportTicketReportAction(reportAction) from shouldDisplayThreadReplies:
ypescript function shouldDisplayThreadReplies(reportAction: OnyxInputOrEntry<ReportAction>, isThreadReportParentAction: boolean, isAgentProcessingInThread = false): boolean { const hasReplies = (reportAction?.childVisibleActionCount ?? 0) > 0 && !reportAction?.childCommenterCount; return (hasReplies || isAgentProcessingInThread) && !isThreadReportParentAction; }Guard Support Ticket Navigation in
avigateToAndOpenChildReport (src/libs/actions/Report/index.ts):
When tapping the thread reply row on a support ticket action, if isCreatedSupportTicketReportAction(parentReportAction) is true and parentReportAction.childReportID exists, directly route to the ticket using getReportRouteForCurrentContext({reportID: parentReportAction.childReportID}) rather than executing createChildReport(). This prevents optimistically overriding the report's ype with a standard thread chat if childReport is not yet populated in the Onyx memory cache.
Hermetic Regression Tests:
Add test coverage in ests/unit/ReportUtilsTest.ts validating that shouldDisplayThreadReplies returns rue for support ticket report actions having visible child actions/replies, and returns alse when no replies exist.
What alternative solutions did you explore? (Optional)
Embedding a custom reply counter badge directly inside SupportTicketPreview.tsx. However, reusing the standard ReportActionItemThread component keeps the design system consistent across threads and avoids duplicate reply counter logic.
Add ReportActionItemThread support ticket press test (Expensify#104400)
SARAMALI15792 commentedon Oct 24, 2026
@ChavdaSachin I have updated my proposal with the test results and the final test branch.
ADITYASINGH1206 commentedon Oct 24, 2026
🚨 Edited by proposal-police: This proposal was edited at 2026-10-24 13:14:13 UTC.
Contributor details
Your Expensify account email: simghaditya21@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~01b6be874e7d6b47d1
Proposal
Please re-state the problem that we are trying to solve in this issue.
In Concierge chat, support ticket preview cards do not display the thread reply counter when a support representative responds. Customers have no visual cue that their conversation with support is happening inside the ticket thread, so they mistakenly continue typing into the main Concierge chat instead.
What is the root cause of that problem?
src/libs/ReportUtils.ts, theshouldDisplayThreadReplieshelper explicitly excludes support ticket creation actions:It checks
(hasReplies || isAgentProcessingInThread) && !isThreadReportParentAction && !isCreatedSupportTicketReportAction(reportAction).Because
!isCreatedSupportTicketReportAction(reportAction)is hardcoded in the return condition,ReportActionItem.tsx:698never renders the existingReportActionItemThreadcomponent beneathSupportTicketPreview, even whenchildVisibleActionCount > 0andchildCommenterCount > 0confirm that support reps have sent replies.tests/unit/ReportUtilsTest.ts, line 7418 explicitly tests and expects[CONST.REPORT.TYPE.SUPPORT_TICKET, false], enforcing this omission in the test suite.ReportActionItemThreaddelegates tonavigateToAndOpenChildReport. If the support ticket report is not yet populated in Onyx (e.g. cold start or fresh device),navigateToAndOpenChildReportfalls back tocreateChildReport, which builds an optimistic chat report rather than navigating to the support ticket route.What changes do you think we should make in order to solve the problem?
Allow thread replies for support ticket actions with replies (
src/libs/ReportUtils.ts):Update
shouldDisplayThreadRepliesto allowisCreatedSupportTicketReportAction(reportAction)when it has active replies (hasRepliesis true):Keep
!isThreadReportParentActionand maintain existing exclusions for context menu actions (joinThread,replyInThread) insrc/pages/inbox/report/ContextMenu/ContextMenuActions.tsxso sub-threads cannot be created on support tickets.Support Ticket-Aware Navigation (
src/pages/inbox/report/ReportActionItemThread.tsx):In
ReportActionItemThread.tsx, intercept support ticket creation actions to navigate directly to the ticket route viagetReportRouteForCurrentContext({reportID: reportAction.childReportID}):This guarantees that tapping the reply row directly opens the support ticket route even when
childReportis not yet cached in Onyx, preventingnavigateToAndOpenChildReportfrom falling back tocreateChildReportand building an optimistic chat report.Preserve Context Menu Constraints:
Keep the existing support-ticket checks in
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsxintact so that "Reply in thread", "Join thread", and "Leave thread" remain disabled for support ticket preview actions.What specific scenarios should we cover in automated tests?
tests/unit/ReportUtilsTest.ts:Update the existing test case for
CONST.REPORT.TYPE.SUPPORT_TICKETto expecttruewhen child replies exist:false.isAgentProcessingInThread === truereturntrue.false.What alternative solutions did you explore? (Optional)
SupportTicketPreview.tsx:Explored creating a dedicated reply count badge directly inside
SupportTicketPreview. We rejected this because it duplicates logic already implemented inReportActionItemThreadand does not follow the requirement to reuse the existing thread-reply UI pattern.navigateToAndOpenChildReportunchanged:Rejected because if the ticket report data has not yet loaded in Onyx,
createChildReportwould construct a generic chat report object, potentially displaying the ticket with the wrong header and missing support ticket actions.melvin-bot commentedon Oct 24, 2026
melvin-bot commentedon Oct 24, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
github-actions commentedon Oct 24, 2026
kevinjulio93 commentedon Oct 24, 2026
Contributor details
Your Expensify account email: kevinjulio93@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~0105b043b39ce044a9
Proposal
Please re-state the problem that we are trying to solve in this issue.
Support ticket previews in the Concierge conversation do not display the thread replies summary (reply count, avatars, timestamp) when a support representative responds. Because the ticket card looks unchanged, customers assume no reply was received and continue typing in the main Concierge room, inadvertently creating new unwanted threads.
What is the root cause of that problem?
There are two distinct root causes:
In
src/libs/ReportUtils.ts,shouldDisplayThreadReplies()explicitly excludes support tickets:Even though the backend sends
childVisibleActionCountandchildCommenterCount, the check forcesfalse, preventingReportActionItemThreadfrom rendering under the preview.ReportActionItemThreadcallsnavigateToAndOpenChildReport. If the support ticket report is not yet hydrated in Onyx,navigateToAndOpenChildReportfalls back tocreateChildReport(). Insrc/libs/actions/Report/index.ts,createChildReport()unconditionally builds an optimistic report viabuildOptimisticChatReport({ ... })(hardcodingtype: CHAT) and merges it into Onyx under${childReportID}.Writing a generic
CHATobject over an existing server-sideSUPPORT_TICKETcorrupts the report type in Onyx until the network fetch returns, temporarily stripping ticket-specific layout (ticket header, resolved footer, survey, and composer behavior).What changes do you think we should make in order to solve the problem?
Applying SOLID principles (Single Responsibility & Open-Closed Principle), we must keep presentation components decoupled from report routing policies and protect Onyx integrity:
1. Enable Thread Replies in
ReportUtils.ts(SRP):Remove the
!isCreatedSupportTicketReportAction(reportAction)clause fromshouldDisplayThreadReplies:This allows
ReportActionItemThreadto mount naturally for support ticket actions with replies, exactly as it does for comments and tasks, while preserving protections against rendering inside parent thread actions or search views.2. Prevent Fabricated Chat Seeding for Typed Child Reports in
src/libs/actions/Report/index.ts(OCP & LSP):In
createChildReport(), we should respect theparentReportAction.childTypecontract. WhenchildReportIDexists andparentReportAction.childTypeindicates an existing server-managed entity (e.g.supportTicket), we must not seed a fabricatedCHATreport into Onyx.Instead, when the child report is not in Onyx,
navigateToAndOpenChildReportshould navigate directly viagetReportRouteForCurrentContext({reportID: childReportID}), allowingReportFetchHandlerto fetch the authentic ticket from the backend without mutating local Onyx state with an invalid type.This approach ensures:
ReportActionItemThread.tsxremains a pure, decoupled presentation component without one-off ticket routing hacks.3. Update Unit & Regression Tests:
tests/unit/ReportUtilsTest.ts: Update theshouldDisplayThreadRepliestable test to expecttrueforSUPPORT_TICKETactions with replies. Add a test ensuring ticket actions without replies returnfalse.tests/unit/ReportTest.ts: Verify thatnavigateToAndOpenChildReportwith an unloaded support ticket report navigates without writing an optimistic chat object to Onyx.What alternative solutions did you explore? (Optional)
ReportActionItemThread.tsx: Adding anif (isCreatedSupportTicketReportAction)inside the presentation component violates SRP and OCP, leaking domain-level report routing into a shared UI component.SupportTicketPreview.tsx: Violates DRY and duplicates avatar rendering, count formatting, and theme styles, risking UI divergence.melvin-bot commentedon Oct 24, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!