From 86cdcbeaff58a4ad877c3716767082cf7e3540fa Mon Sep 17 00:00:00 2001 From: kugel3 Date: Tue, 20 Aug 2024 14:05:15 +0200 Subject: [PATCH 1/6] Preparing --- .../CustomizeFees/CustomizeFees+View.swift | 2 +- .../CustomizeFees/CustomizeFees.swift | 2 +- .../SelectFeePayer/SelectFeePayer.swift | 5 ++ .../TransactionReview.swift | 64 ++++++++++--------- 4 files changed, 40 insertions(+), 33 deletions(-) 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..0c8007395b 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 } diff --git a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift index d09a9bb96d..380f1dec0f 100644 --- a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift +++ b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift @@ -5,6 +5,11 @@ import SwiftUI public struct SelectFeePayer: Sendable, FeatureReducer { public typealias FeePayerCandidates = NonEmpty> + public struct ValidatedFeePayerCandidate { + public let candidate: FeePayerCandidate + public let outcome: FeePayerValidationOutcome + } + public struct State: Sendable, Hashable { public var feePayer: FeePayerCandidate? public let transactionFee: TransactionFee diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift index 9fbc803be6..5b4aec89b3 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 { +// MARK: - FeePayerValidationOutcome +public enum FeePayerValidationOutcome: Equatable { case valid(Details?) case needsFeePayer case insufficientBalance - enum Details { + public enum Details { 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) } } } From 27e6725526c26b1284a0fbeed5302b91edab45f3 Mon Sep 17 00:00:00 2001 From: kugel3 Date: Tue, 20 Aug 2024 14:08:35 +0200 Subject: [PATCH 2/6] wip --- .../SelectFeePayer/SelectFeePayer.swift | 3 ++- .../Features/TransactionReviewFeature/TransactionReview.swift | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift index 380f1dec0f..f4a0536315 100644 --- a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift +++ b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift @@ -5,7 +5,8 @@ import SwiftUI public struct SelectFeePayer: Sendable, FeatureReducer { public typealias FeePayerCandidates = NonEmpty> - public struct ValidatedFeePayerCandidate { + public struct ValidatedFeePayerCandidate: Sendable, Hashable, Identifiable { + public var id: FeePayerCandidate.ID { candidate.id } public let candidate: FeePayerCandidate public let outcome: FeePayerValidationOutcome } diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift index 5b4aec89b3..dc43ea6dfb 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift @@ -973,12 +973,12 @@ public struct ReviewedTransaction: Hashable, Sendable { } // MARK: - FeePayerValidationOutcome -public enum FeePayerValidationOutcome: Equatable { +public enum FeePayerValidationOutcome: Sendable, Hashable { case valid(Details?) case needsFeePayer case insufficientBalance - public enum Details { + public enum Details: Sendable { case introducesNewAccount case feePayerSuperfluous } From 3f397806d1e9f538253675ba823e341497f620b8 Mon Sep 17 00:00:00 2001 From: kugel3 Date: Tue, 20 Aug 2024 22:24:17 +0200 Subject: [PATCH 3/6] done --- .../CustomizeFees/CustomizeFees.swift | 8 +- .../SelectFeePayer/SelectFeePayer+View.swift | 87 ++++++++++--------- .../SelectFeePayer/SelectFeePayer.swift | 38 +++++--- .../TransactionReview.swift | 2 +- 4 files changed, 77 insertions(+), 58 deletions(-) diff --git a/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift b/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift index 0c8007395b..7c4cc1e915 100644 --- a/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift +++ b/RadixWallet/Features/TransactionReviewFeature/CustomizeFees/CustomizeFees.swift @@ -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..34e48b2026 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) @@ -112,12 +102,14 @@ extension SelectFeePayer { // MARK: - SelectAccountToPayForFeeRow enum SelectAccountToPayForFeeRow { struct ViewState: Equatable { + let insufficientBalance: Bool let account: Account let fungible: ResourceBalance.ViewState.Fungible - init(candidate: FeePayerCandidate) { - self.account = candidate.account - self.fungible = .xrd(balance: .init(nominalAmount: candidate.xrdBalance), network: account.networkID) + init(candidate: ValidatedFeePayerCandidate) { + self.insufficientBalance = candidate.outcome == .insufficientBalance + self.account = candidate.candidate.account + self.fungible = .xrd(balance: .init(nominalAmount: candidate.candidate.xrdBalance), network: account.networkID) } } @@ -127,22 +119,33 @@ enum SelectAccountToPayForFeeRow { let isSelected: Bool let action: () -> Void + var buttonState: RadioButton.State { + viewState.insufficientBalance ? .disabled : (isSelected ? .selected : .unselected) + } + var body: some SwiftUI.View { - Button(action: action) { - Card { - VStack(spacing: .zero) { - AccountCard(kind: .innerCompact, account: viewState.account) + VStack(alignment: .leading) { + Button(action: action) { + Card { + VStack(spacing: .zero) { + AccountCard(kind: .innerCompact, account: viewState.account) - HStack { - ResourceBalanceView(.fungible(viewState.fungible), appearance: .compact) + HStack { + ResourceBalanceView(.fungible(viewState.fungible), appearance: .compact) - RadioButton(appearance: .dark, state: isSelected ? .selected : .unselected) + RadioButton(appearance: .dark, state: buttonState) + } + .padding(.medium3) } - .padding(.medium3) } } + .buttonStyle(.inert) + .disabled(buttonState == .disabled) + + if viewState.insufficientBalance { + WarningErrorView(text: L10n.TransactionReview.FeePayerValidation.insufficientBalance, type: .error) + } } - .buttonStyle(.inert) } } } diff --git a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift index f4a0536315..299a75a9a9 100644 --- a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift +++ b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer.swift @@ -1,33 +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 ValidatedFeePayerCandidate: Sendable, Hashable, Identifiable { - public var id: FeePayerCandidate.ID { candidate.id } - public let candidate: FeePayerCandidate - public let outcome: FeePayerValidationOutcome - } - 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 @@ -53,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): @@ -73,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 dc43ea6dfb..f4d66b5b6d 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift @@ -974,9 +974,9 @@ public struct ReviewedTransaction: Hashable, Sendable { // MARK: - FeePayerValidationOutcome public enum FeePayerValidationOutcome: Sendable, Hashable { - case valid(Details?) case needsFeePayer case insufficientBalance + case valid(Details?) public enum Details: Sendable { case introducesNewAccount From 17637f57c44d0e6b537f40235a29815a3f87524b Mon Sep 17 00:00:00 2001 From: kugel3 Date: Wed, 21 Aug 2024 11:20:19 +0200 Subject: [PATCH 4/6] fix test --- .../CustomizeFeePayerTests.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 From 0f214b2271ac05433b25da07fb9f8e260230af19 Mon Sep 17 00:00:00 2001 From: kugel3 Date: Wed, 21 Aug 2024 14:44:10 +0200 Subject: [PATCH 5/6] New radiobutton assets --- .../DesignSystem/Components/RadioButton.swift | 11 ++++++++--- .../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 6 files changed, 34 insertions(+), 3 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..b581b48dd2 100644 --- a/RadixWallet/Core/DesignSystem/Components/RadioButton.swift +++ b/RadixWallet/Core/DesignSystem/Components/RadioButton.swift @@ -3,7 +3,8 @@ public struct RadioButton: View { public enum State { case unselected case selected - case disabled + case disabledSelected + case disabledUnselected } public enum Appearance { @@ -30,14 +31,18 @@ extension RadioButton { AssetResource.radioButtonLightUnselected case (.light, .selected): AssetResource.radioButtonLightSelected - case (.light, .disabled): + case (.light, .disabledSelected): AssetResource.radioButtonLightDisabled + case (.light, .disabledUnselected): + AssetResource.radioButtonLightDisabledUnselected case (.dark, .unselected): AssetResource.radioButtonDarkUnselected case (.dark, .selected): AssetResource.radioButtonDarkSelected - case (.dark, .disabled): + case (.dark, .disabledSelected): AssetResource.radioButtonDarkDisabled + case (.dark, .disabledUnselected): + 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 From 84b34e97ef73e5e91d9c3ac434e831f660f2ad6e Mon Sep 17 00:00:00 2001 From: kugel3 Date: Wed, 21 Aug 2024 15:27:19 +0200 Subject: [PATCH 6/6] Update --- .../DesignSystem/Components/RadioButton.swift | 27 ++++++------ .../SelectFeePayer/SelectFeePayer+View.swift | 41 ++++++++++--------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/RadixWallet/Core/DesignSystem/Components/RadioButton.swift b/RadixWallet/Core/DesignSystem/Components/RadioButton.swift index b581b48dd2..e6646a5a86 100644 --- a/RadixWallet/Core/DesignSystem/Components/RadioButton.swift +++ b/RadixWallet/Core/DesignSystem/Components/RadioButton.swift @@ -3,8 +3,6 @@ public struct RadioButton: View { public enum State { case unselected case selected - case disabledSelected - case disabledUnselected } public enum Appearance { @@ -13,35 +11,38 @@ 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, .disabledSelected): + case (.light, .selected, true): AssetResource.radioButtonLightDisabled - case (.light, .disabledUnselected): + case (.light, .unselected, true): AssetResource.radioButtonLightDisabledUnselected - case (.dark, .unselected): + case (.dark, .unselected, false): AssetResource.radioButtonDarkUnselected - case (.dark, .selected): + case (.dark, .selected, false): AssetResource.radioButtonDarkSelected - case (.dark, .disabledSelected): + case (.dark, .selected, true): AssetResource.radioButtonDarkDisabled - case (.dark, .disabledUnselected): + case (.dark, .unselected, true): AssetResource.radioButtonDarkDisabledUnselected } diff --git a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift index 34e48b2026..36a56bf0a9 100644 --- a/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/SelectFeePayer/SelectFeePayer+View.swift @@ -102,14 +102,14 @@ extension SelectFeePayer { // MARK: - SelectAccountToPayForFeeRow enum SelectAccountToPayForFeeRow { struct ViewState: Equatable { - let insufficientBalance: Bool let account: Account let fungible: ResourceBalance.ViewState.Fungible + let isBalanceInsufficient: Bool init(candidate: ValidatedFeePayerCandidate) { - self.insufficientBalance = candidate.outcome == .insufficientBalance self.account = candidate.candidate.account self.fungible = .xrd(balance: .init(nominalAmount: candidate.candidate.xrdBalance), network: account.networkID) + self.isBalanceInsufficient = candidate.outcome == .insufficientBalance } } @@ -120,32 +120,35 @@ enum SelectAccountToPayForFeeRow { let action: () -> Void var buttonState: RadioButton.State { - viewState.insufficientBalance ? .disabled : (isSelected ? .selected : .unselected) + isSelected ? .selected : .unselected + } + + var isDisabled: Bool { + viewState.isBalanceInsufficient } var body: some SwiftUI.View { - VStack(alignment: .leading) { - Button(action: action) { - Card { - VStack(spacing: .zero) { - AccountCard(kind: .innerCompact, account: viewState.account) + Button(action: action) { + Card { + VStack(alignment: .leading, spacing: .zero) { + AccountCard(kind: .innerCompact, account: viewState.account) - HStack { - ResourceBalanceView(.fungible(viewState.fungible), appearance: .compact) + HStack { + ResourceBalanceView(.fungible(viewState.fungible), appearance: .compact) - RadioButton(appearance: .dark, state: buttonState) - } - .padding(.medium3) + RadioButton(appearance: .dark, state: buttonState, disabled: isDisabled) } - } - } - .buttonStyle(.inert) - .disabled(buttonState == .disabled) + .padding(.medium3) - if viewState.insufficientBalance { - WarningErrorView(text: L10n.TransactionReview.FeePayerValidation.insufficientBalance, type: .error) + if viewState.isBalanceInsufficient { + WarningErrorView(text: L10n.TransactionReview.FeePayerValidation.insufficientBalance, type: .error) + .padding([.horizontal, .bottom], .medium3) + } + } } } + .buttonStyle(.inert) + .disabled(isDisabled) } } }