Skip to content

Commit 8151a35

Browse files
authored
Merge pull request #88747 from Expensify/chirag-AZ-indicator-changes
[Payment due @ahmedGaber93] Remove optimistic Concierge thinking indicator
2 parents d8aac60 + 0e0e450 commit 8151a35

6 files changed

Lines changed: 18 additions & 313 deletions

File tree

src/ONYXKEYS.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,9 +600,6 @@ const ONYXKEYS = {
600600
/** Company cards custom names */
601601
NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES: 'nvp_expensify_ccCustomNames',
602602

603-
/** Whether to kick off the "Concierge is thinking" indicator when AgentZeroStatusGate mounts */
604-
CONCIERGE_THINKING_KICKOFF: 'conciergeThinkingKickoff',
605-
606603
/** The user's Concierge reportID */
607604
CONCIERGE_REPORT_ID: 'conciergeReportID',
608605

@@ -1527,7 +1524,6 @@ type OnyxValuesMapping = {
15271524
[ONYXKEYS.LAST_ROUTE]: string;
15281525
[ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean;
15291526
[ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record<string, string>;
1530-
[ONYXKEYS.CONCIERGE_THINKING_KICKOFF]: boolean;
15311527
[ONYXKEYS.CONCIERGE_REPORT_ID]: string;
15321528
[ONYXKEYS.SELF_DM_REPORT_ID]: string;
15331529
[ONYXKEYS.SHARE_UNKNOWN_USER_DETAILS]: Participant;

src/components/Search/SearchRouter/useAskConcierge.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import useOnyx from '@hooks/useOnyx';
44
import useOpenConciergeAnywhere from '@hooks/useOpenConciergeAnywhere';
55
import useSidePanelReportID from '@hooks/useSidePanelReportID';
66
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
7-
import {addComment, setConciergeThinkingKickoff} from '@userActions/Report';
7+
import {addComment} from '@userActions/Report';
88
import CONST from '@src/CONST';
99
import ONYXKEYS from '@src/ONYXKEYS';
1010

@@ -26,7 +26,6 @@ function useAskConcierge() {
2626
if (!targetReport || !targetReportID) {
2727
return;
2828
}
29-
setConciergeThinkingKickoff();
3029
addComment({
3130
report: targetReport,
3231
notifyReportID: targetReportID,

src/libs/actions/Report/index.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7581,14 +7581,6 @@ function setOptimisticTransactionThread(reportID?: string, parentReportID?: stri
75817581
});
75827582
}
75837583

7584-
function setConciergeThinkingKickoff() {
7585-
Onyx.set(ONYXKEYS.CONCIERGE_THINKING_KICKOFF, true);
7586-
}
7587-
7588-
function clearConciergeThinkingKickoff() {
7589-
Onyx.set(ONYXKEYS.CONCIERGE_THINKING_KICKOFF, null);
7590-
}
7591-
75927584
export type {Video, GuidedSetupData, TaskForParameters, IntroSelected, OpenReportActionParams, ParticipantInfo};
75937585

75947586
export {
@@ -7707,6 +7699,4 @@ export {
77077699
prepareOnyxDataForCleanUpOptimisticParticipants,
77087700
getGuidedSetupDataForOpenReport,
77097701
getReportChannelName,
7710-
setConciergeThinkingKickoff,
7711-
clearConciergeThinkingKickoff,
77127702
};

src/pages/inbox/AgentZeroStatusContext.tsx

Lines changed: 14 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import {getReportChatType} from '@selectors/Report';
22
import agentZeroProcessingIndicatorSelector from '@selectors/ReportNameValuePairs';
33
import React, {createContext, useContext, useEffect, useRef, useState} from 'react';
4-
import useLocalize from '@hooks/useLocalize';
54
import useNetwork from '@hooks/useNetwork';
65
import useOnyx from '@hooks/useOnyx';
7-
import {clearConciergeThinkingKickoff, getReportChannelName} from '@libs/actions/Report';
6+
import {getReportChannelName} from '@libs/actions/Report';
87
import Log from '@libs/Log';
98
import Pusher from '@libs/Pusher';
109
import CONST from '@src/CONST';
@@ -17,7 +16,7 @@ type ReasoningEntry = {
1716
};
1817

1918
type AgentZeroStatusState = {
20-
/** Whether AgentZero is actively working — true when the server sent a processing label or we're optimistically waiting */
19+
/** Whether AgentZero is actively working — true when the server has sent a processing label */
2120
isProcessing: boolean;
2221

2322
/** Chronological list of reasoning steps streamed via Pusher during the current processing request */
@@ -27,23 +26,13 @@ type AgentZeroStatusState = {
2726
statusLabel: string;
2827
};
2928

30-
type AgentZeroStatusActions = {
31-
/** Sets optimistic "thinking" state immediately after the user sends a message, before the server responds */
32-
kickoffWaitingIndicator: () => void;
33-
};
34-
3529
const defaultState: AgentZeroStatusState = {
3630
isProcessing: false,
3731
reasoningHistory: [],
3832
statusLabel: '',
3933
};
4034

41-
const defaultActions: AgentZeroStatusActions = {
42-
kickoffWaitingIndicator: () => {},
43-
};
44-
4535
const AgentZeroStatusStateContext = createContext<AgentZeroStatusState>(defaultState);
46-
const AgentZeroStatusActionsContext = createContext<AgentZeroStatusActions>(defaultActions);
4736

4837
/**
4938
* Cheap outer guard — only subscribes to the scalar CONCIERGE_REPORT_ID.
@@ -77,59 +66,33 @@ function AgentZeroStatusProvider({reportID, children}: React.PropsWithChildren<{
7766
const MIN_DISPLAY_TIME = 300; // ms
7867
// Debounce delay for server label updates
7968
const DEBOUNCE_DELAY = 150; // ms
80-
const OPTIMISTIC_TIMEOUT = 120000; // 2 minutes
8169

8270
/**
83-
* Inner gate — all Pusher, reasoning, label, and processing state.
84-
* Only mounted when reportID matches the Concierge report.
71+
* Inner gate — all Pusher, reasoning, and label state.
72+
* Only mounted for AgentZero chats (Concierge DMs or policy #admins rooms).
8573
* Remounted via key prop when reportID changes, so all state resets automatically.
8674
*/
8775
function AgentZeroStatusGate({reportID, children}: React.PropsWithChildren<{reportID: string}>) {
8876
// Server-driven processing label from report name-value pairs (e.g. "Looking up categories...")
77+
// Backend only writes this when AgentZero is actually handling the chat — the client no longer
78+
// sets an optimistic label on send, so if AZ short-circuits (chat job exists, human responded
79+
// within R2LR_TIME) nothing renders.
8980
const [serverLabel] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, {selector: agentZeroProcessingIndicatorSelector});
9081

91-
// Timestamp set when the user sends a message, before the server label arrives — shows "Concierge is thinking..."
92-
const [optimisticStartTime, setOptimisticStartTime] = useState<number | null>(null);
9382
// Debounced label shown to the user — smooths rapid server label changes
9483
const displayedLabelRef = useRef<string>('');
9584
const [displayedLabel, setDisplayedLabel] = useState<string>('');
9685
// Chronological list of reasoning steps streamed via Pusher during a single processing request
9786
const [reasoningHistory, setReasoningHistory] = useState<ReasoningEntry[]>([]);
98-
const {translate} = useLocalize();
9987
// Timer for debounced label updates — ensures a minimum display time before switching
10088
const updateTimerRef = useRef<NodeJS.Timeout | null>(null);
10189
// Timestamp of the last label update — used to enforce MIN_DISPLAY_TIME
10290
const lastUpdateTimeRef = useRef<number>(0);
10391
const {isOffline} = useNetwork();
10492

105-
// Auto-kickoff "thinking" indicator when opened from search (where kickoffWaitingIndicator isn't accessible)
106-
const [shouldKickoff] = useOnyx(ONYXKEYS.CONCIERGE_THINKING_KICKOFF);
107-
useEffect(() => {
108-
if (!shouldKickoff) {
109-
return;
110-
}
111-
clearConciergeThinkingKickoff();
112-
// eslint-disable-next-line react-hooks/set-state-in-effect -- one-shot kickoff from search; Onyx flag is cleared immediately so it cannot cascade
113-
setOptimisticStartTime(Date.now());
114-
}, [shouldKickoff]);
115-
11693
// Tracks the current agentZeroRequestID so the Pusher callback can detect new requests
11794
const agentZeroRequestIDRef = useRef('');
11895

119-
// Clear optimistic state once server label arrives — the server has taken over
120-
if (serverLabel && optimisticStartTime) {
121-
setOptimisticStartTime(null);
122-
}
123-
124-
// Clear optimistic state when coming back online — stale optimism from offline
125-
const [prevIsOffline, setPrevIsOffline] = useState(isOffline);
126-
if (prevIsOffline !== isOffline) {
127-
setPrevIsOffline(isOffline);
128-
if (!isOffline && optimisticStartTime) {
129-
setOptimisticStartTime(null);
130-
}
131-
}
132-
13396
// Clear reasoning when processing ends (server label transitions from truthy → falsy)
13497
const [prevServerLabel, setPrevServerLabel] = useState(serverLabel);
13598
if (prevServerLabel !== serverLabel) {
@@ -190,12 +153,7 @@ function AgentZeroStatusGate({reportID, children}: React.PropsWithChildren<{repo
190153
// Synchronize the displayed label with debounce and minimum display time.
191154
// displayedLabelRef mirrors state so the effect can check the current value without depending on displayedLabel.
192155
useEffect(() => {
193-
let targetLabel = '';
194-
if (serverLabel) {
195-
targetLabel = serverLabel;
196-
} else if (optimisticStartTime) {
197-
targetLabel = translate('common.thinking');
198-
}
156+
const targetLabel = serverLabel ?? '';
199157

200158
if (displayedLabelRef.current === targetLabel) {
201159
return;
@@ -213,7 +171,6 @@ function AgentZeroStatusGate({reportID, children}: React.PropsWithChildren<{repo
213171
// Immediate update when enough time has passed or when clearing the label
214172
if (remainingMinTime === 0 || targetLabel === '') {
215173
displayedLabelRef.current = targetLabel;
216-
217174
setDisplayedLabel(targetLabel);
218175
lastUpdateTimeRef.current = now;
219176
} else {
@@ -233,52 +190,23 @@ function AgentZeroStatusGate({reportID, children}: React.PropsWithChildren<{repo
233190
}
234191
clearTimeout(updateTimerRef.current);
235192
};
236-
}, [serverLabel, optimisticStartTime, translate]);
237-
238-
// Pusher updates carrying the server label can be silently dropped, leaving the optimistic indicator stuck forever.
239-
useEffect(() => {
240-
if (!optimisticStartTime) {
241-
return;
242-
}
243-
const elapsed = Date.now() - optimisticStartTime;
244-
const remaining = Math.max(0, OPTIMISTIC_TIMEOUT - elapsed);
245-
const timer = setTimeout(() => {
246-
setOptimisticStartTime(null);
247-
}, remaining);
248-
return () => clearTimeout(timer);
249-
}, [optimisticStartTime]);
250-
251-
const kickoffWaitingIndicator = () => {
252-
setOptimisticStartTime(Date.now());
253-
};
193+
}, [serverLabel]);
254194

255-
// True when AgentZero is actively working — either the server sent a label or we're optimistically waiting
256-
const isProcessing = !isOffline && (!!serverLabel || !!optimisticStartTime);
195+
// True when AgentZero is actively working — the server has sent a label
196+
const isProcessing = !isOffline && !!serverLabel;
257197

258198
const stateValue: AgentZeroStatusState = {
259199
isProcessing,
260200
reasoningHistory,
261201
statusLabel: displayedLabel,
262202
};
263203

264-
const actionsValue: AgentZeroStatusActions = {
265-
kickoffWaitingIndicator,
266-
};
267-
268-
return (
269-
<AgentZeroStatusActionsContext.Provider value={actionsValue}>
270-
<AgentZeroStatusStateContext.Provider value={stateValue}>{children}</AgentZeroStatusStateContext.Provider>
271-
</AgentZeroStatusActionsContext.Provider>
272-
);
204+
return <AgentZeroStatusStateContext.Provider value={stateValue}>{children}</AgentZeroStatusStateContext.Provider>;
273205
}
274206

275207
function useAgentZeroStatus(): AgentZeroStatusState {
276208
return useContext(AgentZeroStatusStateContext);
277209
}
278210

279-
function useAgentZeroStatusActions(): AgentZeroStatusActions {
280-
return useContext(AgentZeroStatusActionsContext);
281-
}
282-
283-
export {AgentZeroStatusProvider, useAgentZeroStatus, useAgentZeroStatusActions};
284-
export type {AgentZeroStatusState, AgentZeroStatusActions, ReasoningEntry};
211+
export {AgentZeroStatusProvider, useAgentZeroStatus};
212+
export type {AgentZeroStatusState, ReasoningEntry};

src/pages/inbox/report/ReportActionCompose/useComposerSubmit.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {addDomainToShortMention} from '@libs/ParsingUtils';
2020
import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID, isSentMoneyReportAction} from '@libs/ReportActionsUtils';
2121
import {startSpan} from '@libs/telemetry/activeSpans';
2222
import {generateAccountID} from '@libs/UserUtils';
23-
import {useAgentZeroStatusActions} from '@pages/inbox/AgentZeroStatusContext';
2423
import {ActionListContext} from '@pages/inbox/ReportScreenContext';
2524
import CONST from '@src/CONST';
2625
import ONYXKEYS from '@src/ONYXKEYS';
@@ -30,7 +29,6 @@ import useSidePanelContext from './useSidePanelContext';
3029

3130
function useComposerSubmit(reportID: string): (comment: string) => void {
3231
const {isOffline} = useNetwork();
33-
const {kickoffWaitingIndicator} = useAgentZeroStatusActions();
3432
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
3533
const personalDetails = usePersonalDetails();
3634
const {availableLoginsList} = useShortMentionsList();
@@ -63,7 +61,6 @@ function useComposerSubmit(reportID: string): (comment: string) => void {
6361

6462
return (newComment: string) => {
6563
const newCommentTrimmed = newComment.trim();
66-
kickoffWaitingIndicator();
6764

6865
if (attachmentFileRef.current) {
6966
addAttachmentWithComment({

0 commit comments

Comments
 (0)