From 3d22fdd251ecd870ceb87644f36d0a2ef72bd543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Ch=C3=A1vez?= Date: Sun, 15 Mar 2026 23:14:42 -0600 Subject: [PATCH 1/2] Show 'Concierge is thinking' indicator in #admins room The AgentZero status indicator (typing/thinking) was only shown in Concierge DM chats because the useAgentZeroStatusIndicator hook and the submit form logic were gated on isConciergeChatReport(), which returns false for policy #admins rooms. The backend already sends the indicator NVP for admin rooms, but the App was ignoring it. Extend the check to also activate the indicator for isAdminRoom() reports, so users see "Concierge is thinking..." in the #admins room during onboarding with the suggestedFollowups beta. Co-Authored-By: Claude Opus 4.6 --- src/hooks/useAgentZeroStatusIndicator.ts | 3 ++- src/pages/inbox/ReportScreen.tsx | 3 ++- .../report/ReportActionCompose/ReportActionCompose.tsx | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/hooks/useAgentZeroStatusIndicator.ts b/src/hooks/useAgentZeroStatusIndicator.ts index fd2e6503593dc..c1af11224b11a 100644 --- a/src/hooks/useAgentZeroStatusIndicator.ts +++ b/src/hooks/useAgentZeroStatusIndicator.ts @@ -15,7 +15,8 @@ type AgentZeroStatusState = { }; /** - * Hook to manage AgentZero status indicator for Concierge chats. + * Hook to manage AgentZero status indicator for chats where Concierge responds. + * This includes both Concierge DM chats and policy #admins rooms (where Concierge handles onboarding). * Subscribes to real-time reasoning updates via Pusher and manages processing state. */ function useAgentZeroStatusIndicator(reportID: string, isConciergeChat: boolean): AgentZeroStatusState { diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 41b14a9880ec9..c76be0e9d020b 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -361,12 +361,13 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr const newTransactions = useNewTransactions(reportMetadata?.hasOnceLoadedReportActions, reportTransactions); const isConciergeChat = isConciergeChatReport(report); + const shouldShowConciergeIndicator = isConciergeChat || isAdminRoom(report); const { isProcessing: isConciergeProcessing, reasoningHistory: conciergeReasoningHistory, statusLabel: conciergeStatusLabel, kickoffWaitingIndicator, - } = useAgentZeroStatusIndicator(String(report?.reportID ?? CONST.DEFAULT_NUMBER_ID), isConciergeChat); + } = useAgentZeroStatusIndicator(String(report?.reportID ?? CONST.DEFAULT_NUMBER_ID), shouldShowConciergeIndicator); const {closeSidePanel} = useSidePanelActions(); diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 29118046e8ddb..c6f626f4852a9 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -47,6 +47,7 @@ import { chatIncludesConcierge, getParentReport, getReportRecipientAccountIDs, + isAdminRoom, isChatRoom, isConciergeChatReport, isGroupChat, @@ -222,6 +223,7 @@ function ReportActionCompose({ const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); + const shouldShowConciergeIndicator = useMemo(() => isConciergeChat || isAdminRoom(report), [isConciergeChat, report]); const isTransactionThreadView = useMemo(() => isReportTransactionThread(report), [report]); const isExpensesReport = useMemo(() => reportTransactions && reportTransactions.length > 1, [reportTransactions]); @@ -336,7 +338,7 @@ function ReportActionCompose({ (newComment: string) => { const newCommentTrimmed = newComment.trim(); - if (isConciergeChat && kickoffWaitingIndicator) { + if (shouldShowConciergeIndicator && kickoffWaitingIndicator) { kickoffWaitingIndicator(); } @@ -373,7 +375,7 @@ function ReportActionCompose({ } }, [ - isConciergeChat, + shouldShowConciergeIndicator, kickoffWaitingIndicator, transactionThreadReport, report, From 1d5e72ed1ada13394c54fea6792cf688b39befb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Ch=C3=A1vez?= Date: Tue, 17 Mar 2026 15:28:53 -0600 Subject: [PATCH 2/2] Rename isConciergeChat to isAgentZeroChat and remove unnecessary useMemo Address review feedback: - Rename isConciergeChat parameter to isAgentZeroChat in useAgentZeroStatusIndicator hook since it now supports both Concierge DMs and #admins rooms - Remove useMemo wrapper for shouldShowConciergeIndicator in ReportActionCompose since it's a simple boolean expression that doesn't need memoization Co-Authored-By: Claude Opus 4.6 (1M context) --- src/hooks/useAgentZeroStatusIndicator.ts | 17 +++++++++-------- .../ReportActionCompose/ReportActionCompose.tsx | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hooks/useAgentZeroStatusIndicator.ts b/src/hooks/useAgentZeroStatusIndicator.ts index c1af11224b11a..a99893e64c599 100644 --- a/src/hooks/useAgentZeroStatusIndicator.ts +++ b/src/hooks/useAgentZeroStatusIndicator.ts @@ -15,11 +15,12 @@ type AgentZeroStatusState = { }; /** - * Hook to manage AgentZero status indicator for chats where Concierge responds. + * Hook to manage AgentZero status indicator for chats where AgentZero responds. * This includes both Concierge DM chats and policy #admins rooms (where Concierge handles onboarding). - * Subscribes to real-time reasoning updates via Pusher and manages processing state. + * @param reportID - The report ID to monitor + * @param isAgentZeroChat - Whether the chat is an AgentZero-enabled chat (Concierge DM or #admins room) */ -function useAgentZeroStatusIndicator(reportID: string, isConciergeChat: boolean): AgentZeroStatusState { +function useAgentZeroStatusIndicator(reportID: string, isAgentZeroChat: boolean): AgentZeroStatusState { const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const serverLabel = reportNameValuePairs?.agentZeroProcessingRequestIndicator?.trim() ?? ''; @@ -53,7 +54,7 @@ function useAgentZeroStatusIndicator(reportID: string, isConciergeChat: boolean) }, [reportID]); useEffect(() => { - if (!isConciergeChat) { + if (!isAgentZeroChat) { return; } @@ -64,7 +65,7 @@ function useAgentZeroStatusIndicator(reportID: string, isConciergeChat: boolean) return () => { unsubscribeFromReportReasoningChannel(reportID); }; - }, [isConciergeChat, reportID]); + }, [isAgentZeroChat, reportID]); useEffect(() => { const hadServerLabel = !!prevServerLabelRef.current; @@ -140,13 +141,13 @@ function useAgentZeroStatusIndicator(reportID: string, isConciergeChat: boolean) }, [isOffline]); const kickoffWaitingIndicator = useCallback(() => { - if (!isConciergeChat) { + if (!isAgentZeroChat) { return; } setOptimisticStartTime(Date.now()); - }, [isConciergeChat]); + }, [isAgentZeroChat]); - const isProcessing = isConciergeChat && !isOffline && (!!serverLabel || !!optimisticStartTime); + const isProcessing = isAgentZeroChat && !isOffline && (!!serverLabel || !!optimisticStartTime); return useMemo( () => ({ diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index c6f626f4852a9..dbe93d9727068 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -223,7 +223,7 @@ function ReportActionCompose({ const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); - const shouldShowConciergeIndicator = useMemo(() => isConciergeChat || isAdminRoom(report), [isConciergeChat, report]); + const shouldShowConciergeIndicator = isConciergeChat || isAdminRoom(report); const isTransactionThreadView = useMemo(() => isReportTransactionThread(report), [report]); const isExpensesReport = useMemo(() => reportTransactions && reportTransactions.length > 1, [reportTransactions]);