diff --git a/Flipcash/Core/Navigation/AppRouter+Destination.swift b/Flipcash/Core/Navigation/AppRouter+Destination.swift index 6b1e7327..a40957f6 100644 --- a/Flipcash/Core/Navigation/AppRouter+Destination.swift +++ b/Flipcash/Core/Navigation/AppRouter+Destination.swift @@ -29,6 +29,9 @@ extension AppRouter { /// Wallet's Recent section. `transactionHistory` is the per-token slice. case activity case give(PublicKey) + /// Pushes the buy flow (`BuyAmountScreen`) onto the current stack instead + /// of presenting it as a sheet — the new-UI currency-info "Convert"/"Get". + case buyCurrency(PublicKey) /// Skips the picker step in the withdraw flow and lands the user on /// `WithdrawIntroScreen` with the currency pre-selected. Pushed from /// USDF Currency Info on the Wallet sheet. @@ -74,7 +77,7 @@ extension AppRouter { switch self { case .currencyInfo, .currencyInfoForDeposit, .discoverCurrencies, .currencyCreationSummary, .currencyCreationWizard, - .transactionHistory, .activity, .give, .withdrawCurrency, + .transactionHistory, .activity, .give, .buyCurrency, .withdrawCurrency, .usdcDepositEducation, .usdcDepositAddress: return .balance case .settingsMyAccount, .settingsAdvancedFeatures, .settingsAdvancedBetaFeatures, @@ -102,6 +105,7 @@ extension AppRouter { case .transactionHistory: "transactionHistory" case .activity: "activity" case .give: "give" + case .buyCurrency: "buyCurrency" case .withdrawCurrency: "withdrawCurrency" case .usdcDepositEducation: "usdcDepositEducation" case .usdcDepositAddress: "usdcDepositAddress" @@ -133,6 +137,7 @@ extension AppRouter { .currencyInfoForDeposit(let mint), .transactionHistory(let mint), .give(let mint), + .buyCurrency(let mint), .withdrawCurrency(let mint): return mint.base58 case .tipConversation(let conversationID), diff --git a/Flipcash/Core/Navigation/AppRouter+DestinationView.swift b/Flipcash/Core/Navigation/AppRouter+DestinationView.swift index b3c0feba..63fdb1ab 100644 --- a/Flipcash/Core/Navigation/AppRouter+DestinationView.swift +++ b/Flipcash/Core/Navigation/AppRouter+DestinationView.swift @@ -59,6 +59,9 @@ struct DestinationView: View { GiveScreen(mint: mint) .id(mint) + case .buyCurrency(let mint): + BuyAmountScreen(mint: mint) + // MARK: - Settings flow case .settingsMyAccount: diff --git a/Flipcash/Core/Navigation/AppRouter+NestedSheet.swift b/Flipcash/Core/Navigation/AppRouter+NestedSheet.swift index 1b48e669..276808d6 100644 --- a/Flipcash/Core/Navigation/AppRouter+NestedSheet.swift +++ b/Flipcash/Core/Navigation/AppRouter+NestedSheet.swift @@ -86,23 +86,17 @@ private struct BuySheetRoot: View { let mint: PublicKey - @Environment(SessionContainer.self) private var sessionContainer @Environment(AppRouter.self) private var router var body: some View { @Bindable var router = router NavigationStack(path: $router[.buy]) { - BuyAmountScreen( - mint: mint, - currencyName: sessionContainer.session.balance(for: mint)?.name ?? "this currency", - session: sessionContainer.session, - ratesController: sessionContainer.ratesController - ) - .id(mint) - .environment(\.dismissParentContainer, { router.dismissSheet() }) - // Registers the shared destinations, including the Add Money - // deposit steps, so a buy-shortfall deposit pushes onto this stack. - .appRouterDestinations() + BuyAmountScreen(mint: mint) + .environment(\.dismissParentContainer, { router.dismissSheet() }) + .environment(\.presentedAsSheetRoot, true) + // Registers the shared destinations, including the Add Money + // deposit steps, so a buy-shortfall deposit pushes onto this stack. + .appRouterDestinations() } } } diff --git a/Flipcash/Core/Screens/Main/Buy/BuyAmountScreen.swift b/Flipcash/Core/Screens/Main/Buy/BuyAmountScreen.swift index 3debae24..4a3bffff 100644 --- a/Flipcash/Core/Screens/Main/Buy/BuyAmountScreen.swift +++ b/Flipcash/Core/Screens/Main/Buy/BuyAmountScreen.swift @@ -9,13 +9,41 @@ import SwiftUI import FlipcashCore import FlipcashUI +/// Amount entry for buying a currency. The caller provides the surrounding +/// `NavigationStack` — the buy sheet wraps it, the pushed `.buyCurrency` +/// destination uses the active one. +/// +/// Thin environment-reading wrapper that hands the DI containers to +/// ``BuyAmountScreenContent``, whose `init` seeds the `@State` view model +/// synchronously. `.id(mint)` rebuilds the content (and its view model) when the +/// mint changes. struct BuyAmountScreen: View { + let mint: PublicKey + + @Environment(SessionContainer.self) private var sessionContainer + + var body: some View { + BuyAmountScreenContent( + mint: mint, + currencyName: sessionContainer.session.balance(for: mint)?.name ?? "this currency", + session: sessionContainer.session, + ratesController: sessionContainer.ratesController + ) + .id(mint) + } +} + +private struct BuyAmountScreenContent: View { + @State private var viewModel: BuyAmountViewModel @State private var isShowingCurrencySelection: Bool = false @Environment(AppRouter.self) private var router @Environment(RatesController.self) private var ratesController + /// True when this is the root of a presented sheet; false when pushed onto a + /// host stack (new-UI Convert/Get) where the system back arrow replaces Close. + @Environment(\.presentedAsSheetRoot) private var presentedAsSheetRoot init(mint: PublicKey, currencyName: String, session: Session, ratesController: RatesController) { self._viewModel = State(initialValue: BuyAmountViewModel( @@ -56,7 +84,9 @@ struct BuyAmountScreen: View { .navigationTitle(viewModel.screenTitle) .toolbarTitleDisplayMode(.inline) .toolbar { - if !isDismissBlocked { + // Only the sheet-root presentation gets a Close button; a pushed + // instance relies on the system back arrow. + if presentedAsSheetRoot && !isDismissBlocked { ToolbarItem(placement: .topBarTrailing) { CloseButton(action: router.dismissSheet) }