feat(contacts): add prefilled Add Address flow state and UI (LIVE-35745) - #20910
feat(contacts): add prefilled Add Address flow state and UI (LIVE-35745)#20910claudiiafg wants to merge 1 commit into
Conversation
Web Tools Build Status
|
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR adds a “prefilled” entry path for the Add Address flow (bypassing MAD + manual entry), introduces a dedicated Review screen, and adapts the existing flow/state to carry display context needed for review.
Changes:
- Added
startWithPrefilledto the Add Address flow view model, plus new types to support “prefilled” sessions. - Introduced Review UI (web + native) and wired it into flow rendering for the prefilled path.
- Simplified the Name step UI on web (removes confirmed-address read-only field, adds disclaimer banner) and updated tests accordingly.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| features/flow/contacts/src/steps/AddAddress/web.ts | Re-exports Review component/types for web consumers. |
| features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.test.ts | Updates name labels test data to match removed validAddress label. |
| features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.web.test.ts | Adds coverage for startWithPrefilled and updates state expectations (entryMode/displayContext). |
| features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts | Adds “prefilled” start API, extends session state with entry mode + display context, and updates navigation logic. |
| features/flow/contacts/src/steps/AddAddress/types.ts | Introduces display context / entry mode / prefill params & result types; extends view model interface. |
| features/flow/contacts/src/steps/AddAddress/prefillAddAddress.ts | Defines public API types for opening a prefilled Add Address flow from other features. |
| features/flow/contacts/src/steps/AddAddress/native.ts | Re-exports native Review component/types. |
| features/flow/contacts/src/steps/AddAddress/index.ts | Exposes new prefillAddAddress types at the step index. |
| features/flow/contacts/src/steps/AddAddress/Review/useContactsAddAddressReviewViewModel.web.ts | Adds a small VM to map flow state into Review view props. |
| features/flow/contacts/src/steps/AddAddress/Review/types.ts | Adds Review labels/props/view-props types. |
| features/flow/contacts/src/steps/AddAddress/Review/index.ts | Barrel export for Review (web) + its types. |
| features/flow/contacts/src/steps/AddAddress/Review/ContactsAddAddressReviewView.web.tsx | Adds the web Review UI. |
| features/flow/contacts/src/steps/AddAddress/Review/ContactsAddAddressReviewView.web.test.tsx | Adds test coverage for the web Review UI. |
| features/flow/contacts/src/steps/AddAddress/Review/ContactsAddAddressReview.web.tsx | Hooks web Review VM into the web Review view. |
| features/flow/contacts/src/steps/AddAddress/Review/ContactsAddAddressReview.native.tsx | Adds the native Review UI. |
| features/flow/contacts/src/steps/AddAddress/Flow/ContactsAddAddressFlowContent.web.tsx | Wires Review step into web flow; switches reviewing rendering by entryMode. |
| features/flow/contacts/src/steps/AddAddress/Flow/ContactsAddAddressFlowContent.web.test.tsx | Updates flow tests for new labels + prefilled review rendering. |
| features/flow/contacts/src/steps/AddAddress/Flow/ContactsAddAddressFlowContent.native.tsx | Adds a new native “review” step slot and props for rendering Review. |
| features/flow/contacts/src/steps/AddAddress/AddressName/useContactsAddAddressNameViewModel.web.ts | Removes address from name-step VM (no longer displayed). |
| features/flow/contacts/src/steps/AddAddress/AddressName/types.ts | Removes address/validAddress from Name step types/labels. |
| features/flow/contacts/src/steps/AddAddress/AddressName/Input/ContactsAddAddressNameInput.web.test.tsx | Updates tests to reflect removed confirmed-address field and label changes. |
| features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressNameView.web.tsx | Removes confirmed-address component and adds disclaimer banner. |
| features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressNameView.web.test.tsx | Updates tests for new Name step rendering (no address, disclaimer present). |
| features/flow/contacts/README.md | Documents the new prefilled Add Address entry mode. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Rsdoctor Bundle Diff AnalysisFound 7 projects in monorepo, 2 projects with changes. 📊 Quick Summary
📋 Detailed Reports (Click to expand)📁 desktop-rendererPath:
📦 Download Diff Report: desktop-renderer Bundle Diff 📁 mobilePath:
📦 Download Diff Report: mobile Bundle Diff Generated by Rsdoctor GitHub Action |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (3)
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:87
createMadDisplayContextderivesnetworkIdandnetwork.displayNamefrom the selected currency (currencyId/assetDisplayName). This will produce incorrect network context for non-native assets (e.g., token selection where currency display name ≠ network display name), andnetworkIdmay not represent a network at all. Consider extendingAddAddressCurrencySelectionto include a realnetwork(or anetworkId/networkDisplayName), or derive the network context via a mapping/port sodisplayContext.networkis accurate.
function createMadDisplayContext(selection: AddAddressCurrencySelection): AddAddressDisplayContext {
return {
assetDisplayName: selection.assetDisplayName,
network: {
networkId: selection.currencyId,
displayName: selection.assetDisplayName,
},
};
}
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:148
- When a validation result becomes stale (request id mismatch),
startWithPrefilledreturns{ status: \"unavailable\" }, which conflates "stale/cancelled" with actual service unavailability. This can mislead callers into treating a user-driven cancellation/race as an outage. Suggest adding an explicit status (e.g.,cancelled/stale) toPrefillAddAddressStartResult(and aligning withOpenPrefillAddAddressResult, which already includescancelled), then return that status here instead.
if (validationRequestId.current !== requestId) {
return { status: "unavailable" };
}
features/flow/contacts/src/steps/AddAddress/Review/useContactsAddAddressReviewViewModel.web.ts:18
- A new view-model (
useContactsAddAddressReviewViewModel) is introduced but doesn’t have direct unit coverage. Adding a small test to assert the mapping (resolved address vs. display context fields, and thatonContinueis forwarded) would help prevent accidental regressions as the Review UI evolves.
export function useContactsAddAddressReviewViewModel({
addressEntry,
addressLabel,
displayContext,
labels,
onContinue,
}: ContactsAddAddressReviewProps): ContactsAddAddressReviewViewProps {
return {
address: addressEntry.resolvedAddress,
currency: displayContext.assetDisplayName,
network: displayContext.network.displayName,
name: addressLabel.label,
labels,
onContinue,
};
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (4)
features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressNameView.web.tsx:36
- The changeset text says this PR keeps the existing MAD path unchanged, but the web naming step is now ‘name-only’ for all entry modes (the confirmed address UI was removed). If the intent is to change only the prefilled path, consider passing
entryMode(or a flag) into the naming view and conditionally rendering the confirmed address section forentryMode: \"mad\"; otherwise update the changelog/description to reflect the MAD UX change.
<div className="flex flex-col gap-24">
<TextInput
autoComplete="off"
autoCorrect="off"
data-testid="contacts-add-address-name-input"
helperText={validationMessage}
hideClearButton
label={labels.inputLabel}
maxCount={CONTACT_ADDRESS_LABEL_MAX_LENGTH}
maxLength={CONTACT_ADDRESS_LABEL_MAX_LENGTH}
onChange={onAddressLabelChange}
spellCheck={false}
status={addressLabel.status === "invalid" ? "error" : undefined}
value={addressLabel.value}
/>
<Banner
appearance="info"
aria-label={labels.namingDisclaimerAccessibilityLabel}
data-testid="contacts-add-address-name-disclaimer"
description={labels.namingDisclaimer}
/>
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:148
- This branch represents a superseded/cancelled
startWithPrefilledcall (requestId mismatch), but it returns{ status: \"unavailable\" }, which is also used for genuine validation-port unavailability. That conflates two different outcomes and can cause callers to show the wrong error state. Consider adding an explicit{ status: \"cancelled\" }(or similar) toPrefillAddAddressStartResultand returning that here, while reservingunavailableforvalidationResult.status === \"unavailable\".
if (validationRequestId.current !== requestId) {
return { status: "unavailable" };
}
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:87
AddAddressDisplayContext.networkis populated fromselection.currencyId/selection.assetDisplayName, which makes the field namenetworkmisleading (it’s effectively duplicating the asset/currency selection, not a network context). To avoid confusion and future UI bugs (especially if Review is later enabled for MAD), consider either (a) renamingnetworkto reflect what it contains in the MAD path, or (b) extendingAddAddressCurrencySelectionto include an actual network context and using that here.
function createMadDisplayContext(selection: AddAddressCurrencySelection): AddAddressDisplayContext {
return {
assetDisplayName: selection.assetDisplayName,
network: {
networkId: selection.currencyId,
displayName: selection.assetDisplayName,
},
};
}
features/flow/contacts/src/steps/AddAddress/prefillAddAddress.ts:20
- The
invalid_address.errorunion is duplicated here and also inPrefillAddAddressStartResult(intypes.ts). Duplicating these string unions tends to drift over time. Consider extracting a shared exported type (e.g.,PrefillAddAddressInvalidReason) and reusing it in both result types.
export type OpenPrefillAddAddressResult =
| Readonly<{ status: "saved"; address: ContactAddress }>
| Readonly<{ status: "cancelled" }>
| Readonly<{
status: "invalid_address";
error: "invalid_format" | "domain_not_found" | "sanctioned";
}>
| Readonly<{ status: "unavailable" }>
| Readonly<{ status: "confirmation_failed" }>;
0c8f756 to
2c7a376
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (3)
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:148
- A request-id mismatch indicates the validation was superseded/cancelled (e.g., another validation started), but returning
{ status: \"unavailable\" }conflates cancellation with service unavailability. Consider introducing a distinct result (e.g.,cancelled/superseded) and handling it separately so callers don’t show an erroneous ‘service unavailable’ error state.
if (validationRequestId.current !== requestId) {
return { status: "unavailable" };
}
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:134
cancelAddressValidation()already incrementsvalidationRequestId, and thenstartWithPrefilledincrements it again viacurrent + 1. This double-step isn’t wrong, but it makes the cancellation semantics harder to reason about. Prefer a single, consistent way to advance the request id (either usecancelAddressValidation()and then readvalidationRequestId.current, or inline the increment once) to reduce confusion and future bugs.
cancelAddressValidation();
const existingAddressLabels = params.contact.addresses.map(address => address.label);
const normalizedAddress = params.address.trim();
const requestId = validationRequestId.current + 1;
validationRequestId.current = requestId;
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:154
- The new
startWithPrefilledbranch for an unavailable validation result is not covered by a test inuseAddAddressFlowViewModel.web.test.ts. Add a unit test wherecreateValidationPort({ status: \"unavailable\" })is returned, and assert thatstartWithPrefilledreturns{ status: \"unavailable\" }and the flow state is closed.
if (validationResult.status === "unavailable") {
setState(CLOSED_ADD_ADDRESS_FLOW_STATE);
return { status: "unavailable" };
}
2c7a376 to
17d420e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (2)
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:404
- The
reviewingAddressbranch ingoBackduplicates the same transition logic forprefilledand non-prefilled modes. This can be simplified by removing theentryModeconditional and performing the shared{ origin, ...session }+ ternary return once, reducing risk of future divergence.
if (currentState.entryMode === "prefilled") {
const { origin, ...session } = currentState;
return origin === "addressDetails"
? { ...session, status: "enteringAddress" }
: { ...session, status: "namingAddress" };
}
const { origin, ...session } = currentState;
return origin === "addressDetails"
? { ...session, status: "enteringAddress" }
: { ...session, status: "namingAddress" };
features/flow/contacts/src/steps/AddAddress/AddressName/Input/ContactsAddAddressNameInput.web.test.tsx:19
- Using
as neverin tests sidesteps the type system and can mask real integration issues (e.g., whenresolvedAddressis a branded/validated address type). Prefer constructing a correctly-typed value (e.g., via the existing schema/parser or a small typed test helper) so the tests fail if the contract changes.
value: "0xabc",
resolvedAddress: "0xabc" as never,
17d420e to
f520465
Compare
f520465 to
0e97f7d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (4)
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:405
- The
reviewingAddressbranch duplicates the exact same logic forprefilledvs non-prefilled modes. This can be simplified by removing theentryModeconditional and keeping a singleorigin-based transition, which reduces branching and future risk of one path diverging unintentionally.
case "reviewingAddress": {
if (currentState.entryMode === "prefilled") {
const { origin, ...session } = currentState;
return origin === "addressDetails"
? { ...session, status: "enteringAddress" }
: { ...session, status: "namingAddress" };
}
const { origin, ...session } = currentState;
return origin === "addressDetails"
? { ...session, status: "enteringAddress" }
: { ...session, status: "namingAddress" };
}
features/flow/contacts/src/steps/AddAddress/AddressName/Input/ContactsAddAddressNameInput.web.test.tsx:20
resolvedAddress: "0xabc" as neverdefeats type safety and makes the test less representative of real usage. Prefer constructing a correctly-typed resolved address (e.g., via the same schema/parser used elsewhere in tests) rather than forcing the type system withas never.
value: "0xabc",
resolvedAddress: "0xabc" as never,
inputMethod: "manual",
features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressNameView.web.test.tsx:16
- This test uses a plain string for
address, while production code likely uses a parsed/branded address type (as seen previously in this test file before the change). Using the real parsed type (schema/parse helper) will keep tests aligned with the domain model and catch regressions around formatting/typing earlier.
address: "0xabc",
features/flow/contacts/src/steps/AddAddress/Flow/ContactsAddAddressFlowContent.web.tsx:129
- For
entryMode === "prefilled", the Review UI is effectively required for the intended UX, butreviewLabelsis optional and silently falls back to the confirmation-styleContactsAddAddressCompletion. Consider makingreviewLabelsrequired when the caller intends to support the prefilled mode (or provide internal default labels) to avoid an unexpected/incorrect screen whenreviewLabelsis omitted.
case "reviewingAddress":
if (
state.entryMode === "prefilled" &&
state.displayContext !== null &&
reviewLabels !== undefined
) {
return (
<ContactsAddAddressReview
addressEntry={state.addressEntry}
addressLabel={state.addressLabel}
displayContext={state.displayContext}
labels={reviewLabels}
onContinue={onContinueFromReview}
/>
);
}
return (
<ContactsAddAddressCompletion
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (4)
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts:404
- The
reviewingAddressgoBacklogic is duplicated forprefilledvs non-prefilled, but both branches currently return the exact same result. This duplication increases maintenance cost and makes future changes error-prone; consider removing theentryModeconditional and keeping a singleorigin-based return path.
if (currentState.entryMode === "prefilled") {
const { origin, ...session } = currentState;
return origin === "addressDetails"
? { ...session, status: "enteringAddress" }
: { ...session, status: "namingAddress" };
}
const { origin, ...session } = currentState;
return origin === "addressDetails"
? { ...session, status: "enteringAddress" }
: { ...session, status: "namingAddress" };
features/flow/contacts/src/steps/AddAddress/Flow/ContactsAddAddressFlowContent.web.tsx:135
- For
entryMode === "prefilled", rendering the dedicated Review step depends onreviewLabelsbeing provided; otherwise the UI silently falls back to the completion/“confirm on device” screen. If the intent is “dedicated review step” for prefilled flows, this optionality can cause incorrect UX at runtime. Consider makingreviewLabelsrequired (at least whenentryModeisprefilled) or providing a safe default label set / explicit error behavior to avoid silent fallback.
if (
state.entryMode === "prefilled" &&
state.displayContext !== null &&
reviewLabels !== undefined
) {
return (
<ContactsAddAddressReview
addressEntry={state.addressEntry}
addressLabel={state.addressLabel}
displayContext={state.displayContext}
labels={reviewLabels}
onContinue={onContinueFromReview}
/>
);
}
return (
<ContactsAddAddressCompletion
buttonLabel={completionLabels.continue}
onContinue={onContinueFromReview}
testID="contacts-add-address-review"
title={completionLabels.title}
/>
);
features/flow/contacts/src/steps/AddAddress/AddressName/Input/ContactsAddAddressNameInput.web.test.tsx:21
- The test uses
resolvedAddress: "0xabc" as never, which defeats type-safety and can hide real contract mismatches between the component and domain types. Prefer constructing a validresolvedAddressusing the same schema/type used elsewhere in tests (e.g., parsing a valid address value) so the test fails if the domain type changes.
addressEntry: {
status: "valid",
value: "0xabc",
resolvedAddress: "0xabc" as never,
inputMethod: "manual",
},
features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.web.test.ts:1055
- The tests cover
goBack()from the prefilled naming step, but the prefilled path also introduces new navigation behavior from thereviewingAddressstep (e.g., returning to naming vs entering-address based onorigin). Adding a unit test that entersreviewingAddressvia the prefilled path and assertsgoBack()returns tonamingAddresswould better lock down the new behavior.
it("should close the prefilled naming step on back without returning to address entry", async () => {
const contact = mockContact({ addresses: [] });
const addressValidation = createValidationPort();
const { result } = renderHook(() => useAddAddressFlowViewModel({ addressValidation }));
await act(async () => {
await result.current.startWithPrefilled({
contact,
address: RAW_ADDRESS,
currency: ETHEREUM_SELECTION,
network: ETHEREUM_NETWORK,
});
});
act(() => result.current.goBack());
expect(result.current.state).toEqual({ status: "closed" });
});
0e97f7d to
f663af9
Compare
f663af9 to
0a2d557
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (2)
features/flow/flow-contacts-add-address/src/screens/AddressName/components/Input/ContactsAddAddressNameInput.web.test.tsx:20
resolvedAddress: "0xabc" as neverbypasses the brandedContactAddressValuetype and can let invalid test fixtures slip through. Prefer creating a valid branded value viaContactAddressValueSchema.parse(...)(as the tests did previously) so the fixture matches real runtime data.
addressEntry: {
status: "valid",
value: "0xabc",
resolvedAddress: "0xabc" as never,
inputMethod: "manual",
features/flow/flow-contacts-add-address/src/screens/Flow/ContactsAddAddressFlowContent.web.tsx:117
- When
state.entryMode === "prefilled", the flow should always show the dedicated review UI. Currently, ifreviewLabelsis omitted, the component silently falls back to the generic completion screen, effectively skipping the review step and hiding the address/currency/network details. Consider makingreviewLabelsrequired (or at least fail fast/render an explicit error) whenever the prefilled review path can be reached.
if (
state.entryMode === "prefilled" &&
state.displayContext !== null &&
reviewLabels !== undefined
) {
|


Summary
startWithPrefilled/entryModeand public prefill types in@features/flow-contacts.Test plan
startWithPrefilled, naming UI, review UI, and MAD regression pass in@features/flow-contactsnamingAddressand skips MAD / address entryStacked under: app wiring PR (next)