From 379af483de2ba5ecb9143a7481c66711cb847413 Mon Sep 17 00:00:00 2001 From: Gustaf Kugelberg <123396602+kugel3@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:14:22 +0200 Subject: [PATCH] ABW-3715 Disable Accounts with Insufficient Funds (#1291) --- .../DesignSystem/Components/RadioButton.swift | 26 +++--- .../Generated/AssetResource.generated.swift | 2 + .../Contents.json | 12 +++ .../radioButton-dark-disabled-unselected.pdf | Bin 0 -> 987 bytes .../Contents.json | 12 +++ .../radioButton-light-disabled-unselected.pdf | Bin 0 -> 1425 bytes .../CustomizeFees/CustomizeFees+View.swift | 2 +- .../CustomizeFees/CustomizeFees.swift | 10 ++- .../SelectFeePayer/SelectFeePayer+View.swift | 76 ++++++++++-------- .../SelectFeePayer/SelectFeePayer.swift | 32 ++++++-- .../TransactionReview.swift | 66 +++++++-------- .../CustomizeFeePayerTests.swift | 8 +- 12 files changed, 157 insertions(+), 89 deletions(-) create mode 100644 RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/Contents.json create mode 100644 RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/radioButton-dark-disabled-unselected.pdf create mode 100644 RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/Contents.json create mode 100644 RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/radioButton-light-disabled-unselected.pdf diff --git a/RadixWallet/Core/DesignSystem/Components/RadioButton.swift b/RadixWallet/Core/DesignSystem/Components/RadioButton.swift index 76b27fdcbb..e6646a5a86 100644 --- a/RadixWallet/Core/DesignSystem/Components/RadioButton.swift +++ b/RadixWallet/Core/DesignSystem/Components/RadioButton.swift @@ -3,7 +3,6 @@ public struct RadioButton: View { public enum State { case unselected case selected - case disabled } public enum Appearance { @@ -12,32 +11,39 @@ public struct RadioButton: View { } public let appearance: Appearance - public var state: State + public let state: State + public let isDisabled: Bool public init( appearance: Appearance, - state: State + state: State, + disabled: Bool = false ) { self.appearance = appearance self.state = state + self.isDisabled = disabled } } extension RadioButton { public var body: some View { - let resource: ImageAsset = switch (appearance, state) { - case (.light, .unselected): + let resource: ImageAsset = switch (appearance, state, isDisabled) { + case (.light, .unselected, false): AssetResource.radioButtonLightUnselected - case (.light, .selected): + case (.light, .selected, false): AssetResource.radioButtonLightSelected - case (.light, .disabled): + case (.light, .selected, true): AssetResource.radioButtonLightDisabled - case (.dark, .unselected): + case (.light, .unselected, true): + AssetResource.radioButtonLightDisabledUnselected + case (.dark, .unselected, false): AssetResource.radioButtonDarkUnselected - case (.dark, .selected): + case (.dark, .selected, false): AssetResource.radioButtonDarkSelected - case (.dark, .disabled): + case (.dark, .selected, true): AssetResource.radioButtonDarkDisabled + case (.dark, .unselected, true): + AssetResource.radioButtonDarkDisabledUnselected } return Image(asset: resource) diff --git a/RadixWallet/Core/Resources/Generated/AssetResource.generated.swift b/RadixWallet/Core/Resources/Generated/AssetResource.generated.swift index 4867bb4fd2..dee29ddd95 100644 --- a/RadixWallet/Core/Resources/Generated/AssetResource.generated.swift +++ b/RadixWallet/Core/Resources/Generated/AssetResource.generated.swift @@ -82,9 +82,11 @@ public enum AssetResource { public static let lock = ImageAsset(name: "lock") public static let minusCircle = ImageAsset(name: "minus-circle") public static let plusCircle = ImageAsset(name: "plus-circle") + public static let radioButtonDarkDisabledUnselected = ImageAsset(name: "radioButton-dark-disabled-unselected") public static let radioButtonDarkDisabled = ImageAsset(name: "radioButton-dark-disabled") public static let radioButtonDarkSelected = ImageAsset(name: "radioButton-dark-selected") public static let radioButtonDarkUnselected = ImageAsset(name: "radioButton-dark-unselected") + public static let radioButtonLightDisabledUnselected = ImageAsset(name: "radioButton-light-disabled-unselected") public static let radioButtonLightDisabled = ImageAsset(name: "radioButton-light-disabled") public static let radioButtonLightSelected = ImageAsset(name: "radioButton-light-selected") public static let radioButtonLightUnselected = ImageAsset(name: "radioButton-light-unselected") diff --git a/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/Contents.json b/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/Contents.json new file mode 100644 index 0000000000..91b66b4677 --- /dev/null +++ b/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "radioButton-dark-disabled-unselected.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/radioButton-dark-disabled-unselected.pdf b/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-dark-disabled-unselected.imageset/radioButton-dark-disabled-unselected.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bcebabc25bf5ce16d0cf6f1101f65103003aa68f GIT binary patch literal 987 zcmY!laBB55sH4d%$$Y6sDHAz>0RC@Gj zG@U;E^8NC8cK7C8`K+${DctQBgcJ%+ z(#UdIZDP#c!L2gm!%^9Xj1C^36t`-2^E^;=k)5lZ7{STTW%VRyvAl}mN|P1l)0s|P zmb!Rr*1KnU0*rT;@(5+zxH8*4`ikrw9r+7w1$8&d-^$m_I=BrK!q6asg*7PfAaP}6 zVE~T-6SycS1|9SA@=J;pqGO@q9#UD5s-Pc`m=22w-_(@MM5p`;g=k=`f+N)c!7ws` z2_aZ8lY&x<^Gl18Q;QWqX$BODpm=r8&nrpI1KJ2m2r$8b#3G=Wf+ZY+xptp_l~nU@_c6ki#GzcTOxx%*jtj)ml-M zn#N_IV9o{cJ~&~UnVK4#DgcF{zz`TJV2M1qkU7wus6r;j78pWi7#hutff0wQ(cHis zE>u#Kn3j!66r2<_K&O?bs RC1CHEo0)N`s=E5S0RZ4pDZ>B& literal 0 HcmV?d00001 diff --git a/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/Contents.json b/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/Contents.json new file mode 100644 index 0000000000..ea73614789 --- /dev/null +++ b/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "radioButton-light-disabled-unselected.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/radioButton-light-disabled-unselected.pdf b/RadixWallet/Core/Resources/Resources/Assets.xcassets/Common/radioButton-light-disabled-unselected.imageset/radioButton-light-disabled-unselected.pdf new file mode 100644 index 0000000000000000000000000000000000000000..87fce75d4006b87fea91aefe9e69e38131412a5d GIT binary patch literal 1425 zcmY!laBB55sH4d%$$U3Y>UukCV@pj1xW`Qyxhx7*p4)g4tJLlYV(BeB` zDpz}2c3qhEsq^#W|Nl9+{{FtYJvG1H%`2Vy{@t^A`+uB1zx#9S{=a&)!u}yWyzCR# zuX{f&)9-V`nbdd5Qr#V&CH_90cYm&b?%PX8t{&L<`mhn-$4BfbUoSsvW~lR0INA65 zq(+~jR7yza{vR`Icf?&aaMZ9)?T{)JTIyT2|IX~{a{Y`iZ#T-dH&-k;?k@eM;>e`E z8`x~QXIH#;F*>N*eah7<@L5M{d77W1xYDeI$7(Mf4^FryJk^dncmZdydFuo>W0A(J zH^(IYnnetN)3G9#WJxiJ3bAdZ>m&Z(v;v<_@Kg*C(^q$Zy;JZnAjoix4if!{&d~uq= zHAzc-btLZ9PY*#2!tN=57gTDW|ieGSC5M^7wVL>a_*#jm6+WS$(?EI znt7<-NWgq=_?&6S`{#x5%_;h#63@3#sZI4qrF~V!i%g}9sX;zfp9~YC3=Z41X5>$l zbg^LM&{;u@Y}UuOxRquen6R~D-HV=eTbQOE&eadTvSPDF?S};`T*JL9<%86` zRNH>7+?u{}zU}3v)s1UJq_3(UC`s+O*}LWMFZ<^+x)gOOZ5} zlX>ijz)7}gYurj!&kc`iycMdkGSIYZ{T7MZ4=Mgy2j+Bta$Djv#WHEd{E+IruiLqG z9e(<*y7Y_ZLqF5ysfualm%eG+nY6!qrT9XRXECqNY>_)&&qc+jc;=Uyxdl2)EOvV` z;qJ|As~7HJ<~lk>v2Bwlr}xd@^=tIs=|A4i=^s-Pc`m=4Q5zNsmhiB9- zoL^d$oLZ~^%IKh+2g;1j`FSO&c|aRMxfdoFkXQs1Q!s@J0dpwK3m_px8R4Co0(7bZ z$Ol0RFngTyOM!X~G2CAa$?Gta%uq}Md9WC6A;@76k2@!pBp}-IrDqx8`xR5!}ov1<<=0N|T30az>2^kugqnl-DXo4`Sq$n{n zC$)$R6nma7z~Im*&d*KNRM5yw(S!tweo%gXi2}&O-~iSS&a6rWx*lBcB^H%{y<=!( MV9cee>gw+X0N>3AF8}}l literal 0 HcmV?d00001 diff --git a/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees+View.swift b/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees+View.swift index 3f2096a65a..8b8d252147 100644 --- a/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees+View.swift @@ -58,7 +58,7 @@ extension CustomizeFees { public struct ViewState: Equatable { let mode: TransactionFee.Mode let feePayer: Account? - let feePayingValidation: FeeValidationOutcome? + let feePayingValidation: FeePayerValidationOutcome? } @MainActor diff --git a/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift b/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift index 302bbe0cf5..7c4cc1e915 100644 --- a/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift +++ b/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift @@ -27,7 +27,7 @@ public struct CustomizeFees: FeatureReducer, Sendable { reviewedTransaction.transactionFee } - var feePayingValidation: FeeValidationOutcome? { + var feePayingValidation: FeePayerValidationOutcome? { reviewedTransaction.feePayingValidation.wrappedValue } @@ -107,7 +107,13 @@ public struct CustomizeFees: FeatureReducer, Sendable { public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .changeFeePayerTapped: - state.destination = .selectFeePayer(.init(feePayer: state.feePayer, transactionFee: state.transactionFee)) + state.destination = .selectFeePayer( + .init( + reviewedTransaction: state.reviewedTransaction, + selectedFeePayer: state.feePayer, + transactionFee: state.transactionFee + ) + ) return .none case .toggleMode: state.reviewedTransaction.transactionFee.toggleMode() diff --git a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift index 5175d65774..36a56bf0a9 100644 --- a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift @@ -2,34 +2,24 @@ import ComposableArchitecture import SwiftUI extension SelectFeePayer.State { - var viewState: SelectFeePayer.ViewState { - .init( - feePayerCandidates: feePayerCandidates.rawValue, - fee: transactionFee.totalFee.displayedTotalFee, - selectedPayer: feePayer - ) + var feeString: String { + transactionFee.totalFee.displayedTotalFee } -} -// MARK: - SelectFeePayer.View -extension SelectFeePayer { - public struct ViewState: Equatable { - let feePayerCandidates: Loadable> - let fee: String - let selectedPayer: FeePayerCandidate? - - var selectButtonControlState: ControlState { - switch feePayerCandidates { - case .idle, .loading: - .loading(.local) - case .success: - .enabled - case .failure: - .disabled - } + var selectButtonControlState: ControlState { + switch feePayerCandidates { + case .idle, .loading: + .loading(.local) + case .success: + .enabled + case .failure: + .disabled } } +} +// MARK: - SelectFeePayer.View +extension SelectFeePayer { @MainActor public struct View: SwiftUI.View { private let store: StoreOf @@ -39,7 +29,7 @@ extension SelectFeePayer { } public var body: some SwiftUI.View { - WithViewStore(store, observe: \.viewState, send: { .view($0) }) { viewStore in + WithViewStore(store, observe: { $0 }, send: { .view($0) }) { viewStore in VStack { Text(L10n.CustomizeNetworkFees.SelectFeePayer.navigationTitle) .multilineTextAlignment(.center) @@ -48,7 +38,7 @@ extension SelectFeePayer { .padding(.horizontal, .medium1) .padding(.bottom, .small2) - Text(L10n.CustomizeNetworkFees.SelectFeePayer.subtitle(viewStore.fee)) + Text(L10n.CustomizeNetworkFees.SelectFeePayer.subtitle(viewStore.feeString)) .multilineTextAlignment(.center) .textStyle(.body1HighImportance) .foregroundColor(.app.gray2) @@ -63,8 +53,8 @@ extension SelectFeePayer { VStack(spacing: .small1) { Selection( viewStore.binding( - get: \.selectedPayer, - send: { .selectedPayer($0) } + get: \.selectedFeePayer, + send: { .selectedFeePayer($0) } ), from: candidates ) { item in @@ -79,7 +69,7 @@ extension SelectFeePayer { .padding(.horizontal, .medium1) .padding(.bottom, .medium2) .onFirstAppear { - proxy.scrollTo(viewStore.selectedPayer?.id, anchor: .center) + proxy.scrollTo(viewStore.selectedFeePayer?.id, anchor: .center) } } } @@ -93,8 +83,8 @@ extension SelectFeePayer { } .footer { WithControlRequirements( - viewStore.selectedPayer, - forAction: { viewStore.send(.confirmedFeePayer($0)) } + viewStore.selectedFeePayer, + forAction: { viewStore.send(.confirmedFeePayer($0.candidate)) } ) { action in Button(L10n.CustomizeNetworkFees.SelectFeePayer.selectAccountButtonTitle, action: action) .buttonStyle(.primaryRectangular) @@ -114,10 +104,12 @@ enum SelectAccountToPayForFeeRow { struct ViewState: Equatable { let account: Account let fungible: ResourceBalance.ViewState.Fungible + let isBalanceInsufficient: Bool - init(candidate: FeePayerCandidate) { - self.account = candidate.account - self.fungible = .xrd(balance: .init(nominalAmount: candidate.xrdBalance), network: account.networkID) + init(candidate: ValidatedFeePayerCandidate) { + self.account = candidate.candidate.account + self.fungible = .xrd(balance: .init(nominalAmount: candidate.candidate.xrdBalance), network: account.networkID) + self.isBalanceInsufficient = candidate.outcome == .insufficientBalance } } @@ -127,22 +119,36 @@ enum SelectAccountToPayForFeeRow { let isSelected: Bool let action: () -> Void + var buttonState: RadioButton.State { + isSelected ? .selected : .unselected + } + + var isDisabled: Bool { + viewState.isBalanceInsufficient + } + var body: some SwiftUI.View { Button(action: action) { Card { - VStack(spacing: .zero) { + VStack(alignment: .leading, spacing: .zero) { AccountCard(kind: .innerCompact, account: viewState.account) HStack { ResourceBalanceView(.fungible(viewState.fungible), appearance: .compact) - RadioButton(appearance: .dark, state: isSelected ? .selected : .unselected) + RadioButton(appearance: .dark, state: buttonState, disabled: isDisabled) } .padding(.medium3) + + if viewState.isBalanceInsufficient { + WarningErrorView(text: L10n.TransactionReview.FeePayerValidation.insufficientBalance, type: .error) + .padding([.horizontal, .bottom], .medium3) + } } } } .buttonStyle(.inert) + .disabled(isDisabled) } } } diff --git a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift index d09a9bb96d..299a75a9a9 100644 --- a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift +++ b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift @@ -1,27 +1,37 @@ import ComposableArchitecture import SwiftUI +// MARK: - ValidatedFeePayerCandidate +public struct ValidatedFeePayerCandidate: Sendable, Hashable, Identifiable { + public var id: FeePayerCandidate.ID { candidate.id } + public let candidate: FeePayerCandidate + public let outcome: FeePayerValidationOutcome +} + // MARK: - SelectFeePayer public struct SelectFeePayer: Sendable, FeatureReducer { public typealias FeePayerCandidates = NonEmpty> public struct State: Sendable, Hashable { - public var feePayer: FeePayerCandidate? + public let reviewedTransaction: ReviewedTransaction + public var selectedFeePayer: ValidatedFeePayerCandidate? public let transactionFee: TransactionFee - public var feePayerCandidates: Loadable = .idle + public var feePayerCandidates: Loadable<[ValidatedFeePayerCandidate]> = .idle public init( - feePayer: FeePayerCandidate?, + reviewedTransaction: ReviewedTransaction, + selectedFeePayer: FeePayerCandidate?, transactionFee: TransactionFee ) { - self.feePayer = feePayer + self.reviewedTransaction = reviewedTransaction + self.selectedFeePayer = selectedFeePayer.map { .init(candidate: $0, outcome: reviewedTransaction.validateFeePayer($0)) } self.transactionFee = transactionFee } } public enum ViewAction: Sendable, Equatable { case task - case selectedPayer(FeePayerCandidate?) + case selectedFeePayer(ValidatedFeePayerCandidate?) case confirmedFeePayer(FeePayerCandidate) case pullToRefreshStarted case closeButtonTapped @@ -47,8 +57,8 @@ public struct SelectFeePayer: Sendable, FeatureReducer { state.feePayerCandidates = .loading return loadCandidates(refresh: false) - case let .selectedPayer(candidate): - state.feePayer = candidate + case let .selectedFeePayer(candidate): + state.selectedFeePayer = candidate return .none case let .confirmedFeePayer(payer): @@ -67,7 +77,13 @@ public struct SelectFeePayer: Sendable, FeatureReducer { public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { switch internalAction { case let .feePayerCandidatesLoaded(.success(candidates)): - state.feePayerCandidates = .success(candidates) + let validated = candidates.rawValue.map { candidate in + ValidatedFeePayerCandidate( + candidate: candidate, + outcome: state.reviewedTransaction.validateFeePayer(candidate) + ) + } + state.feePayerCandidates = .success(validated) return .none case let .feePayerCandidatesLoaded(.failure(error)): errorQueue.schedule(error) diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift index 9fbc803be6..f4d66b5b6d 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift @@ -972,18 +972,18 @@ public struct ReviewedTransaction: Hashable, Sendable { let isNonConforming: Bool } -// MARK: - FeeValidationOutcome -enum FeeValidationOutcome: Equatable { - case valid(Details?) +// MARK: - FeePayerValidationOutcome +public enum FeePayerValidationOutcome: Sendable, Hashable { case needsFeePayer case insufficientBalance + case valid(Details?) - enum Details { + public enum Details: Sendable { case introducesNewAccount case feePayerSuperfluous } - var isValid: Bool { + public var isValid: Bool { guard case .valid = self else { return false } return true } @@ -994,39 +994,41 @@ extension ReviewedTransaction { Set(accountWithdraws.keys).union(accountDeposits.keys) } - var feePayingValidation: Loadable { - feePayer.map { selected in - guard let selected else { - if transactionFee.totalFee.lockFee == .zero { - // No fee is required - no fee payer needed - return .valid(.feePayerSuperfluous) - } else { - // Fee is required, but no fee payer selected - invalid - return .needsFeePayer - } + var feePayingValidation: Loadable { + feePayer.map(validateFeePayer) + } + + func validateFeePayer(_ candidate: FeePayerCandidate?) -> FeePayerValidationOutcome { + guard let candidate else { + if transactionFee.totalFee.lockFee == .zero { + // No fee is required - no fee payer needed + return .valid(.feePayerSuperfluous) + } else { + // Fee is required, but no fee payer selected - invalid + return .needsFeePayer } + } - let xrdAddress: ResourceAddress = .xrd(on: networkID) - let feePayerWithdraws = accountWithdraws[selected.account.address] ?? [] - let xrdTransfer: Decimal192 = feePayerWithdraws.reduce(.zero) { partialResult, resource in - if case let .fungible(resourceAddress, indicator) = resource, resourceAddress == xrdAddress { - return partialResult + indicator.amount - } - return partialResult + let xrdAddress: ResourceAddress = .xrd(on: networkID) + let feePayerWithdraws = accountWithdraws[candidate.account.address] ?? [] + let xrdTransfer: Decimal192 = feePayerWithdraws.reduce(.zero) { partialResult, resource in + if case let .fungible(resourceAddress, indicator) = resource, resourceAddress == xrdAddress { + return partialResult + indicator.amount } + return partialResult + } - let totalAmountNeeded = xrdTransfer + transactionFee.totalFee.lockFee + let totalAmountNeeded = xrdTransfer + transactionFee.totalFee.lockFee - guard selected.xrdBalance >= totalAmountNeeded else { - // Insufficient balance to pay for withdraws and transaction fee - return .insufficientBalance - } + guard candidate.xrdBalance >= totalAmountNeeded else { + // Insufficient balance to pay for withdraws and transaction fee + return .insufficientBalance + } - if !involvedAccounts.contains(selected.account.address) { - return .valid(.introducesNewAccount) - } else { - return .valid(nil) - } + if !involvedAccounts.contains(candidate.account.address) { + return .valid(.introducesNewAccount) + } else { + return .valid(nil) } } } diff --git a/RadixWalletTests/Features/TransactionReviewFeatureTests/CustomizeFeePayerTests.swift b/RadixWalletTests/Features/TransactionReviewFeatureTests/CustomizeFeePayerTests.swift index bad1642af8..dcf8b2f54c 100644 --- a/RadixWalletTests/Features/TransactionReviewFeatureTests/CustomizeFeePayerTests.swift +++ b/RadixWalletTests/Features/TransactionReviewFeatureTests/CustomizeFeePayerTests.swift @@ -46,7 +46,13 @@ final class CustomizeFeePayerTests: TestCase { let selectedFeePayer = FeePayerCandidate(account: .previewValue1, xrdBalance: 20) await sut.send(.view(.changeFeePayerTapped)) { - $0.destination = .selectFeePayer(.init(feePayer: nil, transactionFee: .nonContingentLockPaying)) + $0.destination = .selectFeePayer( + .init( + reviewedTransaction: transactionStub, + selectedFeePayer: nil, + transactionFee: .nonContingentLockPaying + ) + ) } await sut.send(.destination(.presented(.selectFeePayer(.delegate(.selected(selectedFeePayer)))))) { $0.destination = nil