diff --git a/.changeset/live-33919-desktop-address-label.md b/.changeset/live-33919-desktop-address-label.md new file mode 100644 index 000000000000..5d425441d848 --- /dev/null +++ b/.changeset/live-33919-desktop-address-label.md @@ -0,0 +1,5 @@ +--- +"ledger-live-desktop": minor +--- + +Render the Desktop Contacts address label step diff --git a/.changeset/tidy-address-flow.md b/.changeset/tidy-address-flow.md new file mode 100644 index 000000000000..e4d12ea5e14e --- /dev/null +++ b/.changeset/tidy-address-flow.md @@ -0,0 +1,5 @@ +--- +"@features/flow-contacts": patch +--- + +Organize Add Address flow files by responsibility diff --git a/apps/ledger-live-desktop/src/mvvm/features/Contacts/__integrations__/Contacts.integration.test.tsx b/apps/ledger-live-desktop/src/mvvm/features/Contacts/__integrations__/Contacts.integration.test.tsx index 5caa858b8f59..8062489db4ed 100644 --- a/apps/ledger-live-desktop/src/mvvm/features/Contacts/__integrations__/Contacts.integration.test.tsx +++ b/apps/ledger-live-desktop/src/mvvm/features/Contacts/__integrations__/Contacts.integration.test.tsx @@ -39,6 +39,15 @@ jest.mock("~/renderer/store", () => ({ resetStore: jest.fn(), })); +jest.mock("@ledgerhq/live-common/bridge/index", () => ({ + ...jest.requireActual( + "@ledgerhq/live-common/bridge/index", + ), + getAccountBridgeByFamily: jest.fn().mockResolvedValue({ + validateAddress: jest.fn().mockResolvedValue(true), + }), +})); + const contextMenuValue = { close: mockClose, view: CONTEXT_MENU_VIEW.myWallet, @@ -543,6 +552,68 @@ describe("Contacts integration", () => { expect(dialog.querySelector('[data-slot="dialog-body"]')).toHaveClass("!mb-0"); }); + it("should render the address and its prefilled name together before review", async () => { + const { store, user } = render( + + + } /> + + , + { + skipRouter: true, + initialState: contactsPageInitialState(), + }, + ); + + await user.click(screen.getByTestId("contacts-me-row")); + await user.click(screen.getByTestId("contacts-detail-add-address")); + + const dialog = screen.getByRole("dialog"); + act(() => { + store + .getState() + .modularDialog.dialogParams?.onAssetSelected?.(getCryptoCurrencyById("ethereum")); + }); + + const addressInput = await screen.findByTestId("contacts-add-address-input"); + const addressNameInput = screen.getByTestId("contacts-add-address-name-input"); + expect(screen.getByRole("dialog")).toBe(dialog); + expect(addressNameInput).toHaveValue("Ethereum"); + expect(addressNameInput).toHaveAttribute("maxlength", "32"); + expect(screen.getByTestId("contacts-add-address-confirm")).toBeDisabled(); + + fireEvent.change(addressInput, { + target: { value: "0x1ad23b2cf8d2e0591ea417eb82f7cd9746c53034" }, + }); + + const confirmationButton = screen.getByTestId("contacts-add-address-confirm"); + await waitFor(() => expect(confirmationButton).toBeEnabled()); + + await user.clear(addressNameInput); + await user.type(addressNameInput, "Ethereum 💎"); + + expect(screen.getByText("Special characters are not allowed.")).toBeVisible(); + expect(confirmationButton).toBeDisabled(); + + await user.clear(addressNameInput); + await user.type(addressNameInput, "Exchange"); + await user.click(confirmationButton); + + expect(screen.getByRole("dialog")).toBe(dialog); + expect(screen.getByTestId("contacts-add-address-review")).toBeVisible(); + + await user.click( + screen.getByRole("button", { name: "components.dialogHeader.goBackAriaLabel" }), + ); + expect(screen.getByTestId("contacts-add-address-input")).toBeVisible(); + expect(screen.getByTestId("contacts-add-address-name-input")).toHaveValue("Exchange"); + + await user.click(screen.getByTestId("contacts-add-address-confirm")); + await user.click(screen.getByTestId("contacts-add-address-review-continue")); + + expect(screen.getByTestId("contacts-add-address-success")).toBeVisible(); + }); + it("should expose the Add Address session started for Me", async () => { const { user } = render( diff --git a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/ContactsAddAddressFlowDialog.tsx b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/ContactsAddAddressFlowDialog.tsx index 4a9e5e433715..732058f6ac10 100644 --- a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/ContactsAddAddressFlowDialog.tsx +++ b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/ContactsAddAddressFlowDialog.tsx @@ -3,12 +3,31 @@ import { DialogFlow, type DialogFlowScreenRegistry } from "LLD/components/Dialog import { ModularDialogFlow } from "LLD/features/ModularDialog/ModularDialogFlow"; import { ContactsAddAddressEntry, + ContactsAddAddressCompletion, + ContactsAddAddressName, type AddAddressFlowState, type ContactsAddAddressEntryLabels, + type ContactsAddAddressNameLabels, } from "@features/flow-contacts"; -import type { ContactsAddAddressFlowDialogProps } from "./types"; +import type { ContactsAddAddressFlowDialogProps, ContactsAddAddressReviewLabels } from "./types"; -type ContactsAddAddressDialogStep = "currency" | "address"; +type ContactsAddAddressDialogStep = "currency" | "address" | "name" | "review" | "success"; + +function resolveCurrentStep(state: AddAddressFlowState): ContactsAddAddressDialogStep { + switch (state.status) { + case "enteringAddress": + return "address"; + case "namingAddress": + return "name"; + case "reviewingAddress": + return "review"; + case "success": + return "success"; + case "selectingCurrency": + case "closed": + return "currency"; + } +} function isEnteringAddress( state: AddAddressFlowState, @@ -19,21 +38,75 @@ function isEnteringAddress( function createAddressContent( state: Extract, labels: ContactsAddAddressEntryLabels, + nameLabels: ContactsAddAddressNameLabels, onAddressChange: ContactsAddAddressFlowDialogProps["onAddressChange"], + onAddressLabelChange: ContactsAddAddressFlowDialogProps["onAddressLabelChange"], + onContinueFromAddressDetails: ContactsAddAddressFlowDialogProps["onContinueFromAddressDetails"], ): React.JSX.Element { return ( + ); +} + +function isNamingAddress( + state: AddAddressFlowState, +): state is Extract { + return state.status === "namingAddress"; +} + +function createNameContent( + state: Extract, + labels: ContactsAddAddressNameLabels, + onAddressLabelChange: ContactsAddAddressFlowDialogProps["onAddressLabelChange"], + onContinueFromName: ContactsAddAddressFlowDialogProps["onContinueFromName"], +): React.JSX.Element { + return ( + + ); +} + +function createCompletionContent( + state: Extract, + labels: ContactsAddAddressReviewLabels, + onContinueFromReview: ContactsAddAddressFlowDialogProps["onContinueFromReview"], + onClose: ContactsAddAddressFlowDialogProps["onClose"], +): React.JSX.Element { + const isReviewingAddress = state.status === "reviewingAddress"; + + return ( + ); } export function ContactsAddAddressFlowDialog({ state, - labels, + entryLabels, + nameLabels, + reviewLabels, onAddressChange, + onContinueFromAddressDetails, + onAddressLabelChange, + onContinueFromName, + onContinueFromReview, onBack, onClose, }: ContactsAddAddressFlowDialogProps): React.JSX.Element | null { @@ -45,7 +118,10 @@ export function ContactsAddAddressFlowDialog({ {modularDialog => { const isAddressEntry = isEnteringAddress(state); - const currentStep: ContactsAddAddressDialogStep = isAddressEntry ? "address" : "currency"; + const isAddressNaming = isNamingAddress(state); + const isReviewingAddress = state.status === "reviewingAddress"; + const isSuccess = state.status === "success"; + const currentStep = resolveCurrentStep(state); const screens: DialogFlowScreenRegistry = { currency: { content: modularDialog.content, @@ -60,13 +136,49 @@ export function ContactsAddAddressFlowDialog({ }, address: { content: isAddressEntry - ? createAddressContent(state, labels, onAddressChange) + ? createAddressContent( + state, + entryLabels, + nameLabels, + onAddressChange, + onAddressLabelChange, + onContinueFromAddressDetails, + ) + : modularDialog.content, + options: { + dialogHeaderProps: { density: "expanded", title: entryLabels.title }, + hasBackButton: true, + }, + }, + name: { + content: isAddressNaming + ? createNameContent(state, nameLabels, onAddressLabelChange, onContinueFromName) : modularDialog.content, options: { - dialogHeaderProps: { density: "expanded", title: labels.title }, + dialogHeaderProps: { density: "expanded", title: entryLabels.title }, + hasBackButton: true, + }, + }, + review: { + content: + isReviewingAddress || isSuccess + ? createCompletionContent(state, reviewLabels, onContinueFromReview, onClose) + : modularDialog.content, + options: { + dialogHeaderProps: { density: "expanded", title: entryLabels.title }, hasBackButton: true, }, }, + success: { + content: + isReviewingAddress || isSuccess + ? createCompletionContent(state, reviewLabels, onContinueFromReview, onClose) + : modularDialog.content, + options: { + dialogHeaderProps: { density: "expanded", title: entryLabels.title }, + hasBackButton: false, + }, + }, }; return ( @@ -77,7 +189,11 @@ export function ContactsAddAddressFlowDialog({ dialogContentProps: { className: "w-400 bg-canvas-sheet pb-0" }, }} isOpen - onBack={isAddressEntry ? onBack : modularDialog.onBack} + onBack={ + isAddressEntry || isAddressNaming || isReviewingAddress + ? onBack + : modularDialog.onBack + } onClose={onClose} screens={screens} /> diff --git a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/index.ts b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/index.ts index 3f2af919d2a9..1953733131a7 100644 --- a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/index.ts +++ b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/index.ts @@ -1,2 +1,2 @@ export { ContactsAddAddressFlowDialog } from "./ContactsAddAddressFlowDialog"; -export type { ContactsAddAddressFlowDialogProps } from "./types"; +export type { ContactsAddAddressFlowDialogProps, ContactsAddAddressReviewLabels } from "./types"; diff --git a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/types.ts b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/types.ts index 726a1d3bd173..a6deabe3c1a8 100644 --- a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/types.ts +++ b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/components/ContactsAddAddressFlowDialog/types.ts @@ -2,12 +2,26 @@ import type { AddAddressFlowState, AddAddressInputSource, ContactsAddAddressEntryLabels, + ContactsAddAddressNameLabels, } from "@features/flow-contacts"; +export type ContactsAddAddressReviewLabels = Readonly<{ + title: string; + continue: string; + successTitle: string; + close: string; +}>; + export type ContactsAddAddressFlowDialogProps = Readonly<{ state: AddAddressFlowState; - labels: ContactsAddAddressEntryLabels; + entryLabels: ContactsAddAddressEntryLabels; + nameLabels: ContactsAddAddressNameLabels; + reviewLabels: ContactsAddAddressReviewLabels; onAddressChange: (address: string, inputMethod: AddAddressInputSource) => void; + onContinueFromAddressDetails: () => void; + onAddressLabelChange: (value: string) => void; + onContinueFromName: () => void; + onContinueFromReview: () => void; onBack: () => void; onClose: () => void; }>; diff --git a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/useContactsViewModel.ts b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/useContactsViewModel.ts index be4c8bb060e8..cb70bc9c0107 100644 --- a/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/useContactsViewModel.ts +++ b/apps/ledger-live-desktop/src/mvvm/features/Contacts/screens/Contacts/useContactsViewModel.ts @@ -1,7 +1,12 @@ import { useCallback, useEffect, useMemo, useState, type ChangeEvent } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; -import type { ContactId } from "@domain/entity-contact"; +import { + CONTACT_ADDRESS_LABEL_TOO_LONG_ERROR_NAME, + DUPLICATE_CONTACT_ADDRESS_LABEL_ERROR_NAME, + INVALID_CONTACT_ADDRESS_LABEL_ERROR_NAME, + type ContactId, +} from "@domain/entity-contact"; import { CONTACTS_FEATURE_INTRODUCTION_HIGHLIGHTS, createContactsListViewModel, @@ -15,6 +20,7 @@ import { type AddAddressContact, type AddAddressFlowState, type ContactsAddAddressEntryLabels, + type ContactsAddAddressNameLabels, type ContactAddressDetailDialogProps, type ContactsLedgerSyncStatus, type ContactsListViewLabels, @@ -25,7 +31,10 @@ import { useContactsFeatureIntroductionPreference } from "../../hooks/useContact import { useContactsCurrencySelectionAdapter } from "../../hooks/useContactsCurrencySelectionAdapter"; import { useContactsAddressValidationAdapter } from "../../hooks/useContactsAddressValidationAdapter"; import { useContactDetailPaneAdapter } from "./useContactDetailPaneAdapter"; -import type { ContactsAddAddressFlowDialogProps } from "./components/ContactsAddAddressFlowDialog"; +import type { + ContactsAddAddressFlowDialogProps, + ContactsAddAddressReviewLabels, +} from "./components/ContactsAddAddressFlowDialog"; export type ContactsPageViewModel = Omit & Readonly<{ @@ -54,6 +63,10 @@ export function useContactsViewModel(): ContactsPageViewModel { completeCurrencySelection, goBack: goBackAddAddress, updateAddress, + updateAddressLabel, + continueFromAddressDetails, + continueFromName, + continueFromReview, close: closeAddAddress, } = useAddAddressFlowViewModel({ addressValidation }); const selectCurrencyForContact = useCallback( @@ -82,6 +95,14 @@ export function useContactsViewModel(): ContactsPageViewModel { closeAddAddress(); }, [cancelCurrencySelection, closeAddAddress]); const onBackAddAddress = useCallback(() => { + if ( + addAddressFlowState.status === "namingAddress" || + addAddressFlowState.status === "reviewingAddress" + ) { + goBackAddAddress(); + return; + } + if (addAddressFlowState.status !== "enteringAddress") { return; } @@ -105,22 +126,56 @@ export function useContactsViewModel(): ContactsPageViewModel { }), [t], ); + const addAddressNameLabels = useMemo( + () => ({ + inputLabel: t("contacts.addAddressName.inputLabel"), + continueToReview: t("contacts.addAddressName.continueToReview"), + validAddress: t("contacts.addAddressEntry.validAddress"), + validationErrors: { + [INVALID_CONTACT_ADDRESS_LABEL_ERROR_NAME]: t("contacts.addAddressName.invalidLabel"), + [DUPLICATE_CONTACT_ADDRESS_LABEL_ERROR_NAME]: t("contacts.addAddressName.duplicateLabel"), + [CONTACT_ADDRESS_LABEL_TOO_LONG_ERROR_NAME]: t("contacts.addAddressName.tooLongLabel"), + }, + }), + [t], + ); + const addAddressReviewLabels = useMemo( + () => ({ + title: t("contacts.addAddressReview.title"), + continue: t("contacts.addAddressReview.continue"), + successTitle: t("contacts.addAddressReview.successTitle"), + close: t("contacts.addAddressReview.close"), + }), + [t], + ); const addAddressFlowDialog = useMemo( () => ({ state: addAddressFlowState, - labels: addAddressEntryLabels, + entryLabels: addAddressEntryLabels, + nameLabels: addAddressNameLabels, + reviewLabels: addAddressReviewLabels, onAddressChange: (address, inputMethod) => { void updateAddress(address, inputMethod); }, + onContinueFromAddressDetails: continueFromAddressDetails, + onAddressLabelChange: updateAddressLabel, + onContinueFromName: continueFromName, + onContinueFromReview: continueFromReview, onBack: onBackAddAddress, onClose: onCloseAddAddress, }), [ addAddressEntryLabels, + addAddressNameLabels, + addAddressReviewLabels, addAddressFlowState, onBackAddAddress, onCloseAddAddress, updateAddress, + updateAddressLabel, + continueFromAddressDetails, + continueFromName, + continueFromReview, ], ); const { detail, addressDetailDialog, onOpenMe, onOpenContact } = diff --git a/apps/ledger-live-desktop/static/i18n/en/app.json b/apps/ledger-live-desktop/static/i18n/en/app.json index c962d7e403a1..245f996d03da 100644 --- a/apps/ledger-live-desktop/static/i18n/en/app.json +++ b/apps/ledger-live-desktop/static/i18n/en/app.json @@ -9306,6 +9306,19 @@ "validationUnavailable": "Address validation is temporarily unavailable.", "ensDisclaimer": "This ENS name resolves to the address shown above." }, + "addAddressName": { + "inputLabel": "Address name", + "continueToReview": "Continue to review", + "invalidLabel": "Special characters are not allowed.", + "duplicateLabel": "This address name is already used for this contact.", + "tooLongLabel": "Address names must be 32 characters or fewer." + }, + "addAddressReview": { + "title": "Review address", + "continue": "Confirm address", + "successTitle": "Address saved", + "close": "Close" + }, "addContactDrawer": { "namePlaceholder": "Contact name", "namingDisclaimer": "For privacy, avoid full names and surnames. Use a nickname or just a first name + initial, e.g. 'John S'.", diff --git a/features/flow/contacts/README.md b/features/flow/contacts/README.md index 13f0d8c6c1a5..1013b613100c 100644 --- a/features/flow/contacts/README.md +++ b/features/flow/contacts/README.md @@ -92,8 +92,15 @@ src/ │ │ ├── model/ # Contact-name validation and creation contract │ │ └── index.ts / web.ts / native.ts │ ├── AddAddress/ # Shared address-entry flow and native step content +│ │ ├── AddressEntry/ # Shared types and dumb platform views +│ │ │ ├── Entry/ # Address-entry containers, view models and tests +│ │ │ └── Placeholder/ # Temporary native state and test +│ │ ├── AddressName/ # Web address-label field +│ │ ├── Completion/ # Web success content +│ │ ├── CurrencySelection/ # MAD selection view model +│ │ ├── Flow/ # Session state and transitions │ │ ├── model/ # Network resolver, MAD port and address validation -│ │ └── ContactsAddAddressEntry.native.tsx / ContactsAddAddressPlaceholderView.native.tsx / useAddAddressFlowViewModel.ts / types.ts / index.ts +│ │ └── index.ts / web.ts / native.ts │ ├── Introduction/ # Feature intro + Ledger Sync intro (ex featureIntroduction) │ │ ├── Feature/ (web dialog + native content) / LedgerSync/ (web dialog + native content) │ │ ├── useContactsFeatureIntroductionState.ts / resolver.ts / ports.ts / constants.ts / types.ts diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.types.ts b/features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntry.types.ts similarity index 85% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.types.ts rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntry.types.ts index 36440a4aed46..d2810dde1867 100644 --- a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.types.ts +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntry.types.ts @@ -1,4 +1,8 @@ -import type { AddAddressEntryLabels, AddAddressEntryState, AddAddressInputSource } from "./types"; +import type { + AddAddressEntryLabels, + AddAddressEntryState, + AddAddressInputSource, +} from "../Flow/types"; export type ContactsAddAddressEntryProps = Readonly<{ addressEntry: AddAddressEntryState; diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.web.types.ts b/features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntry.web.types.ts similarity index 95% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.web.types.ts rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntry.web.types.ts index 6f1646518730..62f96cdd07d2 100644 --- a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.web.types.ts +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntry.web.types.ts @@ -1,11 +1,11 @@ import type { ChangeEvent, ClipboardEvent } from "react"; -import type { ContactsAddAddressNameLabels } from "./AddressName/types"; +import type { ContactsAddAddressNameLabels } from "../AddressName/types"; import type { AddAddressEntryLabels, AddAddressEntryState, AddAddressInputSource, AddAddressLabelState, -} from "./types"; +} from "../Flow/types"; export type AddressLabelConfiguration = Readonly<{ addressLabel: AddAddressLabelState; diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntryView.native.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntryView.native.tsx similarity index 100% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntryView.native.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntryView.native.tsx diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntryView.web.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntryView.web.tsx similarity index 100% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntryView.web.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/ContactsAddAddressEntryView.web.tsx diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.native.test.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.native.test.tsx similarity index 99% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.native.test.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.native.test.tsx index 835b1f4e878d..548c5e4c9ea4 100644 --- a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.native.test.tsx +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.native.test.tsx @@ -1,7 +1,7 @@ import React from "react"; import { fireEvent, render, screen } from "@testing-library/react-native"; import { ContactAddressValueSchema } from "@domain/entity-contact"; -import type { AddAddressEntryLabels, AddAddressEntryState } from "./types"; +import type { AddAddressEntryLabels, AddAddressEntryState } from "../../Flow/types"; import { ContactsAddAddressEntry } from "./ContactsAddAddressEntry.native"; const labels: AddAddressEntryLabels = { diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.native.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.native.tsx similarity index 67% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.native.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.native.tsx index a282d24c2b6e..7d86b9ebb349 100644 --- a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.native.tsx +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.native.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { ContactsAddAddressEntryView } from "./ContactsAddAddressEntryView.native"; -import type { ContactsAddAddressEntryProps } from "./ContactsAddAddressEntry.types"; +import { ContactsAddAddressEntryView } from "../ContactsAddAddressEntryView.native"; +import type { ContactsAddAddressEntryProps } from "../ContactsAddAddressEntry.types"; import { useContactsAddAddressEntryViewModel } from "./useContactsAddAddressEntryViewModel.native"; export function ContactsAddAddressEntry(props: ContactsAddAddressEntryProps): React.JSX.Element { diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.web.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.web.tsx similarity index 64% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.web.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.web.tsx index 064b9f531705..b934fe618bc7 100644 --- a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressEntry.web.tsx +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/ContactsAddAddressEntry.web.tsx @@ -1,6 +1,6 @@ import React from "react"; -import type { ContactsAddAddressEntryWebProps } from "./ContactsAddAddressEntry.web.types"; -import { ContactsAddAddressEntryView } from "./ContactsAddAddressEntryView.web"; +import type { ContactsAddAddressEntryWebProps } from "../ContactsAddAddressEntry.web.types"; +import { ContactsAddAddressEntryView } from "../ContactsAddAddressEntryView.web"; import { useContactsAddAddressEntryViewModel } from "./useContactsAddAddressEntryViewModel.web"; export function ContactsAddAddressEntry(props: ContactsAddAddressEntryWebProps): React.JSX.Element { diff --git a/features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.native.ts b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.native.ts similarity index 98% rename from features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.native.ts rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.native.ts index 9e1c32ffb266..fd46dfb4ed4b 100644 --- a/features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.native.ts +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.native.ts @@ -2,8 +2,8 @@ import { useCallback } from "react"; import type { ContactsAddAddressEntryProps, ContactsAddAddressEntryViewProps, -} from "./ContactsAddAddressEntry.types"; -import type { AddAddressEntryLabels, AddAddressEntryState } from "./types"; +} from "../ContactsAddAddressEntry.types"; +import type { AddAddressEntryLabels, AddAddressEntryState } from "../../Flow/types"; type AddressInputPresentation = Readonly<{ status?: "error" | "success"; diff --git a/features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.test.ts b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.web.test.ts similarity index 96% rename from features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.test.ts rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.web.test.ts index fd0107b502b0..ab2db6e8c391 100644 --- a/features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.test.ts +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.web.test.ts @@ -4,9 +4,9 @@ import { INVALID_CONTACT_ADDRESS_LABEL_ERROR_NAME, ContactAddressValueSchema, } from "@domain/entity-contact"; -import type { ContactsAddAddressNameLabels } from "./AddressName/types"; -import type { ContactsAddAddressEntryWebProps } from "./ContactsAddAddressEntry.web.types"; -import type { AddAddressEntryLabels } from "./types"; +import type { ContactsAddAddressNameLabels } from "../../AddressName/types"; +import type { ContactsAddAddressEntryWebProps } from "../ContactsAddAddressEntry.web.types"; +import type { AddAddressEntryLabels } from "../../Flow/types"; import { useContactsAddAddressEntryViewModel } from "./useContactsAddAddressEntryViewModel.web"; const labels: AddAddressEntryLabels = { diff --git a/features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.ts b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.web.ts similarity index 98% rename from features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.ts rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.web.ts index 099a07e01fa8..82a917a75ed2 100644 --- a/features/flow/contacts/src/steps/AddAddress/useContactsAddAddressEntryViewModel.web.ts +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Entry/useContactsAddAddressEntryViewModel.web.ts @@ -3,7 +3,7 @@ import type { AddressLabelConfiguration, ContactsAddAddressEntryWebProps, ContactsAddAddressEntryWebViewProps, -} from "./ContactsAddAddressEntry.web.types"; +} from "../ContactsAddAddressEntry.web.types"; function getAddressLabelConfiguration({ addressLabel, diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressPlaceholderView.native.test.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Placeholder/ContactsAddAddressPlaceholderView.native.test.tsx similarity index 100% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressPlaceholderView.native.test.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Placeholder/ContactsAddAddressPlaceholderView.native.test.tsx diff --git a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressPlaceholderView.native.tsx b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Placeholder/ContactsAddAddressPlaceholderView.native.tsx similarity index 92% rename from features/flow/contacts/src/steps/AddAddress/ContactsAddAddressPlaceholderView.native.tsx rename to features/flow/contacts/src/steps/AddAddress/AddressEntry/Placeholder/ContactsAddAddressPlaceholderView.native.tsx index 732569d76f75..e31ea3cd2a7a 100644 --- a/features/flow/contacts/src/steps/AddAddress/ContactsAddAddressPlaceholderView.native.tsx +++ b/features/flow/contacts/src/steps/AddAddress/AddressEntry/Placeholder/ContactsAddAddressPlaceholderView.native.tsx @@ -1,6 +1,6 @@ import React from "react"; import { BottomSheetHeader, BottomSheetView, Box, Button, Text } from "@ledgerhq/lumen-ui-rnative"; -import type { AddAddressPlaceholderViewProps } from "./types"; +import type { AddAddressPlaceholderViewProps } from "../../Flow/types"; export function ContactsAddAddressPlaceholderView({ title, diff --git a/features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressName.native.test.tsx b/features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressName.native.test.tsx index bae95c709591..6c60c70e86bb 100644 --- a/features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressName.native.test.tsx +++ b/features/flow/contacts/src/steps/AddAddress/AddressName/ContactsAddAddressName.native.test.tsx @@ -7,7 +7,7 @@ import { DUPLICATE_CONTACT_ADDRESS_LABEL_ERROR_NAME, INVALID_CONTACT_ADDRESS_LABEL_ERROR_NAME, } from "@domain/entity-contact"; -import type { AddAddressLabelState, AddAddressNameLabels } from "../types"; +import type { AddAddressLabelState, AddAddressNameLabels } from "../Flow/types"; import { ContactsAddAddressName } from "./ContactsAddAddressName.native"; const labels: AddAddressNameLabels = { diff --git a/features/flow/contacts/src/steps/AddAddress/AddressName/types.ts b/features/flow/contacts/src/steps/AddAddress/AddressName/types.ts index 7d7ffe1979da..0874cef8d93c 100644 --- a/features/flow/contacts/src/steps/AddAddress/AddressName/types.ts +++ b/features/flow/contacts/src/steps/AddAddress/AddressName/types.ts @@ -4,7 +4,7 @@ import type { AddAddressLabelState, AddAddressNameLabels, ValidAddAddressEntryState, -} from "../types"; +} from "../Flow/types"; export type ContactsAddAddressNameLabels = Readonly<{ inputLabel: string; diff --git a/features/flow/contacts/src/steps/AddAddress/Completion/ContactsAddAddressCompletion.web.tsx b/features/flow/contacts/src/steps/AddAddress/Completion/ContactsAddAddressCompletion.web.tsx index 861422d7e95a..6f83ed09271a 100644 --- a/features/flow/contacts/src/steps/AddAddress/Completion/ContactsAddAddressCompletion.web.tsx +++ b/features/flow/contacts/src/steps/AddAddress/Completion/ContactsAddAddressCompletion.web.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Button } from "@ledgerhq/lumen-ui-react"; -import type { AddAddressPlaceholderViewProps } from "../types"; +import type { AddAddressPlaceholderViewProps } from "../Flow/types"; export function ContactsAddAddressCompletion({ title, diff --git a/features/flow/contacts/src/steps/AddAddress/useAddAddressCurrencySelectionViewModel.ts b/features/flow/contacts/src/steps/AddAddress/CurrencySelection/useAddAddressCurrencySelectionViewModel.ts similarity index 88% rename from features/flow/contacts/src/steps/AddAddress/useAddAddressCurrencySelectionViewModel.ts rename to features/flow/contacts/src/steps/AddAddress/CurrencySelection/useAddAddressCurrencySelectionViewModel.ts index 77068c9ce2fc..2725237d0b6f 100644 --- a/features/flow/contacts/src/steps/AddAddress/useAddAddressCurrencySelectionViewModel.ts +++ b/features/flow/contacts/src/steps/AddAddress/CurrencySelection/useAddAddressCurrencySelectionViewModel.ts @@ -1,8 +1,8 @@ import { useCallback, useMemo, useRef, useState } from "react"; -import { useContactsFeature, type ContactsFeaturePlatform } from "../../featureFlags"; -import type { ContactsCurrencySelectionPort } from "./model/ports"; -import { resolveEligibleAddressCurrencyIds } from "./model/resolveEligibleAddressCurrencyIds"; -import type { AddAddressCurrencySelection } from "./types"; +import { useContactsFeature, type ContactsFeaturePlatform } from "../../../featureFlags"; +import type { ContactsCurrencySelectionPort } from "../model/ports"; +import { resolveEligibleAddressCurrencyIds } from "../model/resolveEligibleAddressCurrencyIds"; +import type { AddAddressCurrencySelection } from "../Flow/types"; export type UseAddAddressCurrencySelectionViewModelOptions = Readonly<{ platform: ContactsFeaturePlatform; diff --git a/features/flow/contacts/src/steps/AddAddress/useAddAddressCurrencySelectionViewModel.web.test.tsx b/features/flow/contacts/src/steps/AddAddress/CurrencySelection/useAddAddressCurrencySelectionViewModel.web.test.tsx similarity index 98% rename from features/flow/contacts/src/steps/AddAddress/useAddAddressCurrencySelectionViewModel.web.test.tsx rename to features/flow/contacts/src/steps/AddAddress/CurrencySelection/useAddAddressCurrencySelectionViewModel.web.test.tsx index 82c4b4cb3bd5..8a1d286d3313 100644 --- a/features/flow/contacts/src/steps/AddAddress/useAddAddressCurrencySelectionViewModel.web.test.tsx +++ b/features/flow/contacts/src/steps/AddAddress/CurrencySelection/useAddAddressCurrencySelectionViewModel.web.test.tsx @@ -9,7 +9,7 @@ import { featureFlagsReducer, type Features, } from "@shared/feature-flags"; -import type { ContactsCurrencySelectionPort } from "./model/ports"; +import type { ContactsCurrencySelectionPort } from "../model/ports"; import { type AddAddressCurrencySelectionResult, useAddAddressCurrencySelectionViewModel, diff --git a/features/flow/contacts/src/steps/AddAddress/types.ts b/features/flow/contacts/src/steps/AddAddress/Flow/types.ts similarity index 100% rename from features/flow/contacts/src/steps/AddAddress/types.ts rename to features/flow/contacts/src/steps/AddAddress/Flow/types.ts diff --git a/features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts b/features/flow/contacts/src/steps/AddAddress/Flow/useAddAddressFlowViewModel.ts similarity index 99% rename from features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts rename to features/flow/contacts/src/steps/AddAddress/Flow/useAddAddressFlowViewModel.ts index 42f65af71868..9a9f474e3ae4 100644 --- a/features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.ts +++ b/features/flow/contacts/src/steps/AddAddress/Flow/useAddAddressFlowViewModel.ts @@ -7,7 +7,10 @@ import { type ContactAddressLabel, type ContactId, } from "@domain/entity-contact"; -import type { ContactsAddressValidationPort, ContactsAddressValidationResult } from "./model/ports"; +import type { + ContactsAddressValidationPort, + ContactsAddressValidationResult, +} from "../model/ports"; import type { AddAddressContact, AddAddressCurrencySelection, diff --git a/features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.web.test.ts b/features/flow/contacts/src/steps/AddAddress/Flow/useAddAddressFlowViewModel.web.test.ts similarity index 99% rename from features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.web.test.ts rename to features/flow/contacts/src/steps/AddAddress/Flow/useAddAddressFlowViewModel.web.test.ts index b42c038ad932..100bdccdea4d 100644 --- a/features/flow/contacts/src/steps/AddAddress/useAddAddressFlowViewModel.web.test.ts +++ b/features/flow/contacts/src/steps/AddAddress/Flow/useAddAddressFlowViewModel.web.test.ts @@ -7,7 +7,10 @@ import { } from "@domain/entity-contact"; import { mockContact, mockContactAddress, mockMeContact } from "@domain/entity-contact/schema.mock"; import { getCryptoCurrencyById } from "@domain/entity-currency-crypto"; -import type { ContactsAddressValidationPort, ContactsAddressValidationResult } from "./model/ports"; +import type { + ContactsAddressValidationPort, + ContactsAddressValidationResult, +} from "../model/ports"; import { useAddAddressFlowViewModel } from "./useAddAddressFlowViewModel"; const ETHEREUM_CURRENCY_ID = getCryptoCurrencyById("ethereum").id; diff --git a/features/flow/contacts/src/steps/AddAddress/index.ts b/features/flow/contacts/src/steps/AddAddress/index.ts index 1312b4e06a70..8af89d8b0f89 100644 --- a/features/flow/contacts/src/steps/AddAddress/index.ts +++ b/features/flow/contacts/src/steps/AddAddress/index.ts @@ -18,7 +18,7 @@ export { type AddAddressCurrencySelectionResult, type AddAddressCurrencySelectionViewModel, type UseAddAddressCurrencySelectionViewModelOptions, -} from "./useAddAddressCurrencySelectionViewModel"; +} from "./CurrencySelection/useAddAddressCurrencySelectionViewModel"; export type { AddAddressContact, AddAddressCurrencySelection, @@ -33,8 +33,8 @@ export type { AddAddressPlaceholderViewProps, ValidAddAddressEntryState, ValidAddAddressLabelState, -} from "./types"; +} from "./Flow/types"; export { useAddAddressFlowViewModel, type UseAddAddressFlowViewModelOptions, -} from "./useAddAddressFlowViewModel"; +} from "./Flow/useAddAddressFlowViewModel"; diff --git a/features/flow/contacts/src/steps/AddAddress/model/ports.ts b/features/flow/contacts/src/steps/AddAddress/model/ports.ts index 3eca363c1c00..908a7e0f4f19 100644 --- a/features/flow/contacts/src/steps/AddAddress/model/ports.ts +++ b/features/flow/contacts/src/steps/AddAddress/model/ports.ts @@ -1,5 +1,5 @@ import type { CryptoCurrency } from "@domain/entity-currency-crypto"; -import type { AddAddressCurrencySelection } from "../types"; +import type { AddAddressCurrencySelection } from "../Flow/types"; export type { ContactsAddressValidationPort, ContactsAddressValidationResult, diff --git a/features/flow/contacts/src/steps/AddAddress/native.ts b/features/flow/contacts/src/steps/AddAddress/native.ts index bb792893bbc1..df8a70526a6f 100644 --- a/features/flow/contacts/src/steps/AddAddress/native.ts +++ b/features/flow/contacts/src/steps/AddAddress/native.ts @@ -1,5 +1,5 @@ -export { ContactsAddAddressEntry } from "./ContactsAddAddressEntry.native"; -export type { ContactsAddAddressEntryProps } from "./ContactsAddAddressEntry.types"; +export { ContactsAddAddressEntry } from "./AddressEntry/Entry/ContactsAddAddressEntry.native"; +export type { ContactsAddAddressEntryProps } from "./AddressEntry/ContactsAddAddressEntry.types"; export { ContactsAddAddressName } from "./AddressName/ContactsAddAddressName.native"; export type { ContactsAddAddressNameNativeProps as ContactsAddAddressNameProps } from "./AddressName/types"; -export { ContactsAddAddressPlaceholderView } from "./ContactsAddAddressPlaceholderView.native"; +export { ContactsAddAddressPlaceholderView } from "./AddressEntry/Placeholder/ContactsAddAddressPlaceholderView.native"; diff --git a/features/flow/contacts/src/steps/AddAddress/web.ts b/features/flow/contacts/src/steps/AddAddress/web.ts index ec03c4e6ef37..ca8a858819fb 100644 --- a/features/flow/contacts/src/steps/AddAddress/web.ts +++ b/features/flow/contacts/src/steps/AddAddress/web.ts @@ -1,6 +1,6 @@ -export { ContactsAddAddressEntry } from "./ContactsAddAddressEntry.web"; -export type { ContactsAddAddressEntryWebProps as ContactsAddAddressEntryProps } from "./ContactsAddAddressEntry.web.types"; -export type { AddAddressEntryLabels as ContactsAddAddressEntryLabels } from "./types"; +export { ContactsAddAddressEntry } from "./AddressEntry/Entry/ContactsAddAddressEntry.web"; +export type { ContactsAddAddressEntryWebProps as ContactsAddAddressEntryProps } from "./AddressEntry/ContactsAddAddressEntry.web.types"; +export type { AddAddressEntryLabels as ContactsAddAddressEntryLabels } from "./Flow/types"; export { ContactsAddAddressNameInput as ContactsAddAddressName } from "./AddressName/Input/ContactsAddAddressNameInput.web"; export { ContactsAddAddressCompletion } from "./Completion/ContactsAddAddressCompletion.web"; export type {