Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ABW-3565] Show resource details on Asset Transfer #1211

Merged
merged 10 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions RadixWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
48CFC2C72ADC10D900E77A5C /* MessageMode+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD3D2ADC10D800E77A5C /* MessageMode+View.swift */; };
48CFC2C82ADC10D900E77A5C /* ResourceAsset+Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD412ADC10D800E77A5C /* ResourceAsset+Reducer.swift */; };
48CFC2C92ADC10D900E77A5C /* FungibleResourceAsset+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD422ADC10D800E77A5C /* FungibleResourceAsset+View.swift */; };
48CFC2CA2ADC10D900E77A5C /* NonFungibleResourceAsset+Reducer+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD432ADC10D800E77A5C /* NonFungibleResourceAsset+Reducer+View.swift */; };
48CFC2CA2ADC10D900E77A5C /* NonFungibleResourceAsset+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD432ADC10D800E77A5C /* NonFungibleResourceAsset+View.swift */; };
48CFC2CB2ADC10D900E77A5C /* ResourceAsset+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD442ADC10D800E77A5C /* ResourceAsset+View.swift */; };
48CFC2CC2ADC10D900E77A5C /* FungibleResourceAsset+Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD452ADC10D800E77A5C /* FungibleResourceAsset+Reducer.swift */; };
48CFC2CD2ADC10D900E77A5C /* ReceivingAccount+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48CFBD462ADC10D800E77A5C /* ReceivingAccount+View.swift */; };
Expand Down Expand Up @@ -913,6 +913,7 @@
5BBC7D9E2C3D390E00B04BD6 /* BootstrapClient+Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7D9B2C3D390D00B04BD6 /* BootstrapClient+Interface.swift */; };
5BBC7D9F2C3D390E00B04BD6 /* BootstrapClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7D9C2C3D390D00B04BD6 /* BootstrapClient+Live.swift */; };
5BBC7DA42C3D442800B04BD6 /* URLRequest+Extra.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7DA32C3D442800B04BD6 /* URLRequest+Extra.swift */; };
5BBC7DA82C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7DA72C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift */; };
5BC82B6C2BED18A1009AC162 /* FactoryReset+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC82B6A2BED18A1009AC162 /* FactoryReset+View.swift */; };
5BC82B6D2BED18A1009AC162 /* FactoryReset+Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC82B6B2BED18A1009AC162 /* FactoryReset+Reducer.swift */; };
830818482B9F1621002D8351 /* HTTPClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830818472B9F1621002D8351 /* HTTPClient+Live.swift */; };
Expand Down Expand Up @@ -1366,7 +1367,7 @@
48CFBD3D2ADC10D800E77A5C /* MessageMode+View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessageMode+View.swift"; sourceTree = "<group>"; };
48CFBD412ADC10D800E77A5C /* ResourceAsset+Reducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ResourceAsset+Reducer.swift"; sourceTree = "<group>"; };
48CFBD422ADC10D800E77A5C /* FungibleResourceAsset+View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FungibleResourceAsset+View.swift"; sourceTree = "<group>"; };
48CFBD432ADC10D800E77A5C /* NonFungibleResourceAsset+Reducer+View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NonFungibleResourceAsset+Reducer+View.swift"; sourceTree = "<group>"; };
48CFBD432ADC10D800E77A5C /* NonFungibleResourceAsset+View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NonFungibleResourceAsset+View.swift"; sourceTree = "<group>"; };
48CFBD442ADC10D800E77A5C /* ResourceAsset+View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ResourceAsset+View.swift"; sourceTree = "<group>"; };
48CFBD452ADC10D800E77A5C /* FungibleResourceAsset+Reducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FungibleResourceAsset+Reducer.swift"; sourceTree = "<group>"; };
48CFBD462ADC10D800E77A5C /* ReceivingAccount+View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReceivingAccount+View.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2061,6 +2062,7 @@
5BBC7D9B2C3D390D00B04BD6 /* BootstrapClient+Interface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BootstrapClient+Interface.swift"; sourceTree = "<group>"; };
5BBC7D9C2C3D390D00B04BD6 /* BootstrapClient+Live.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BootstrapClient+Live.swift"; sourceTree = "<group>"; };
5BBC7DA32C3D442800B04BD6 /* URLRequest+Extra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Extra.swift"; sourceTree = "<group>"; };
5BBC7DA72C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NonFungibleResourceAsset+Reducer.swift"; sourceTree = "<group>"; };
5BC82B6A2BED18A1009AC162 /* FactoryReset+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactoryReset+View.swift"; sourceTree = "<group>"; };
5BC82B6B2BED18A1009AC162 /* FactoryReset+Reducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactoryReset+Reducer.swift"; sourceTree = "<group>"; };
830818472B9F1621002D8351 /* HTTPClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPClient+Live.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3236,10 +3238,11 @@
isa = PBXGroup;
children = (
48CFBD412ADC10D800E77A5C /* ResourceAsset+Reducer.swift */,
48CFBD422ADC10D800E77A5C /* FungibleResourceAsset+View.swift */,
48CFBD432ADC10D800E77A5C /* NonFungibleResourceAsset+Reducer+View.swift */,
48CFBD442ADC10D800E77A5C /* ResourceAsset+View.swift */,
48CFBD452ADC10D800E77A5C /* FungibleResourceAsset+Reducer.swift */,
48CFBD422ADC10D800E77A5C /* FungibleResourceAsset+View.swift */,
5BBC7DA72C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift */,
48CFBD432ADC10D800E77A5C /* NonFungibleResourceAsset+View.swift */,
);
path = Asset;
sourceTree = "<group>";
Expand Down Expand Up @@ -7526,7 +7529,7 @@
48CFC5982ADC10DA00E77A5C /* ControlState.swift in Sources */,
A41266F92B160F4C00EA38E9 /* ManualAccountRecoveryCoordinator+View.swift in Sources */,
A43F1E232BC72884001DD3FA /* AnyRange.swift in Sources */,
48CFC2CA2ADC10D900E77A5C /* NonFungibleResourceAsset+Reducer+View.swift in Sources */,
48CFC2CA2ADC10D900E77A5C /* NonFungibleResourceAsset+View.swift in Sources */,
48CFC4B72ADC10DA00E77A5C /* TransactionPreviewResponse.swift in Sources */,
48CFC3452ADC10D900E77A5C /* Completion+View.swift in Sources */,
5B43B08B2BDAAD4B00AA1E92 /* AddressDetails+View.swift in Sources */,
Expand Down Expand Up @@ -7827,6 +7830,7 @@
83EE47852AF0EE3C00155F03 /* ProgrammaticScryptoSborValueString.swift in Sources */,
83EE47912AF0EE3C00155F03 /* FromLedgerStateMixin.swift in Sources */,
48CFC5BD2ADC10DA00E77A5C /* Hint.swift in Sources */,
5BBC7DA82C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift in Sources */,
48CFC3622ADC10D900E77A5C /* LedgerHardwareDevices+Reducer.swift in Sources */,
48CFC2B22ADC10D900E77A5C /* DerivePublicKeys.swift in Sources */,
48CFC34D2ADC10D900E77A5C /* Login.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ private final class HomeCardsObserver: Sargon.HomeCardsObserver, Sendable {
let subject: AsyncCurrentValueSubject<[HomeCard]> = .init([])

func handleCardsUpdate(cards: [HomeCard]) {
print("M- Received cards from Sargon \(cards)")
subject.send(cards)
}
}
14 changes: 14 additions & 0 deletions RadixWallet/Core/DesignSystem/Extensions/View+Extra.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,18 @@ extension View {
func eraseToAnyView() -> AnyView {
AnyView(self)
}

/// Embeds the view on a `Button` when an action isprovided.
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
/// Otherwise returns the same view unmodified.
func embedInButton(when action: (() -> Void)?) -> some View {
Group {
if let action {
Button(action: action) {
self
}
} else {
self
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct FungibleResourceAsset: Sendable, FeatureReducer {
// MARK: - Mutable state

@PresentationState
public var alert: AlertState<ViewAction.Alert>?
public var destination: Destination.State? = nil

public var transferAmountStr: String = ""
public var transferAmount: Decimal192? = nil
Expand All @@ -51,15 +51,25 @@ public struct FungibleResourceAsset: Sendable, FeatureReducer {
case amountChanged(String)
case maxAmountTapped
case focusChanged(Bool)
case removeTapped
case resourceTapped
}

case alert(PresentationAction<Alert>)
public enum DelegateAction: Equatable, Sendable {
case amountChanged
case resourceTapped
}

public enum Alert: Hashable, Sendable {
case chooseXRDAmountAlert(ChooseXRDAmountAlert)
public struct Destination: DestinationReducer {
public enum State: Sendable, Hashable {
case chooseXRDAmount(AlertState<Action.ChooseXRDAmount>)
case needsToPayFeeFromOtherAccount(AlertState<Action.NeedsToPayFeeFromOtherAccount>)
}

public enum Action: Sendable, Equatable {
case chooseXRDAmount(ChooseXRDAmount)
case needsToPayFeeFromOtherAccount(NeedsToPayFeeFromOtherAccount)

public enum ChooseXRDAmountAlert: Hashable, Sendable {
public enum ChooseXRDAmount: Hashable, Sendable {
case deductFee(Decimal192)
case sendAll(Decimal192)
case cancel
Expand All @@ -70,13 +80,23 @@ public struct FungibleResourceAsset: Sendable, FeatureReducer {
case cancel
}
}

public var body: some ReducerOf<Self> {
EmptyReducer()
}
}

public enum DelegateAction: Equatable, Sendable {
case removed
case amountChanged
public init() {}

public var body: some ReducerOf<Self> {
Reduce(core)
.ifLet(destinationPath, action: /Action.destination) {
Destination()
}
}

private let destinationPath: WritableKeyPath<State, PresentationState<Destination.State>> = \.$destination

public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action> {
switch viewAction {
case let .amountChanged(transferAmountStr):
Expand All @@ -95,13 +115,13 @@ public struct FungibleResourceAsset: Sendable, FeatureReducer {

if state.isXRD {
if remainingAmount >= State.defaultFee {
state.alert = .chooseXRDAmount(
state.destination = .chooseXRDAmount(.alert(
feeDeductedAmount: remainingAmount - State.defaultFee,
maxAmount: remainingAmount
)
))
return .none
} else if remainingAmount > 0 {
state.alert = .willNeedToPayFeeFromOtherAccount(remainingAmount)
state.destination = .needsToPayFeeFromOtherAccount(.anotherAccount(remainingAmount))
return .none
}
}
Expand All @@ -114,68 +134,59 @@ public struct FungibleResourceAsset: Sendable, FeatureReducer {
state.focused = focused
return .none

case .removeTapped:
return .send(.delegate(.removed))

case let .alert(action):
state.alert = nil
switch action {
case let .presented(.chooseXRDAmountAlert(.deductFee(amount))),
let .presented(.chooseXRDAmountAlert(.sendAll(amount))),
let .presented(.needsToPayFeeFromOtherAccount(.confirm(amount))):
state.transferAmount = amount
state.transferAmountStr = amount.formattedPlain(useGroupingSeparator: false)
return .send(.delegate(.amountChanged))

case .presented(.needsToPayFeeFromOtherAccount(.cancel)),
.presented(.chooseXRDAmountAlert(.cancel)):
return .none

case .dismiss:
return .none
}
case .resourceTapped:
return .send(.delegate(.resourceTapped))
}
}

public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect<Action> {
switch presentedAction {
case let .chooseXRDAmount(.deductFee(amount)),
let .chooseXRDAmount(.sendAll(amount)),
let .needsToPayFeeFromOtherAccount(.confirm(amount)):
state.transferAmount = amount
state.transferAmountStr = amount.formattedPlain(useGroupingSeparator: false)
return .send(.delegate(.amountChanged))

default:
return .none
}
}
}

extension AlertState where Action == FungibleResourceAsset.ViewAction.Alert {
fileprivate static func chooseXRDAmount(feeDeductedAmount: Decimal192, maxAmount: Decimal192) -> Self {
AlertState(
title: TextState(L10n.AssetTransfer.MaxAmountDialog.title),
message: TextState(L10n.AssetTransfer.MaxAmountDialog.body),
buttons:
[
ButtonState.default(
TextState(L10n.AssetTransfer.MaxAmountDialog.saveXrdForFeeButton(feeDeductedAmount.formatted())),
action: .send(.chooseXRDAmountAlert(.deductFee(feeDeductedAmount)))
),
ButtonState.default(
TextState(L10n.AssetTransfer.MaxAmountDialog.sendAllButton(maxAmount.formatted())),
action: .send(.chooseXRDAmountAlert(.sendAll(maxAmount)))
),
ButtonState.default(
TextState(L10n.Common.cancel),
action: .send(.chooseXRDAmountAlert(.cancel))
),
]
)
extension AlertState<FungibleResourceAsset.Destination.Action.ChooseXRDAmount> {
fileprivate static func alert(feeDeductedAmount: Decimal192, maxAmount: Decimal192) -> AlertState {
AlertState {
TextState(L10n.AssetTransfer.MaxAmountDialog.title)
} actions: {
ButtonState(action: .deductFee(feeDeductedAmount)) {
TextState(L10n.AssetTransfer.MaxAmountDialog.saveXrdForFeeButton(feeDeductedAmount.formatted()))
}
ButtonState(action: .sendAll(maxAmount)) {
TextState(L10n.AssetTransfer.MaxAmountDialog.sendAllButton(maxAmount.formatted()))
}
ButtonState(role: .cancel, action: .cancel) {
TextState(L10n.Common.cancel)
}
} message: {
TextState(L10n.AssetTransfer.MaxAmountDialog.body)
}
}
}

fileprivate static func willNeedToPayFeeFromOtherAccount(_ amount: Decimal192) -> Self {
AlertState(
title: TextState(L10n.AssetTransfer.MaxAmountDialog.title),
message: TextState("Sending the full amount of XRD in this account will require you to pay the transaction fee from a different account"),
buttons:
[
ButtonState.default(
TextState(L10n.Common.ok),
action: .send(.needsToPayFeeFromOtherAccount(.confirm(amount)))
),
ButtonState.default(
TextState(L10n.Common.cancel),
action: .send(.needsToPayFeeFromOtherAccount(.cancel))
),
]
)
extension AlertState<FungibleResourceAsset.Destination.Action.NeedsToPayFeeFromOtherAccount> {
fileprivate static func anotherAccount(_ amount: Decimal192) -> AlertState {
AlertState {
TextState(L10n.AssetTransfer.MaxAmountDialog.title)
} actions: {
ButtonState(action: .confirm(amount)) {
TextState(L10n.Common.ok)
}
ButtonState(role: .cancel, action: .cancel) {
TextState(L10n.Common.cancel)
}
} message: {
TextState("Sending the full amount of XRD in this account will require you to pay the transaction fee from a different account")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,26 @@ extension FungibleResourceAsset.View {
public var body: some View {
WithViewStore(store, observe: { $0 }, send: { .view($0) }) { viewStore in
VStack(alignment: .trailing) {
ResourceBalanceView(viewStore.resourceBalance, appearance: .compact)
.withAuxiliary(spacing: .small2) {
TextField(
Decimal192.zero.formatted(),
text: viewStore.binding(
get: \.transferAmountStr,
send: { .amountChanged($0) }
)
ResourceBalanceView(viewStore.resourceBalance, appearance: .compact) {
viewStore.send(.resourceTapped)
}
.withAuxiliary(spacing: .small2) {
TextField(
Decimal192.zero.formatted(),
text: viewStore.binding(
get: \.transferAmountStr,
send: { .amountChanged($0) }
)
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.lineLimit(1)
.minimumScaleFactor(0.7)
.foregroundColor(.app.gray1)
.textStyle(.sectionHeader)
.focused($focused)
.bind(viewStore.focusedBinding, to: $focused)
}
)
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.lineLimit(1)
.minimumScaleFactor(0.7)
.foregroundColor(.app.gray1)
.textStyle(.sectionHeader)
.focused($focused)
.bind(viewStore.focusedBinding, to: $focused)
}

if viewStore.totalExceedsBalance {
// TODO: Add better style
Expand Down Expand Up @@ -84,13 +86,41 @@ extension FungibleResourceAsset.View {
}
}
.padding(.medium3)
.alert(store: store.alert)
.destinations(with: store)
}
}
}

private extension StoreOf<FungibleResourceAsset> {
var alert: AlertPresentationStore<FungibleResourceAsset.ViewAction.Alert> {
scope(state: \.$alert, action: { .view(.alert($0)) })
var destination: PresentationStoreOf<FungibleResourceAsset.Destination> {
func scopeState(state: State) -> PresentationState<FungibleResourceAsset.Destination.State> {
state.$destination
}
return scope(state: scopeState, action: Action.destination)
}
}

@MainActor
private extension View {
func destinations(with store: StoreOf<FungibleResourceAsset>) -> some View {
let destinationStore = store.destination
return chooseXRDAmount(with: destinationStore)
.needsToPayFeeFromOtherAccount(with: destinationStore)
}

private func chooseXRDAmount(with destinationStore: PresentationStoreOf<FungibleResourceAsset.Destination>) -> some View {
alert(
store: destinationStore,
state: /FungibleResourceAsset.Destination.State.chooseXRDAmount,
action: FungibleResourceAsset.Destination.Action.chooseXRDAmount
)
}

private func needsToPayFeeFromOtherAccount(with destinationStore: PresentationStoreOf<FungibleResourceAsset.Destination>) -> some View {
alert(
store: destinationStore,
state: /FungibleResourceAsset.Destination.State.needsToPayFeeFromOtherAccount,
action: FungibleResourceAsset.Destination.Action.needsToPayFeeFromOtherAccount
)
}
}
Loading
Loading