From ee8a77d1ec1bfdb3114136487db599a73d39b030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Bonet=20=28via=20MelvinBot=29?= Date: Fri, 29 May 2026 17:09:22 +0000 Subject: [PATCH 1/5] Don't disable AI prompt Save button when offline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Bonet --- .../settings/Profile/AgentAIPromptSection.tsx | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/pages/settings/Profile/AgentAIPromptSection.tsx b/src/pages/settings/Profile/AgentAIPromptSection.tsx index dc8a219300a3..347793e27098 100644 --- a/src/pages/settings/Profile/AgentAIPromptSection.tsx +++ b/src/pages/settings/Profile/AgentAIPromptSection.tsx @@ -11,6 +11,7 @@ import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {clearAgentPromptUpdateError, openProfilePage, updateAgentPrompt} from '@libs/actions/Agent'; @@ -47,6 +48,7 @@ function scrollInputIntoView(parentScrollViewRef: RefObject function AgentAIPromptSection({accountID, parentScrollViewRef}: AgentAIPromptSectionProps) { const {translate} = useLocalize(); + const {isOffline} = useNetwork(); const styles = useThemeStyles(); const icons = useMemoizedLazyExpensifyIcons(['Checkmark']); const [agentPrompt] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT}${accountID}`); @@ -95,19 +97,23 @@ function AgentAIPromptSection({accountID, parentScrollViewRef}: AgentAIPromptSec // eslint-disable-next-line react-hooks/exhaustive-deps }, [agentPrompt?.promptErrors]); + const triggerSavedConfirmation = useCallback(() => { + setShowSavedConfirmation(true); + if (savedConfirmationTimerRef.current) { + clearTimeout(savedConfirmationTimerRef.current); + } + savedConfirmationTimerRef.current = setTimeout(() => { + setShowSavedConfirmation(false); + savedConfirmationTimerRef.current = null; + }, SAVED_CONFIRMATION_DURATION_MS); + }, []); + useEffect(() => { if (wasSavingRef.current && !isSaving && !hasPromptErrors) { - setShowSavedConfirmation(true); - if (savedConfirmationTimerRef.current) { - clearTimeout(savedConfirmationTimerRef.current); - } - savedConfirmationTimerRef.current = setTimeout(() => { - setShowSavedConfirmation(false); - savedConfirmationTimerRef.current = null; - }, SAVED_CONFIRMATION_DURATION_MS); + triggerSavedConfirmation(); } wasSavingRef.current = isSaving; - }, [isSaving, hasPromptErrors]); + }, [isSaving, hasPromptErrors, triggerSavedConfirmation]); useEffect(() => { return () => { @@ -147,6 +153,12 @@ function AgentAIPromptSection({accountID, parentScrollViewRef}: AgentAIPromptSec } dismissInput(); updateAgentPrompt(accountID, trimmed, agentPrompt?.prompt ?? ''); + + // Offline: treat the optimistic write as the final state for UX purposes. The request will be + // replayed on reconnect, so showing "Saved" immediately matches the queued-but-done intent. + if (isOffline) { + triggerSavedConfirmation(); + } }; const handleChangeText = (text: string) => { @@ -192,17 +204,17 @@ function AgentAIPromptSection({accountID, parentScrollViewRef}: AgentAIPromptSec testID="ai-prompt-input" onFocus={handleInputFocus} /> +