Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4c1d660
fix: disable edit navigation for pending agents
nabi-ebrahimi Jul 20, 2026
de3562d
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Jul 23, 2026
cfeff19
Fix optimistic agent edit page lookup
nabi-ebrahimi Jul 23, 2026
94d3e01
Classify optimistic agent mapping for export
nabi-ebrahimi Jul 23, 2026
f456a43
Use RAM-only agent ID mapping
nabi-ebrahimi Jul 23, 2026
7b96d52
classify agent mapping for export
nabi-ebrahimi Jul 23, 2026
e5dcc93
test: Cover agent mapping fallback
nabi-ebrahimi Jul 23, 2026
7fb2d59
test: Cover agent mapping selector
nabi-ebrahimi Jul 23, 2026
143a525
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Jul 29, 2026
44db2e4
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Jul 31, 2026
f598f4f
Fix optimistic agent route param
nabi-ebrahimi Jul 31, 2026
c16e191
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 4, 2026
2ecdbbf
Refine agent ID mapping selector
nabi-ebrahimi Aug 4, 2026
a5c0e4a
Refine agent ID mapping selector
nabi-ebrahimi Aug 4, 2026
f882573
Target agent route param update
nabi-ebrahimi Aug 4, 2026
232dfb6
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 5, 2026
e41cc75
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 7, 2026
2268ca2
Fix agent edit navigation for optimistic accountIDs
nabi-ebrahimi Aug 14, 2026
d900c68
Resolve optimistic agent accountID via persisted mapping
nabi-ebrahimi Aug 14, 2026
0e19104
fix: lint
nabi-ebrahimi Aug 14, 2026
09bfdee
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 17, 2026
75fac6e
Fix agent navigation accountID inconsistency
nabi-ebrahimi Aug 17, 2026
5b1fc9a
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 21, 2026
cf36124
Remove stale entries from optimisticAgentAccountIDMapping
nabi-ebrahimi Aug 21, 2026
2a6549c
Add unit tests for agent mapping resolution and pruning
nabi-ebrahimi Aug 21, 2026
8a45f67
Fix lint errors in agent mapping tests
nabi-ebrahimi Aug 21, 2026
2ee26d7
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 22, 2026
aad8450
Add blank line before comments
nabi-ebrahimi Aug 22, 2026
15b55eb
Clean up mapping from ProfilePage too
nabi-ebrahimi Aug 22, 2026
185142f
Backfill missing mapping timestamps
nabi-ebrahimi Aug 22, 2026
9d3e196
Combine agent mapping into a derived value
nabi-ebrahimi Aug 22, 2026
412c328
Use OnyxUtils.get instead of connectWithoutView in test
nabi-ebrahimi Aug 22, 2026
c38518c
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 26, 2026
0ecee43
Merge branch 'main' into fix/disable-pending-agent-edit-navigation
nabi-ebrahimi Aug 27, 2026
d4a61f6
Trim comment
nabi-ebrahimi Aug 27, 2026
a057933
split max-age constant into days
nabi-ebrahimi Aug 27, 2026
e7b9193
Read agent mapping via connectWithoutView, fix resolve race
nabi-ebrahimi Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ const ONYXKEYS = {
/** Contains all the personalDetails the user has access to, keyed by accountID */
PERSONAL_DETAILS_LIST: 'personalDetailsList',

/** Maps an agent's optimistic accountID to the real one CreateAgent assigns, so an agent screen opened on the optimistic ID resolves to the real agent. Persisted so it survives a reload (the mapping is sent only once). */
OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING: 'optimisticAgentAccountIDMapping',

/** When each OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING entry was written locally, so stale entries can be pruned. */
OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT: 'optimisticAgentAccountIDMappingCreatedAt',
Comment thread
nabi-ebrahimi marked this conversation as resolved.

/** Contains all the private personal details of the user */
PRIVATE_PERSONAL_DETAILS: 'private_personalDetails',

Expand Down Expand Up @@ -1323,6 +1329,7 @@ const ONYXKEYS = {
CARD_FEED_ERRORS: 'cardFeedErrors',
RAM_ONLY_SORTED_REPORT_ACTIONS: 'sortedReportActions',
LOGIN_TO_ACCOUNT_ID_MAP: 'loginToAccountIDMap',
OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_ENTRIES: 'optimisticAgentAccountIDMappingEntries',
},

/** Stores HybridApp specific state required to interoperate with OldDot */
Expand Down Expand Up @@ -1597,6 +1604,8 @@ type OnyxValuesMapping = {
[ONYXKEYS.STATUS_DRAFT_CUSTOM_CLEAR_AFTER_DATE]: string;
[ONYXKEYS.INPUT_FOCUSED]: boolean;
[ONYXKEYS.PERSONAL_DETAILS_LIST]: OnyxTypes.PersonalDetailsList;
[ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING]: OnyxTypes.OptimisticAgentAccountIDMapping;
[ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT]: OnyxTypes.OptimisticAgentAccountIDMappingCreatedAt;
[ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: OnyxTypes.PrivatePersonalDetails;
[ONYXKEYS.PERSONAL_DETAILS_METADATA]: Record<string, OnyxTypes.PersonalDetailsMetadata>;
[ONYXKEYS.TASK]: OnyxTypes.Task;
Expand Down Expand Up @@ -1857,6 +1866,7 @@ type OnyxDerivedValuesMapping = {
[ONYXKEYS.DERIVED.CARD_FEED_ERRORS]: OnyxTypes.CardFeedErrorsDerivedValue;
[ONYXKEYS.DERIVED.RAM_ONLY_SORTED_REPORT_ACTIONS]: OnyxTypes.SortedReportActionsDerivedValue;
[ONYXKEYS.DERIVED.LOGIN_TO_ACCOUNT_ID_MAP]: OnyxTypes.LoginToAccountIDMapDerivedValue;
[ONYXKEYS.DERIVED.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_ENTRIES]: OnyxTypes.OptimisticAgentAccountIDMappingEntriesDerivedValue;
};

type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping & OnyxDerivedValuesMapping;
Expand Down
38 changes: 38 additions & 0 deletions src/hooks/useResolvedAgentAccountID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {backfillOptimisticAccountIDMappingCreatedAt} from '@libs/actions/Agent';

import ONYXKEYS from '@src/ONYXKEYS';

import {useEffect} from 'react';

import useOnyx from './useOnyx';

/**
* Resolves an agent's optimistic accountID to the real one CreateAgent assigns, via the persisted
* `OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING`, so an agent screen opened on the optimistic accountID (even after a reload)
* shows the real agent instead of "Hmm... it's not here".
*
* The derived `OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_ENTRIES` defers its first compute until all its dependency
* connections are established, which can briefly lag behind the raw key on a cold cache — long enough to flash a
* not-found page in between. It's still used for `createdAt` below, which has no such timing sensitivity.
*
* Without a stamped `createdAt`, an entry is invisible to createAgent()'s pruning and never expires — this can
* happen when the mapping arrives via sync from another device/tab that resolved it first.
*
* @returns `[resolvedAccountID, isMappingLoaded]` - a not-found screen should wait for `isMappingLoaded` to avoid a
* brief not-found flash while the mapping loads. No-op (returns the input) when there's no mapping entry.
*/
Comment thread
nabi-ebrahimi marked this conversation as resolved.
function useResolvedAgentAccountID(routeAccountID: number): [number, boolean] {
const [realAccountID, mappingMetadata] = useOnyx(ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING, {selector: (mapping) => mapping?.[routeAccountID]});
const [createdAt, createdAtMetadata] = useOnyx(ONYXKEYS.DERIVED.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_ENTRIES, {selector: (entries) => entries?.[routeAccountID]?.createdAt});

useEffect(() => {
if (realAccountID === undefined || createdAt !== undefined || createdAtMetadata.status !== 'loaded') {
return;
}
backfillOptimisticAccountIDMappingCreatedAt(routeAccountID);
}, [realAccountID, createdAt, createdAtMetadata.status, routeAccountID]);

return [realAccountID ?? routeAccountID, mappingMetadata.status === 'loaded'];
}

export default useResolvedAgentAccountID;
2 changes: 2 additions & 0 deletions src/libs/ExportOnyxState/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const onyxKeysToRemove = new Set<ValueOf<typeof ONYXKEYS> | ValueOf<typeof ONYXK
ONYXKEYS.NVP_PRIVATE_BILLING_STATUS,
ONYXKEYS.RAM_ONLY_PLAID_LINK_TOKEN,
ONYXKEYS.RAM_ONLY_MERGE_HR_LINK_TOKEN,
ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING,
ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT,
ONYXKEYS.RAM_ONLY_IS_PRODUCT_MARKETING_WINDOW_COVERED,
ONYXKEYS.ONFIDO_TOKEN,
ONYXKEYS.ONFIDO_APPLICANT_ID,
Expand Down
57 changes: 53 additions & 4 deletions src/libs/actions/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,56 @@ import type {AvatarSource} from '@libs/UserAvatarUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Policy} from '@src/types/onyx';
import type {OptimisticAgentAccountIDMappingCreatedAt, Policy} from '@src/types/onyx';
import type NewAgentTemplate from '@src/types/onyx/NewAgentTemplate';
import type PolicyEmployee from '@src/types/onyx/PolicyEmployee';
import type {AnyOnyxUpdate} from '@src/types/onyx/Request';

import type {OnyxCollection, OnyxCollectionInputValue, OnyxUpdate} from 'react-native-onyx';
import type {OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx';

import Onyx from 'react-native-onyx';

const OPTIMISTIC_ACCOUNT_ID_MAPPING_MAX_AGE_IN_DAYS = 30;
const OPTIMISTIC_ACCOUNT_ID_MAPPING_MAX_AGE_MS = OPTIMISTIC_ACCOUNT_ID_MAPPING_MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000;

// Non-React file, so it reads its own data here instead of via useOnyx + a param (same pattern as HandleUnusedOptimisticID.ts).
let optimisticAccountIDMappingCreatedAt: OnyxEntry<OptimisticAgentAccountIDMappingCreatedAt>;
Onyx.connectWithoutView({
key: ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT,
callback: (value) => {
optimisticAccountIDMappingCreatedAt = value;
},
});

function getStaleOptimisticAccountIDMappingUpdates(): AnyOnyxUpdate[] {
const now = Date.now();
const staleOptimisticAccountIDs = Object.entries(optimisticAccountIDMappingCreatedAt ?? {})
.filter(([, createdAt]) => now - createdAt > OPTIMISTIC_ACCOUNT_ID_MAPPING_MAX_AGE_MS)
Comment thread
nabi-ebrahimi marked this conversation as resolved.
.map(([staleOptimisticAccountID]) => staleOptimisticAccountID);

if (staleOptimisticAccountIDs.length === 0) {
return [];
}

const staleEntries = Object.fromEntries(staleOptimisticAccountIDs.map((id) => [id, null]));
return [
{onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING, value: staleEntries},
{onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT, value: staleEntries},
];
}

function openAgentsPage() {
const finallyData: Array<OnyxUpdate<typeof ONYXKEYS.ARE_AGENTS_LOADED>> = [
const finallyData: AnyOnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.ARE_AGENTS_LOADED,
value: true,
},
];

read(READ_COMMANDS.OPEN_AGENTS_PAGE, null, {finallyData});
const optimisticData = getStaleOptimisticAccountIDMappingUpdates();

read(READ_COMMANDS.OPEN_AGENTS_PAGE, null, {optimisticData, finallyData});
}

function openProfilePage() {
Expand Down Expand Up @@ -106,6 +137,7 @@ function createAgent(
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReportID}`,
value: {isOptimisticReport: true},
},
...getStaleOptimisticAccountIDMappingUpdates(),
];

const successData: AnyOnyxUpdate[] = [
Expand All @@ -129,6 +161,13 @@ function createAgent(
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReportID}`,
value: {isOptimisticReport: false},
},

// Stamped here, not where the mapping itself arrives, since that onyxData is backend-owned.
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT,
value: {[optimisticAccountID]: Date.now()},
},
];

const failureData: AnyOnyxUpdate[] = [
Expand Down Expand Up @@ -174,6 +213,15 @@ function createAgent(
return {optimisticAccountID, avatarURI, optimisticReportID};
}

/**
* Backfills a createdAt timestamp for a mapping entry this device notices without one — e.g. one that arrived via
* sync from another device/tab that resolved it first, so this device never got the chance to stamp it itself.
* Without a timestamp an entry is invisible to createAgent()'s pruning and never expires.
*/
function backfillOptimisticAccountIDMappingCreatedAt(optimisticAccountID: number) {
Onyx.merge(ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT, {[optimisticAccountID]: Date.now()});
}

/**
* Stash the template chosen in the "New agent" picker so the custom-agent builder can open pre-filled.
*/
Expand Down Expand Up @@ -462,6 +510,7 @@ export {
openAgentsPage,
openProfilePage,
createAgent,
backfillOptimisticAccountIDMappingCreatedAt,
setNewAgentTemplate,
clearNewAgentTemplate,
clearAgentError,
Expand Down
2 changes: 2 additions & 0 deletions src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {OnyxDerivedValueConfig} from './types';
import cardFeedErrorsConfig from './configs/cardFeedErrors';
import loginToAccountIDMapConfig from './configs/loginToAccountIDMap';
import nonPersonalAndWorkspaceCardListConfig from './configs/nonPersonalAndWorkspaceCardList';
import optimisticAgentAccountIDMappingEntriesConfig from './configs/optimisticAgentAccountIDMappingEntries';
import outstandingReportsByPolicyIDConfig from './configs/outstandingReportsByPolicyID';
import personalAndWorkspaceCardListConfig from './configs/personalAndWorkspaceCardList';
import reportAttributesConfig from './configs/reportAttributes';
Expand All @@ -28,6 +29,7 @@ const ONYX_DERIVED_VALUES = {
[ONYXKEYS.DERIVED.CARD_FEED_ERRORS]: cardFeedErrorsConfig,
[ONYXKEYS.DERIVED.RAM_ONLY_SORTED_REPORT_ACTIONS]: sortedReportActionsConfig,
[ONYXKEYS.DERIVED.LOGIN_TO_ACCOUNT_ID_MAP]: loginToAccountIDMapConfig,
[ONYXKEYS.DERIVED.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_ENTRIES]: optimisticAgentAccountIDMappingEntriesConfig,
} as const satisfies {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[Key in ValueOf<typeof ONYXKEYS.DERIVED>]: OnyxDerivedValueConfig<Key, any>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';

import ONYXKEYS from '@src/ONYXKEYS';
import type {OptimisticAgentAccountIDMappingEntriesDerivedValue} from '@src/types/onyx';

/**
* Combines OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING (backend-owned) and OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT
* (client-owned) into one key per optimistic accountID, so consumers read a single always-in-sync source instead
* of two parallel keys that would otherwise have to be kept manually in lockstep.
*/
export default createOnyxDerivedValueConfig({
key: ONYXKEYS.DERIVED.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_ENTRIES,
dependencies: [ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING, ONYXKEYS.OPTIMISTIC_AGENT_ACCOUNT_ID_MAPPING_CREATED_AT],
compute: ([mapping, createdAtByOptimisticAccountID]) => {
if (!mapping) {
return {};
}

const entries: OptimisticAgentAccountIDMappingEntriesDerivedValue = {};
for (const [optimisticAccountID, realAccountID] of Object.entries(mapping)) {
if (realAccountID === undefined) {
continue;
}
entries[optimisticAccountID] = {realAccountID, createdAt: createdAtByOptimisticAccountID?.[optimisticAccountID]};
}
return entries;
},
});
18 changes: 13 additions & 5 deletions src/pages/settings/Agents/EditAgentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useConfirmModal from '@hooks/useConfirmModal';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useResolvedAgentAccountID from '@hooks/useResolvedAgentAccountID';
import useRuleBotGuardModal from '@hooks/useRuleBotGuardModal';
import useSwitchToDelegator from '@hooks/useSwitchToDelegator';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -40,22 +41,29 @@ function EditAgentPage({route}: EditAgentPageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const icons = useMemoizedLazyExpensifyIcons(['Trashcan', 'ChatBubble', 'Users']);
const accountID = route.params.accountID;

// Resolve the optimistic accountID to the real one so opening this page mid-CreateAgent (or after a reload) doesn't 404.
const [accountID, isResolvedAccountIDLoaded] = useResolvedAgentAccountID(route.params.accountID);
const [agent, agentMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT}${accountID}`);
const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (list) => list?.[accountID]});
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const {showConfirmModal} = useConfirmModal();
const showRuleBotGuardModal = useRuleBotGuardModal();
const chatWithAgent = useChatWithAgent();
const switchToDelegator = useSwitchToDelegator();
const isOnyxLoaded = agentMetadata.status === 'loaded' && personalDetailsMetadata.status === 'loaded';

// Wait for the optimistic->real mapping to load too, so an optimistic accountID that is about to resolve after a
// reload doesn't briefly flash the not-found page before the mapping is read from storage.
const isOnyxLoaded = isResolvedAccountIDLoaded && agentMetadata.status === 'loaded' && personalDetailsMetadata.status === 'loaded';
const shouldShowNotFoundPage = isOnyxLoaded && !agent && !personalDetails;

const agentLogin = personalDetails?.login ?? '';
const handleBackPress = () => Navigation.goBack();
const handleEditAvatarPress = () => Navigation.navigate(ROUTES.SETTINGS_AGENTS_EDIT_AVATAR.getRoute(accountID));
const handleEditNamePress = () => Navigation.navigate(ROUTES.SETTINGS_AGENTS_EDIT_NAME.getRoute(accountID));
const handleEditPromptPress = () => Navigation.navigate(ROUTES.SETTINGS_AGENTS_EDIT_PROMPT.getRoute(accountID));

// Navigate with the raw route accountID (not the resolved one) so the sub-page URL stays consistent with this page's URL and device back doesn't create a duplicate entry.
const handleEditAvatarPress = () => Navigation.navigate(ROUTES.SETTINGS_AGENTS_EDIT_AVATAR.getRoute(route.params.accountID));
const handleEditNamePress = () => Navigation.navigate(ROUTES.SETTINGS_AGENTS_EDIT_NAME.getRoute(route.params.accountID));
const handleEditPromptPress = () => Navigation.navigate(ROUTES.SETTINGS_AGENTS_EDIT_PROMPT.getRoute(route.params.accountID));
const handleDeletePress = async () => {
const ruleBotEnforcedPolicy = getRuleBotEnforcedPolicy(accountID, allPolicies);
if (ruleBotEnforcedPolicy) {
Expand Down
6 changes: 4 additions & 2 deletions src/pages/settings/Agents/Fields/EditAgentAvatarPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useDiscardChangesConfirmation from '@hooks/useDiscardChangesConfirmation'
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useResolvedAgentAccountID from '@hooks/useResolvedAgentAccountID';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';

Expand Down Expand Up @@ -233,11 +234,12 @@ function EditAgentAvatarContent({accountID, fallbackRoute, onSave, initialPreset
EditAgentAvatarContent.displayName = 'EditAgentAvatarContent';

function EditAgentAvatarPage({route}: EditAgentAvatarPageProps) {
const {accountID} = route.params;
// Resolve the optimistic accountID to the real one so opening this page mid-CreateAgent (or after a reload) doesn't 404.
const [accountID] = useResolvedAgentAccountID(route.params.accountID);
return (
<EditAgentAvatarContent
accountID={accountID}
fallbackRoute={ROUTES.SETTINGS_AGENTS_EDIT.getRoute(accountID)}
fallbackRoute={ROUTES.SETTINGS_AGENTS_EDIT.getRoute(route.params.accountID)}
/>
);
}
Expand Down
9 changes: 6 additions & 3 deletions src/pages/settings/Agents/Fields/EditNamePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import TextInput from '@components/TextInput';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useResolvedAgentAccountID from '@hooks/useResolvedAgentAccountID';
import useThemeStyles from '@hooks/useThemeStyles';

import {updateAgentName} from '@libs/actions/Agent';
Expand All @@ -28,14 +29,16 @@ type EditNamePageProps = PlatformStackScreenProps<SettingsNavigatorParamList, ty
function EditNamePage({route}: EditNamePageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const accountID = route.params.accountID;

// Resolve the optimistic accountID to the real one so opening this page mid-CreateAgent (or after a reload) doesn't 404.
const [accountID] = useResolvedAgentAccountID(route.params.accountID);
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (list) => list?.[accountID]});

const {inputCallbackRef} = useAutoFocusInput();

const handleSubmit = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.EDIT_AGENT_NAME_FORM>) => {
updateAgentName(accountID, values[INPUT_IDS.FIRST_NAME].trim(), personalDetails?.displayName ?? '');
Navigation.goBack(ROUTES.SETTINGS_AGENTS_EDIT.getRoute(accountID));
Navigation.goBack(ROUTES.SETTINGS_AGENTS_EDIT.getRoute(route.params.accountID));
};

return (
Expand All @@ -47,7 +50,7 @@ function EditNamePage({route}: EditNamePageProps) {
>
<HeaderWithBackButton
title={translate('editAgentNamePage.title')}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_AGENTS_EDIT.getRoute(accountID))}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_AGENTS_EDIT.getRoute(route.params.accountID))}
/>
<FormProvider
formID={ONYXKEYS.FORMS.EDIT_AGENT_NAME_FORM}
Expand Down
5 changes: 4 additions & 1 deletion src/pages/settings/Agents/Fields/EditPromptPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import useIsInLandscapeMode from '@hooks/useIsInLandscapeMode';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useResolvedAgentAccountID from '@hooks/useResolvedAgentAccountID';
import useThemeStyles from '@hooks/useThemeStyles';

import {updateAgentPrompt} from '@libs/actions/Agent';
Expand All @@ -32,7 +33,9 @@ function EditPromptPage({route}: EditPromptPageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const shouldUseScrollableLayout = useIsInLandscapeMode();
const accountID = route.params.accountID;

// Resolve the optimistic accountID to the real one so opening this page mid-CreateAgent (or after a reload) doesn't 404.
const [accountID] = useResolvedAgentAccountID(route.params.accountID);
const [agentPrompt] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT}${accountID}`);

const validate = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.EDIT_AGENT_PROMPT_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.EDIT_AGENT_PROMPT_FORM> => {
Expand Down
Loading
Loading