diff --git a/RadixWallet/Core/FeaturePrelude/AddressView/AddressView.swift b/RadixWallet/Core/FeaturePrelude/AddressView/AddressView.swift index ef31068c40..b30c2b4e80 100644 --- a/RadixWallet/Core/FeaturePrelude/AddressView/AddressView.swift +++ b/RadixWallet/Core/FeaturePrelude/AddressView/AddressView.swift @@ -4,6 +4,7 @@ public struct AddressView: View { let identifiable: LedgerIdentifiable let isTappable: Bool let imageColor: Color? + let isImageHidden: Bool private let showLocalIdOnly: Bool @Dependency(\.gatewaysClient) var gatewaysClient @@ -17,12 +18,14 @@ public struct AddressView: View { _ identifiable: LedgerIdentifiable, showLocalIdOnly: Bool = false, isTappable: Bool = true, - imageColor: Color? = nil + imageColor: Color? = nil, + isImageHidden: Bool = false ) { self.identifiable = identifiable self.showLocalIdOnly = showLocalIdOnly self.isTappable = isTappable self.imageColor = imageColor + self.isImageHidden = isImageHidden } } @@ -73,11 +76,14 @@ extension AddressView { HStack(spacing: .small3) { Text(formattedText) .lineLimit(1) - if let imageColor { - image - .foregroundStyle(imageColor) - } else { - image + + if !isImageHidden { + if let imageColor { + image + .foregroundStyle(imageColor) + } else { + image + } } } } diff --git a/RadixWallet/Core/SharedModels/Assets/AccountPortfolio+Extensions.swift b/RadixWallet/Core/SharedModels/Assets/AccountPortfolio+Extensions.swift index 808e916383..53f6ef714c 100644 --- a/RadixWallet/Core/SharedModels/Assets/AccountPortfolio+Extensions.swift +++ b/RadixWallet/Core/SharedModels/Assets/AccountPortfolio+Extensions.swift @@ -1,6 +1,6 @@ extension OnLedgerEntity.NonFungibleToken { - public init(resourceAddress: ResourceAddress, nftID: NonFungibleLocalId, nftData: NFTData?) throws { + public init(resourceAddress: ResourceAddress, nftID: NonFungibleLocalId, nftData: NFTData?) { self.init( id: NonFungibleGlobalID( resourceAddress: resourceAddress, diff --git a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/AddAsset+Reducer.swift b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/AddAsset+Reducer.swift index 73ac7d46d4..ea9d55ca98 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/AddAsset+Reducer.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/AddAsset+Reducer.swift @@ -37,7 +37,9 @@ public struct AddAsset: FeatureReducer, Sendable { case let .allowDenyAssets(exceptionRule): address = try? .assetException(.init(address: .init(validatingAddress: resourceAddress), exceptionRule: exceptionRule)) case .allowDepositors: - if let resourceAddress = try? ResourceAddress(validatingAddress: resourceAddress) { + if let nonFungibleGlobalId = try? NonFungibleGlobalId(resourceAddress) { + address = .allowedDepositor(.nonFungible(value: nonFungibleGlobalId)) + } else if let resourceAddress = try? ResourceAddress(validatingAddress: resourceAddress) { address = .allowedDepositor(.resource(value: resourceAddress)) } } diff --git a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift index ceaa8112b7..c1b1416553 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift @@ -123,7 +123,7 @@ extension ResourcesList.View { @ViewBuilder func resourceRowView(_ viewState: ResourceViewState, _ viewStore: ViewStoreOf) -> some SwiftUI.View { - HStack { + HStack(spacing: .zero) { if viewState.address.resourceAddress.isNonFungible { Thumbnail(.nft, url: viewState.iconURL) } else { @@ -131,12 +131,13 @@ extension ResourcesList.View { } VStack(alignment: .leading, spacing: .zero) { - Text(viewState.name ?? "") + Text(viewState.name ?? "-") .textStyle(.body1HighImportance) .foregroundColor(.app.gray1) AddressView( viewState.address.ledgerIdentifiable, - isTappable: false + isTappable: false, + isImageHidden: true ) .foregroundColor(.app.gray2) } diff --git a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ThirdPartyDeposits+Reducer.swift b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ThirdPartyDeposits+Reducer.swift index 05dde2d63e..13f08e149d 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ThirdPartyDeposits+Reducer.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ThirdPartyDeposits+Reducer.swift @@ -130,7 +130,7 @@ public struct ManageThirdPartyDeposits: FeatureReducer, Sendable { .run { send in do { /// Wait for user to complete the interaction with Transaction Review - let result = try await dappInteractionClient.addWalletInteraction( + let result = await dappInteractionClient.addWalletInteraction( .transaction(.init(send: .init(transactionManifest: manifest))), .accountDepositSettings ) diff --git a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceView.swift b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceView.swift index 3103adba71..37b8bdfa98 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceView.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceView.swift @@ -119,7 +119,7 @@ public struct ResourceBalanceView: View { public let isSelected: Bool? public let action: (() -> Void)? - public enum Appearance: Equatable { + public enum Appearance: Sendable, Equatable { case standard case compact(border: Bool) @@ -241,8 +241,8 @@ extension ResourceBalanceView { VStack(alignment: .leading, spacing: .medium3) { FungibleView( thumbnail: .lsu(viewState.icon), - caption1: viewState.title, - caption2: viewState.validatorName, + caption1: viewState.title ?? "-", + caption2: viewState.validatorName ?? "-", fallback: nil, amount: viewState.amount, compact: compact, @@ -273,8 +273,8 @@ extension ResourceBalanceView { VStack(alignment: .leading, spacing: .zero) { FungibleView( thumbnail: .poolUnit(viewState.poolIcon), - caption1: viewState.poolName, - caption2: viewState.dAppName.wrappedValue?.flatMap { $0 }, + caption1: viewState.poolName ?? "-", + caption2: viewState.dAppName.wrappedValue?.flatMap { $0 } ?? "-", fallback: nil, amount: viewState.amount, compact: compact, @@ -314,8 +314,8 @@ extension ResourceBalanceView { VStack(alignment: .leading, spacing: .zero) { NonFungibleView( thumbnail: .stakeClaimNFT(viewState.resourceMetadata.iconURL), - caption1: viewState.resourceMetadata.title ?? "", - caption2: viewState.validatorName ?? "", + caption1: viewState.resourceMetadata.title, + caption2: viewState.validatorName, compact: compact ) @@ -457,7 +457,7 @@ extension ResourceBalanceView { compact: compact ) - if useSpacer { + if useSpacer, isSelected == nil { Spacer(minLength: .small2) } @@ -465,9 +465,7 @@ extension ResourceBalanceView { .padding(.leading, isSelected != nil ? .small2 : 0) if let isSelected { - if !useSpacer, caption1 == nil { - Spacer(minLength: .small2) - } + Spacer(minLength: .small2) CheckmarkView(appearance: .dark, isChecked: isSelected) } } @@ -497,8 +495,8 @@ extension ResourceBalanceView { CaptionedThumbnailView( type: thumbnail.type, url: thumbnail.url, - caption1: caption1, - caption2: caption2, + caption1: caption1 ?? "-", + caption2: caption2 ?? "-", compact: compact ) diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift index 683ded954d..a98d352ecf 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift @@ -88,7 +88,7 @@ extension TransactionReview { networkID: networkID ) - let proofs: TransactionReviewProofs.State? = try await exctractProofs(summary.presentedProofs.map(\.resourceAddress)) + let proofs: TransactionReviewProofs.State? = try await exctractProofs(summary.presentedProofs) return Sections( withdrawals: withdrawals, @@ -344,9 +344,11 @@ extension TransactionReview { return DappEntity(id: dAppDefinitionAddress, metadata: metadata) } - private func exctractProofs(_ accountProofs: [ResourceAddress]) async throws -> TransactionReviewProofs.State? { + private func exctractProofs(_ accountProofs: [ResourceSpecifier]) async throws -> TransactionReviewProofs.State? { let proofs = try await accountProofs .uniqued() + .asyncMap(extractResourceBalanceInfo) + .flatMap { $0 } .asyncMap(extractProofInfo) guard !proofs.isEmpty else { return nil } @@ -354,10 +356,33 @@ extension TransactionReview { return TransactionReviewProofs.State(proofs: proofs.asIdentified()) } - private func extractProofInfo(_ address: ResourceAddress) async throws -> ProofEntity { + private func extractResourceBalanceInfo(specifier: ResourceSpecifier) async throws -> [(ResourceAddress, ResourceBalance.Details)] { + switch specifier { + case let .fungible(resourceAddress, amount): + return [( + resourceAddress, + .fungible( + .init( + isXRD: resourceAddress.isXRD, + amount: .init(nominalAmount: amount) + ) + ) + )] + case let .nonFungible(resourceAddress, ids): + let globalIds = ids.map { NonFungibleGlobalId(resourceAddress: resourceAddress, nonFungibleLocalId: $0) } + let tokens = try await onLedgerEntitiesClient.getNonFungibleTokenData( + .init(resource: resourceAddress, nonFungibleIds: globalIds) + ) + return tokens.map { (resourceAddress, .nonFungible($0)) } + } + } + + private func extractProofInfo(resourceAddress: ResourceAddress, details: ResourceBalance.Details) async throws -> ProofEntity { try await ProofEntity( - id: address, - metadata: onLedgerEntitiesClient.getResource(address, metadataKeys: .dappMetadataKeys).metadata + resourceBalance: ResourceBalance( + resource: onLedgerEntitiesClient.getResource(resourceAddress, metadataKeys: .dappMetadataKeys), + details: details + ) ) } diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift index 18fb1cc917..72be179416 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview.swift @@ -356,54 +356,7 @@ public struct TransactionReview: Sendable, FeatureReducer { switch childAction { case let .withdrawals(.delegate(.showAsset(transfer, token))), let .deposits(.delegate(.showAsset(transfer, token))): - switch transfer.details { - case let .fungible(details): - state.destination = .fungibleTokenDetails( - .init( - resourceAddress: transfer.resource.resourceAddress, - resource: .success(transfer.resource), - ownedFungibleResource: .init( - resourceAddress: transfer.resource.resourceAddress, - atLedgerState: transfer.resource.atLedgerState, - amount: details.amount, - metadata: transfer.resource.metadata - ), - isXRD: details.isXRD - ) - ) - - case let .nonFungible(details): - state.destination = .nonFungibleTokenDetails(.init( - resourceAddress: transfer.resource.resourceAddress, - resourceDetails: .success(transfer.resource), - token: details, - ledgerState: transfer.resource.atLedgerState - )) - - case let .liquidStakeUnit(details): - state.destination = .lsuDetails(.init( - validator: details.validator, - stakeUnitResource: .init(resource: details.resource, amount: .init(nominalAmount: details.amount)), - xrdRedemptionValue: details.worth - )) - - return .none - - case let .poolUnit(details): - state.destination = .poolUnitDetails(.init(resourcesDetails: details.details)) - - case let .stakeClaimNFT(details): - state.destination = .nonFungibleTokenDetails(.init( - resourceAddress: transfer.resource.resourceAddress, - resourceDetails: .success(transfer.resource), - token: token, - ledgerState: transfer.resource.atLedgerState, - stakeClaim: details.stakeClaimTokens.stakeClaims.first, - isClaimStakeEnabled: false - )) - } - - return .none + return resourceDetailsEffect(state: &state, resource: transfer.resource, details: transfer.details, nft: token) case let .dAppsUsed(.delegate(.openDapp(dAppID))), let .contributingToPools(.delegate(.openDapp(dAppID))), let .redeemingFromPools(.delegate(.openDapp(dAppID))): state.destination = .dApp(.init(dAppDefinitionAddress: dAppID)) @@ -431,6 +384,10 @@ public struct TransactionReview: Sendable, FeatureReducer { return .none + case let .proofs(.delegate(.showAsset(proof))): + let resource = proof.resourceBalance.resource + return resourceDetailsEffect(state: &state, resource: resource, details: proof.resourceBalance.details) + case .networkFee(.delegate(.showCustomizeFees)): guard let reviewedTransaction = state.reviewedTransaction else { return .none @@ -731,6 +688,58 @@ extension TransactionReview { } } } + + func resourceDetailsEffect( + state: inout State, + resource: OnLedgerEntity.Resource, + details: ResourceBalance.Details, + nft: OnLedgerEntity.NonFungibleToken? = nil + ) -> Effect { + switch details { + case let .fungible(details): + state.destination = .fungibleTokenDetails(.init( + resourceAddress: resource.resourceAddress, + resource: .success(resource), + ownedFungibleResource: .init( + resourceAddress: resource.resourceAddress, + atLedgerState: resource.atLedgerState, + amount: details.amount, + metadata: resource.metadata + ), + isXRD: details.isXRD + )) + + case let .nonFungible(details): + state.destination = .nonFungibleTokenDetails(.init( + resourceAddress: resource.resourceAddress, + resourceDetails: .success(resource), + token: details, + ledgerState: resource.atLedgerState + )) + + case let .liquidStakeUnit(details): + state.destination = .lsuDetails(.init( + validator: details.validator, + stakeUnitResource: .init(resource: details.resource, amount: .init(nominalAmount: details.amount)), + xrdRedemptionValue: details.worth + )) + + case let .poolUnit(details): + state.destination = .poolUnitDetails(.init(resourcesDetails: details.details)) + + case let .stakeClaimNFT(details): + state.destination = .nonFungibleTokenDetails(.init( + resourceAddress: resource.resourceAddress, + resourceDetails: .success(resource), + token: nft, + ledgerState: resource.atLedgerState, + stakeClaim: details.stakeClaimTokens.stakeClaims.first, + isClaimStakeEnabled: false + )) + } + + return .none + } } // MARK: - FailedToAddLockFee @@ -783,8 +792,8 @@ extension TransactionReview { extension TransactionReview { public struct ProofEntity: Sendable, Identifiable, Hashable { - public let id: ResourceAddress - public let metadata: OnLedgerEntity.Metadata + public var id: ResourceBalance { resourceBalance } + public let resourceBalance: ResourceBalance } public struct DappEntity: Sendable, Identifiable, Hashable { diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs+View.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs+View.swift index 55650dbe6c..d89847b38a 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs+View.swift @@ -28,43 +28,15 @@ extension TransactionReviewProofs { } .padding(.bottom, .medium2) - ForEach(viewStore.proofs) { proof in - VStack(spacing: 0) { - let metadata = proof.metadata - ProofView(thumbnail: metadata.iconURL, name: metadata.name) { - viewStore.send(.proofTapped(id: proof.id)) + VStack(spacing: .medium3) { + ForEach(viewStore.proofs) { proof in + ResourceBalanceView(proof.resourceBalance.viewState, appearance: .compact) { + viewStore.send(.proofTapped(proof)) } - .padding(.bottom, .medium3) - - if proof.id != viewStore.proofs.last?.id { - Separator() - .padding(.bottom, .medium3) - } - } - } - } - } - } - - struct ProofView: SwiftUI.View { - let thumbnail: URL? - let name: String? - let action: () -> Void - - var body: some SwiftUI.View { - Button(action: action) { - HStack(spacing: 0) { - Thumbnail(.dapp, url: thumbnail, size: .smallest) - .padding(.trailing, .small1) - - if let name { - Text(name) - .textStyle(.body1HighImportance) - .foregroundColor(.app.gray1) } - - Spacer(minLength: 0) + Separator() } + .padding(.bottom, .medium3) } } } diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs.swift index eb2936eaa3..f41a449df4 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewProofs/TransactionReviewProofs.swift @@ -13,7 +13,11 @@ public struct TransactionReviewProofs: Sendable, FeatureReducer { public enum ViewAction: Sendable, Equatable { case infoTapped - case proofTapped(id: TransactionReview.ProofEntity.ID) + case proofTapped(TransactionReview.ProofEntity) + } + + public enum DelegateAction: Sendable, Equatable { + case showAsset(TransactionReview.ProofEntity) } public init() {} @@ -22,9 +26,8 @@ public struct TransactionReviewProofs: Sendable, FeatureReducer { switch viewAction { case .infoTapped: .none - case let .proofTapped(id): - // FIXME: Handle tap - .none + case let .proofTapped(proof): + .send(.delegate(.showAsset(proof))) } } }