From 4a6a7c904d3be63329681282684c869c1ff06e98 Mon Sep 17 00:00:00 2001 From: Gustaf Kugelberg <123396602+kugel3@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:46:14 +0200 Subject: [PATCH] [ABW-2124] transaction review loading (#699) --- .../AccountPortfoliosClient+Live.swift | 2 +- .../Assets/AccountPortfolio.swift | 7 +- Sources/EngineKit/ReviewedTransaction.swift | 7 + .../HelperViews/AssetResourceDetails.swift | 7 - .../NonFungibleAssetList+View.swift | 7 +- .../SettingsFeature/Settings+View.swift | 30 ++- .../FungibleTokenDetails+Extensions.swift | 45 ++-- .../MetadataValue+Extensions.swift | 6 + .../NonFungibleTokenDetails+Extensions.swift | 72 +++--- .../TransactionReview+View.swift | 8 +- .../TransactionReview.swift | 232 ++++++++---------- .../TransactionReviewAccount+View.swift | 14 +- .../TransactionReviewGuarantees.swift | 4 +- 13 files changed, 204 insertions(+), 237 deletions(-) diff --git a/Sources/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/Sources/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index 6085e07c98..c7c1054eb2 100644 --- a/Sources/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/Sources/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -306,7 +306,7 @@ extension AccountPortfoliosClient { nonFungibleLocalId: .from(stringFormat: item.nonFungibleId) ), name: details.name, - description: nil, + description: details.tokenDescription, keyImageURL: details.keyImageURL, metadata: [], stakeClaimAmount: details.claimAmount, diff --git a/Sources/Core/SharedModels/Assets/AccountPortfolio.swift b/Sources/Core/SharedModels/Assets/AccountPortfolio.swift index 776fc8cd7f..9603c7cf9b 100644 --- a/Sources/Core/SharedModels/Assets/AccountPortfolio.swift +++ b/Sources/Core/SharedModels/Assets/AccountPortfolio.swift @@ -354,7 +354,12 @@ extension [AccountPortfolio.NonFungibleResource.NonFungibleToken.NFTData] { } public var keyImageURL: URL? { - self[.keyImageURL]?.url + guard let string = self[Field.keyImageURL]?.string else { return nil } + return URL(string: string) + } + + public var tokenDescription: String? { + self[.description]?.string } public var claimEpoch: UInt64? { diff --git a/Sources/EngineKit/ReviewedTransaction.swift b/Sources/EngineKit/ReviewedTransaction.swift index b57ecca478..d13831c3b5 100644 --- a/Sources/EngineKit/ReviewedTransaction.swift +++ b/Sources/EngineKit/ReviewedTransaction.swift @@ -244,6 +244,13 @@ extension MetadataValue { return nil } + public var stringArray: [String]? { + if case let .stringArrayValue(value) = self { + return value + } + return nil + } + public var url: URL? { if case let .urlValue(value) = self { return URL(string: value) diff --git a/Sources/Features/AssetsFeature/Components/HelperViews/AssetResourceDetails.swift b/Sources/Features/AssetsFeature/Components/HelperViews/AssetResourceDetails.swift index fa4b4f5411..a9fb2d2b18 100644 --- a/Sources/Features/AssetsFeature/Components/HelperViews/AssetResourceDetails.swift +++ b/Sources/Features/AssetsFeature/Components/HelperViews/AssetResourceDetails.swift @@ -25,7 +25,6 @@ struct AssetResourceDetailsSection: View { .textStyle(.body1Regular) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, .large2) - .transition(transition) AssetDetailsSeparator() } @@ -35,12 +34,10 @@ struct AssetResourceDetailsSection: View { if let validatorAddress = viewState.validatorAddress { KeyValueView(validatorAddress: validatorAddress) - .transition(transition) } if let resourceName = viewState.resourceName { KeyValueView(key: "Name", value: resourceName) // FIXME: Strings - make a common name string for all asset details, remove the specific one(s) - .transition(transition) } if let currentSupply = viewState.currentSupply { @@ -48,7 +45,6 @@ struct AssetResourceDetailsSection: View { key: L10n.AssetDetails.currentSupply, value: currentSupply ) - .transition(transition) } AssetBehaviorsView(behaviors: viewState.behaviors) @@ -57,10 +53,7 @@ struct AssetResourceDetailsSection: View { } .padding(.horizontal, .large2) } - .animation(.default, value: viewState) } - - private let transition: AnyTransition = .opacity.combined(with: .scale(scale: 0.8)) } // MARK: - AssetDetailsSeparator diff --git a/Sources/Features/AssetsFeature/Components/NonFungibleAssetList/NonFungibleAssetList+View.swift b/Sources/Features/AssetsFeature/Components/NonFungibleAssetList/NonFungibleAssetList+View.swift index 2ace855d7d..3794edf818 100644 --- a/Sources/Features/AssetsFeature/Components/NonFungibleAssetList/NonFungibleAssetList+View.swift +++ b/Sources/Features/AssetsFeature/Components/NonFungibleAssetList/NonFungibleAssetList+View.swift @@ -13,15 +13,12 @@ extension NonFungibleAssetList { public var body: some SwiftUI.View { VStack(spacing: .medium1) { ForEachStore( - store.scope( - state: \.rows, - action: { .child(.asset($0, $1)) } - ), + store.scope(state: \.rows) { .child(.asset($0, $1)) }, content: { NonFungibleAssetList.Row.View(store: $0) } ) } .sheet( - store: store.scope(state: \.$destination, action: { .child(.destination($0)) }), + store: store.scope(state: \.$destination) { .child(.destination($0)) }, state: /NonFungibleAssetList.Destinations.State.details, action: NonFungibleAssetList.Destinations.Action.details, content: { detailsStore in diff --git a/Sources/Features/SettingsFeature/Settings+View.swift b/Sources/Features/SettingsFeature/Settings+View.swift index 208262ce81..6db2e7c39b 100644 --- a/Sources/Features/SettingsFeature/Settings+View.swift +++ b/Sources/Features/SettingsFeature/Settings+View.swift @@ -33,6 +33,10 @@ extension Settings { let shouldShowMigrateOlympiaButton: Bool let appVersion: String + var showsSomeBanner: Bool { + shouldShowAddP2PLinkButton || shouldShowMigrateOlympiaButton + } + init(state: Settings.State) { #if DEBUG let retCommitHash: String = buildInformation().version @@ -101,22 +105,24 @@ extension Settings.View { private func settingsView(viewStore: ViewStoreOf) -> some View { ScrollView { VStack(spacing: .zero) { - VStack(spacing: .medium3) { - if viewStore.shouldShowAddP2PLinkButton { - ConnectExtensionView { - viewStore.send(.addP2PLinkButtonTapped) + if viewStore.showsSomeBanner { + VStack(spacing: .medium3) { + if viewStore.shouldShowAddP2PLinkButton { + ConnectExtensionView { + viewStore.send(.addP2PLinkButtonTapped) + } } - } - if viewStore.shouldShowMigrateOlympiaButton { - MigrateOlympiaAccountsView { - viewStore.send(.importOlympiaButtonTapped) - } dismiss: { - viewStore.send(.dismissImportOlympiaHeaderButtonTapped) + if viewStore.shouldShowMigrateOlympiaButton { + MigrateOlympiaAccountsView { + viewStore.send(.importOlympiaButtonTapped) + } dismiss: { + viewStore.send(.dismissImportOlympiaHeaderButtonTapped) + } + .transition(headerTransition) } - .transition(headerTransition) } + .padding(.medium3) } - .padding(.medium3) ForEach(rows) { row in SettingsRow(row: row) { diff --git a/Sources/Features/TransactionReviewFeature/HelperExtensions/FungibleTokenDetails+Extensions.swift b/Sources/Features/TransactionReviewFeature/HelperExtensions/FungibleTokenDetails+Extensions.swift index fff66065d0..6a50d2987e 100644 --- a/Sources/Features/TransactionReviewFeature/HelperExtensions/FungibleTokenDetails+Extensions.swift +++ b/Sources/Features/TransactionReviewFeature/HelperExtensions/FungibleTokenDetails+Extensions.swift @@ -6,36 +6,31 @@ import OnLedgerEntitiesClient import Prelude import SharedModels -extension FungibleTokenDetails.State { - init( - transfer: TransactionReview.FungibleTransfer, - metadata: [String: MetadataValue?]? = nil, - resource: OnLedgerEntity.Resource? = nil - ) { +extension AccountPortfolio.FungibleResource { + init(resourceAddress: ResourceAddress, amount: BigDecimal, metadata: [String: MetadataValue?]) { self.init( - resource: .init(transfer: transfer, metadata: metadata, resource: resource), - isXRD: transfer.isXRD + resourceAddress: resourceAddress, + amount: amount, + name: metadata.name, + symbol: metadata.symbol, + description: metadata.description, + iconURL: metadata.iconURL, + tags: metadata.tags ) } -} -extension AccountPortfolio.FungibleResource { - init( - transfer: TransactionReview.FungibleTransfer, - metadata: [String: MetadataValue?]?, - resource: OnLedgerEntity.Resource? = nil - ) { + init(amount: BigDecimal, onLedgerEntity: OnLedgerEntity.Resource) { self.init( - resourceAddress: transfer.resource, - amount: transfer.amount, - divisibility: resource?.divisibility, - name: resource?.name ?? transfer.name, - symbol: resource?.symbol ?? transfer.symbol, - description: metadata?.description ?? resource?.description, - iconURL: resource?.iconURL ?? metadata?.iconURL ?? transfer.thumbnail, - behaviors: resource?.behaviors ?? [], - tags: resource?.tags ?? [], - totalSupply: resource?.totalSupply + resourceAddress: onLedgerEntity.resourceAddress, + amount: amount, + divisibility: onLedgerEntity.divisibility, + name: onLedgerEntity.name, + symbol: onLedgerEntity.symbol, + description: onLedgerEntity.description, + iconURL: onLedgerEntity.iconURL, + behaviors: onLedgerEntity.behaviors, + tags: onLedgerEntity.tags, + totalSupply: onLedgerEntity.totalSupply ) } } diff --git a/Sources/Features/TransactionReviewFeature/HelperExtensions/MetadataValue+Extensions.swift b/Sources/Features/TransactionReviewFeature/HelperExtensions/MetadataValue+Extensions.swift index 41fb36e11f..7f88ad5cfe 100644 --- a/Sources/Features/TransactionReviewFeature/HelperExtensions/MetadataValue+Extensions.swift +++ b/Sources/Features/TransactionReviewFeature/HelperExtensions/MetadataValue+Extensions.swift @@ -1,5 +1,7 @@ import EngineToolkit import Foundation +import NonEmpty +import SharedModels extension [String: MetadataValue?] { var name: String? { @@ -17,4 +19,8 @@ extension [String: MetadataValue?] { var description: String? { self["description"]??.string } + + var tags: [AssetTag] { + self["tags"]??.stringArray?.compactMap { NonEmptyString(rawValue: $0) }.map(AssetTag.custom) ?? [] + } } diff --git a/Sources/Features/TransactionReviewFeature/HelperExtensions/NonFungibleTokenDetails+Extensions.swift b/Sources/Features/TransactionReviewFeature/HelperExtensions/NonFungibleTokenDetails+Extensions.swift index 169194399f..4c89443a89 100644 --- a/Sources/Features/TransactionReviewFeature/HelperExtensions/NonFungibleTokenDetails+Extensions.swift +++ b/Sources/Features/TransactionReviewFeature/HelperExtensions/NonFungibleTokenDetails+Extensions.swift @@ -1,64 +1,50 @@ import AssetsFeature +import EngineKit import EngineToolkit import Foundation +import GatewayAPI import OnLedgerEntitiesClient import Prelude import SharedModels -extension NonFungibleTokenDetails.State { - init( - transfer: TransactionReview.NonFungibleTransfer, - metadata: [String: MetadataValue?]? = nil, - resource: OnLedgerEntity.Resource? = nil - ) throws { - try self.init( - token: .init(transfer: transfer, resource: resource), - resource: .init(transfer: transfer, metadata: metadata, resource: resource) +extension AccountPortfolio.NonFungibleResource { + init(resourceAddress: ResourceAddress, metadata: [String: MetadataValue?]) { + self.init( + resourceAddress: resourceAddress, + name: metadata.name, + description: metadata.description, + iconURL: metadata.iconURL, + tags: metadata.tags ) } -} -extension AccountPortfolio.NonFungibleResource { - init( - transfer: TransactionReview.NonFungibleTransfer, - metadata: [String: MetadataValue?]?, - resource: OnLedgerEntity.Resource? - ) { + init(onLedgerEntity: OnLedgerEntity.Resource, tokens: IdentifiedArrayOf = []) { self.init( - resourceAddress: transfer.resource, - name: metadata?.name ?? resource?.name ?? transfer.resourceName, - description: metadata?.description ?? resource?.description, - iconURL: resource?.iconURL ?? metadata?.iconURL ?? transfer.resourceImage, - behaviors: resource?.behaviors ?? [], - tags: resource?.tags ?? [], - tokens: [], - totalSupply: resource?.totalSupply + resourceAddress: onLedgerEntity.resourceAddress, + name: onLedgerEntity.name, + description: onLedgerEntity.description, + iconURL: onLedgerEntity.iconURL, + behaviors: onLedgerEntity.behaviors, + tags: onLedgerEntity.tags, + tokens: tokens, + totalSupply: onLedgerEntity.totalSupply ) } } extension AccountPortfolio.NonFungibleResource.NonFungibleToken { - init( - transfer: TransactionReview.NonFungibleTransfer, - resource: OnLedgerEntity.Resource? - ) throws { + init(resourceAddress: ResourceAddress, nftResponseItem: GatewayAPI.StateNonFungibleDetailsResponseItem) throws { try self.init( - id: transfer.nonFungibleGlobalId(), - name: transfer.tokenName, - description: nil, // FIXME: FIND - keyImageURL: nil, // FIXME: FIND - metadata: [], // FIXME: FIND + id: .fromParts( + resourceAddress: resourceAddress.intoEngine(), + nonFungibleLocalId: .from(stringFormat: nftResponseItem.nonFungibleId) + ), + name: nftResponseItem.details.name, + description: nftResponseItem.details.description, + keyImageURL: nftResponseItem.details.keyImageURL, + metadata: [], // FIXME: Find? stakeClaimAmount: nil, - canBeClaimed: false // FIXME: FIND - ) - } -} - -extension TransactionReview.NonFungibleTransfer { - func nonFungibleGlobalId() throws -> NonFungibleGlobalId { - try .fromParts( - resourceAddress: resource.intoEngine(), - nonFungibleLocalId: nonFungibleLocalIdFromStr(string: tokenID) + canBeClaimed: false // FIXME: Find? ) } } diff --git a/Sources/Features/TransactionReviewFeature/TransactionReview+View.swift b/Sources/Features/TransactionReviewFeature/TransactionReview+View.swift index 3a06a0eb87..e7235eca95 100644 --- a/Sources/Features/TransactionReviewFeature/TransactionReview+View.swift +++ b/Sources/Features/TransactionReviewFeature/TransactionReview+View.swift @@ -294,7 +294,13 @@ extension View { store: destinationStore, state: /TransactionReview.Destinations.State.nonFungibleTokenDetails, action: TransactionReview.Destinations.Action.nonFungibleTokenDetails, - content: { NonFungibleTokenDetails.View(store: $0) } + content: { detailsStore in + WithNavigationBar { + destinationStore.send(.dismiss) + } content: { + NonFungibleTokenDetails.View(store: detailsStore) + } + } ) } diff --git a/Sources/Features/TransactionReviewFeature/TransactionReview.swift b/Sources/Features/TransactionReviewFeature/TransactionReview.swift index 0dff5ea7ee..86d93b5963 100644 --- a/Sources/Features/TransactionReviewFeature/TransactionReview.swift +++ b/Sources/Features/TransactionReviewFeature/TransactionReview.swift @@ -20,9 +20,8 @@ public struct TransactionReview: Sendable, FeatureReducer { public let transactionManifest: TransactionManifest public let message: Message public let signTransactionPurpose: SigningPurpose.SignTransactionPurpose - public var networkID: NetworkID? { - reviewedTransaction?.networkId - } + + public var networkID: NetworkID? { reviewedTransaction?.networkId } public var reviewedTransaction: ReviewedTransaction? = nil @@ -81,11 +80,9 @@ public struct TransactionReview: Sendable, FeatureReducer { } public enum InternalAction: Sendable, Equatable { - case defaultDepositGuaranteeLoaded(BigDecimal) case previewLoaded(TaskResult) case createTransactionReview(TransactionReview.TransactionContent) case buildTransactionItentResult(TaskResult) - case loadedOnLedgerResource(Transfer, TaskResult) case notarizeResult(TaskResult) } @@ -178,9 +175,6 @@ public struct TransactionReview: Sendable, FeatureReducer { switch viewAction { case .appeared: return .run { [state = state] send in - let defaultDepositGuarantees = await appPreferencesClient.getPreferences().transaction.defaultDepositGuarantee - await send(.internal(.defaultDepositGuaranteeLoaded(defaultDepositGuarantees))) - let preview = await TaskResult { try await transactionClient.getTransactionReview(.init( manifestToSign: state.transactionManifest, @@ -257,30 +251,20 @@ public struct TransactionReview: Sendable, FeatureReducer { switch childAction { case let .withdrawals(.delegate(.showAsset(assetTransfer))), let .deposits(.delegate(.showAsset(assetTransfer))): - let metadata = state.reviewedTransaction?.metadataForNewlyCreatedResource(assetTransfer.resource) switch assetTransfer { case let .fungible(transfer): - state.destination = .fungibleTokenDetails(.init(transfer: transfer, metadata: metadata)) + state.destination = .fungibleTokenDetails(.init( + resource: transfer.fungibleResource, + isXRD: transfer.isXRD + )) case let .nonFungible(transfer): - do { - state.destination = try .nonFungibleTokenDetails(.init(transfer: transfer, metadata: metadata)) - } catch { - errorQueue.schedule(error) - } - } - - if metadata != nil { - // Only newly created resources have metadata, if so it's pointless to call onLedgerEntitiesClient - return .none + state.destination = .nonFungibleTokenDetails(.init( + token: transfer.token, + resource: transfer.nonFungibleResource + )) } - return .run { send in - let result = await TaskResult { - try await onLedgerEntitiesClient.getResource(assetTransfer.resource) - } - - await send(.internal(.loadedOnLedgerResource(assetTransfer, result))) - } + return .none case let .dAppsUsed(.delegate(.openDapp(id))): state.destination = .dApp(.init(dAppID: id)) @@ -425,10 +409,6 @@ public struct TransactionReview: Sendable, FeatureReducer { public func reduce(into state: inout State, internalAction: InternalAction) -> EffectTask { switch internalAction { - case let .defaultDepositGuaranteeLoaded(defaultGuarantee): - // FIXME: Apply guarantee - return .none - case let .previewLoaded(.failure(error)): return .send(.delegate(.failed(TransactionFailure.failedToPrepareTXReview(.failedToGenerateTXReview(error))))) @@ -455,31 +435,6 @@ public struct TransactionReview: Sendable, FeatureReducer { state.networkFee = content.networkFee return .none - case let .loadedOnLedgerResource(assetTransfer, .success(resource)): - let kind = resource.resourceAddress.decodedKind - // Now we also have the resource and we can update the details view - switch (assetTransfer, kind) { - case let (.fungible(transfer), .globalFungibleResourceManager): - guard case .fungibleTokenDetails = state.destination else { return .none } - state.destination = .fungibleTokenDetails(.init(transfer: transfer, resource: resource)) - case let (.nonFungible(transfer), .globalNonFungibleResourceManager): - guard case .nonFungibleTokenDetails = state.destination else { return .none } - do { - state.destination = try .nonFungibleTokenDetails(.init(transfer: transfer, resource: resource)) - } catch { - errorQueue.schedule(error) - } - default: - struct OnLedgerError: Error {} - errorQueue.schedule(OnLedgerError()) - } - - return .none - - case let .loadedOnLedgerResource(_, .failure(error)): - loggerGlobal.warning("Failed to load on-ledger resource: \(error)") - return .none - case let .buildTransactionItentResult(.success(intent)): guard let reviewedTransaction = state.reviewedTransaction else { return .none @@ -658,9 +613,7 @@ extension TransactionReview { private func extractUsedDapps(_ transaction: TransactionType.GeneralTransaction) async throws -> TransactionReviewDappsUsed.State? { let dApps = try await transaction.allAddress - .filter { - $0.entityType() == .globalGenericComponent - } + .filter { $0.entityType() == .globalGenericComponent } .map { try $0.asSpecific() } .asyncMap(extractDappInfo) @@ -727,6 +680,8 @@ extension TransactionReview { userAccounts: [Account], networkID: NetworkID ) async throws -> TransactionReviewAccounts.State? { + let defaultDepositGuarantee = await appPreferencesClient.getPreferences().transaction.defaultDepositGuarantee + var deposits: [Account: [Transfer]] = [:] for (accountAddress, accountDeposits) in transaction.accountDeposits { @@ -738,7 +693,8 @@ extension TransactionReview { metadataOfCreatedEntities: transaction.metadataOfNewlyCreatedEntities, createdEntities: transaction.addressesOfNewlyCreatedEntities, networkID: networkID, - type: $0.transferType + type: $0.transferType, + defaultDepositGuarantee: defaultDepositGuarantee ) } @@ -763,86 +719,101 @@ extension TransactionReview { metadataOfCreatedEntities: [String: [String: MetadataValue?]]?, createdEntities: [EngineToolkit.Address], networkID: NetworkID, - type: TransferType + type: TransferType, + defaultDepositGuarantee: BigDecimal = 1 ) async throws -> [Transfer] { let resourceAddress: ResourceAddress = try resourceQuantifier.resourceAddress.asSpecific() - let isNewResource = createdEntities.contains(resourceQuantifier.resourceAddress) - - let metadata: (name: String?, symbol: String?, thumbnail: URL?) = await { - if let newResourceMetadata = metadataOfCreatedEntities?[resourceAddress.address] { - return ( - newResourceMetadata.name, - newResourceMetadata.symbol, - newResourceMetadata.iconURL - ) - } else { - let remoteMetadata = try? await gatewayAPIClient.getEntityMetadata(resourceAddress.address, [.name, .symbol, .iconURL]) - return ( - remoteMetadata?.name, - remoteMetadata?.symbol, - remoteMetadata?.iconURL - ) + func getTransferInfo() async throws -> Either { + if let newlyCreatedMetadata = metadataOfCreatedEntities?[resourceAddress.address] { + return .right(newlyCreatedMetadata) + } else { + return try await .left(onLedgerEntitiesClient.getResource(resourceAddress)) } - }() + } switch resourceQuantifier { case let .fungible(_, source): let amount = try BigDecimal(fromString: source.amount.asStr()) + let isXRD = resourceAddress.isXRD(on: networkID) + + switch try await getTransferInfo() { + case let .left(onLedgerEntity): + // A fungible resource existing on ledger + func guarantee() -> TransactionClient.Guarantee? { + guard case let .predicted(instructionIndex, _) = source else { return nil } + let guaranteedAmount = defaultDepositGuarantee * amount + return .init(amount: guaranteedAmount, instructionIndex: instructionIndex, resourceAddress: resourceAddress) + } - func guarantee() -> TransactionClient.Guarantee? { - guard case let .predicted(instructionIndex, _) = source, !isNewResource else { return nil } - return .init(amount: amount, instructionIndex: instructionIndex, resourceAddress: resourceAddress) + return [.fungible(.init( + fungibleResource: .init( + amount: amount, + onLedgerEntity: onLedgerEntity + ), + isXRD: isXRD, + guarantee: guarantee() + ))] + + case let .right(newEntityMetadata): + // A newly created fungible resource + return [.fungible(.init( + fungibleResource: .init( + resourceAddress: resourceAddress, + amount: amount, + metadata: newEntityMetadata + ), + isXRD: isXRD + ))] } - - return [.fungible(.init( - resource: resourceAddress, - amount: amount, - name: metadata.name, - symbol: metadata.symbol, - thumbnail: metadata.thumbnail, - isXRD: resourceAddress.isXRD(on: networkID), - guarantee: guarantee() - ))] case let .nonFungible(_, _, .guaranteed(ids)), let .nonFungible(_, _, ids: .predicted(instructionIndex: _, value: ids)): - if isNewResource { - return try ids.map { id in - try Transfer.nonFungible(.init( - resource: resourceAddress, - resourceName: metadata.name, - resourceImage: metadata.thumbnail, - tokenID: id.toString().userFacingNonFungibleLocalID, - tokenName: nil + + switch try await getTransferInfo() { + case let .left(onLedgerEntity): + // A non-fungible resource existing on ledger + let maximumNFTIDChunkSize = 29 + + var result: [Transfer] = [] + for idChunk in ids.chunks(ofCount: maximumNFTIDChunkSize) { + let tokens = try await gatewayAPIClient.getNonFungibleData(.init( + resourceAddress: resourceAddress.address, + nonFungibleIds: idChunk.map { + try $0.toString() + } )) - } - } + .nonFungibleIds + .map { responseItem in + try Transfer.nonFungible( + .init( + nonFungibleResource: .init(onLedgerEntity: onLedgerEntity), + token: .init(resourceAddress: resourceAddress, nftResponseItem: responseItem) + ) + ) + } - let maximumNFTIDChunkSize = 29 + result.append(contentsOf: tokens) + } + return result - var result: [Transfer] = [] - for idChunk in ids.chunks(ofCount: maximumNFTIDChunkSize) { - let tokens = try await gatewayAPIClient.getNonFungibleData(.init( - resourceAddress: resourceAddress.address, - nonFungibleIds: idChunk.map { - try $0.toString() - } - )) - .nonFungibleIds - .map { - Transfer.nonFungible(.init( - resource: resourceAddress, - resourceName: metadata.name, - resourceImage: metadata.thumbnail, - tokenID: $0.nonFungibleId.userFacingNonFungibleLocalID, - tokenName: nil + case let .right(newEntityMetadata): + // A newly created non-fungible resource + return try ids.map { id in + try .nonFungible(.init( + nonFungibleResource: .init( + resourceAddress: resourceAddress, + metadata: newEntityMetadata + ), + token: .init( + id: .fromParts( + resourceAddress: resourceAddress.intoEngine(), + nonFungibleLocalId: id + ), + name: nil // FIXME: Can we get the name? + ) )) } - - result.append(contentsOf: tokens) } - - return result } } } @@ -913,9 +884,9 @@ extension TransactionReview { public var resource: ResourceAddress { switch self { case let .fungible(details): - return details.resource + return details.fungibleResource.resourceAddress case let .nonFungible(details): - return details.resource + return details.nonFungibleResource.resourceAddress } } @@ -944,22 +915,15 @@ extension TransactionReview { public struct FungibleTransfer: Sendable, Hashable { public let id = Transfer.ID() - public let resource: ResourceAddress - public let amount: BigDecimal - public let name: String? - public let symbol: String? - public let thumbnail: URL? + public let fungibleResource: AccountPortfolio.FungibleResource public let isXRD: Bool public var guarantee: TransactionClient.Guarantee? } public struct NonFungibleTransfer: Sendable, Hashable { public let id = Transfer.ID() - public let resource: ResourceAddress - public let resourceName: String? - public let resourceImage: URL? - public let tokenID: String - public let tokenName: String? + public let nonFungibleResource: AccountPortfolio.NonFungibleResource + public let token: AccountPortfolio.NonFungibleResource.NonFungibleToken } } diff --git a/Sources/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift b/Sources/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift index e6997cf416..1e6c1ab4e5 100644 --- a/Sources/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift +++ b/Sources/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift @@ -96,10 +96,11 @@ extension TransactionReviewAccount { extension TransactionReviewTokenView.ViewState { init(transfer: TransactionReview.FungibleTransfer) { + let resource = transfer.fungibleResource self.init( - name: transfer.symbol ?? transfer.name ?? L10n.TransactionReview.unknown, - thumbnail: transfer.isXRD ? .xrd : .known(transfer.thumbnail), - amount: transfer.amount, + name: resource.symbol ?? resource.name ?? L10n.TransactionReview.unknown, + thumbnail: transfer.isXRD ? .xrd : .known(resource.iconURL), + amount: resource.amount, guaranteedAmount: transfer.guarantee?.amount, fiatAmount: nil ) @@ -108,10 +109,11 @@ extension TransactionReviewTokenView.ViewState { extension TransferNFTView.ViewState { init(transfer: TransactionReview.NonFungibleTransfer) { + let token = transfer.token self.init( - tokenID: transfer.tokenID, - tokenName: transfer.tokenName, - thumbnail: transfer.resourceImage + tokenID: token.id.localId().toUserFacingString(), + tokenName: token.name, + thumbnail: transfer.nonFungibleResource.iconURL ) } } diff --git a/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift b/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift index aba35225f8..1e0ffd73ef 100644 --- a/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift +++ b/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift @@ -89,7 +89,7 @@ public struct TransactionReviewGuarantee: Sendable, FeatureReducer { return nil } - self.percentageStepper = .init(value: 100 * guaranteed / transfer.amount) + self.percentageStepper = .init(value: 100 * guaranteed / transfer.fungibleResource.amount) } } @@ -119,7 +119,7 @@ public struct TransactionReviewGuarantee: Sendable, FeatureReducer { } let newMinimumDecimal = value * 0.01 - let newAmount = newMinimumDecimal * state.transfer.amount + let newAmount = newMinimumDecimal * state.transfer.fungibleResource.amount state.transfer.guarantee?.amount = newAmount return .none