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-1666 bBlock duplicate NFT transfer #551

Merged
merged 5 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all 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

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ public struct ReceivingAccount: Sendable, FeatureReducer {
public var assets: IdentifiedArrayOf<ResourceAsset.State>
public var canBeRemoved: Bool

public struct Asset: Sendable, Hashable, Identifiable {
public typealias ID = UUID
public let id = ID()
}

public init(
account: Account?,
assets: IdentifiedArrayOf<ResourceAsset.State>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public struct TransferAccountList: Sendable, FeatureReducer {
Scope(state: /MainState.chooseAccount, action: /MainAction.chooseAccount) {
ChooseReceivingAccount()
}

Scope(state: /MainState.addAsset, action: /MainAction.addAsset) {
AssetsView()
}
Expand Down Expand Up @@ -226,7 +225,7 @@ extension TransferAccountList {
nonXrdResources: nonXrdResources
)

let selectedNonFunibleResources = assets
let selectedNonFungibleResources = assets
.compactMap(/ResourceAsset.State.nonFungibleAsset)
.reduce(into: IdentifiedArrayOf<AssetsView.State.Mode.SelectedAssets.NonFungibleTokensPerResource>()) { partialResult, asset in
var resource = partialResult[id: asset.resourceAddress] ?? .init(
Expand All @@ -237,11 +236,21 @@ extension TransferAccountList {
partialResult.updateOrAppend(resource)
}

let nftsSelectedForOtherAccounts = state.receivingAccounts
.filter { $0.id != id }
.flatMap(\.assets)
.compactMap(/ResourceAsset.State.nonFungibleAsset)
.map(\.nftToken.id)

state.destination = .relayed(
id,
with: .addAsset(.init(
account: state.fromAccount,
mode: .selection(.init(fungibleResources: selectedFungibleResources, nonFungibleResources: selectedNonFunibleResources))
mode: .selection(.init(
fungibleResources: selectedFungibleResources,
nonFungibleResources: selectedNonFungibleResources,
disabledNFTs: Set(nftsSelectedForOtherAccounts)
))
))
)
return .none
Expand Down
28 changes: 21 additions & 7 deletions Sources/Features/AssetsFeature/AssetsView+Reducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,17 @@ public struct AssetsView: Sendable, FeatureReducer {
FungibleAssetList.Row.State(xrdToken: $0, isSelected: state.mode.xrdRowSelected)
}
let nonXrd = portfolio.fungibleResources.nonXrdResources.map {
FungibleAssetList.Row.State(nonXRDToken: $0, isSelected: state.mode.nonXrdRowSelected($0.resourceAddress))
FungibleAssetList.Row.State(
nonXRDToken: $0,
isSelected: state.mode.nonXrdRowSelected($0.resourceAddress)
)
}
let nfts = portfolio.nonFungibleResources.map {
NonFungibleAssetList.Row.State(resource: $0, selectedAssets: state.mode.nftRowSelectedAssets($0.resourceAddress))
NonFungibleAssetList.Row.State(
resource: $0,
disabled: state.mode.selectedAssets?.disabledNFTs ?? [],
selectedAssets: state.mode.nftRowSelectedAssets($0.resourceAddress)
)
}

state.fungibleTokenList = .init(xrdToken: xrd, nonXrdTokens: .init(uniqueElements: nonXrd))
Expand Down Expand Up @@ -151,9 +158,12 @@ extension AssetsView.State {

let nonFungibleResources = nonFungibleTokenList.rows.compactMap {
if let selectedAssets = $0.selectedAssets, !selectedAssets.isEmpty {
let selected = $0.resource.tokens.filter { token in selectedAssets.contains(token.id)
}

return Mode.SelectedAssets.NonFungibleTokensPerResource(
resourceAddress: $0.resource.resourceAddress,
tokens: selectedAssets
tokens: selected
)
}
return nil
Expand All @@ -165,7 +175,8 @@ extension AssetsView.State {

return .init(
fungibleResources: fungibleresources,
nonFungibleResources: IdentifiedArrayOf(uniqueElements: nonFungibleResources)
nonFungibleResources: IdentifiedArrayOf(uniqueElements: nonFungibleResources),
disabledNFTs: mode.selectedAssets?.disabledNFTs ?? []
)
}

Expand Down Expand Up @@ -205,13 +216,16 @@ extension AssetsView.State {

public var fungibleResources: AccountPortfolio.FungibleResources
public var nonFungibleResources: IdentifiedArrayOf<NonFungibleTokensPerResource>
public var disabledNFTs: Set<NonFungibleAssetList.Row.State.AssetID>

public init(
fungibleResources: AccountPortfolio.FungibleResources = .init(),
nonFungibleResources: IdentifiedArrayOf<NonFungibleTokensPerResource> = []
nonFungibleResources: IdentifiedArrayOf<NonFungibleTokensPerResource> = [],
disabledNFTs: Set<NonFungibleAssetList.Row.State.AssetID>
) {
self.fungibleResources = fungibleResources
self.nonFungibleResources = nonFungibleResources
self.disabledNFTs = disabledNFTs
}

public var assetsCount: Int {
Expand Down Expand Up @@ -248,8 +262,8 @@ extension AssetsView.State {
selectedAssets?.fungibleResources.nonXrdResources.contains { $0.resourceAddress == resource }
}

func nftRowSelectedAssets(_ resource: ResourceAddress) -> NonFungibleAssetList.Row.State.SelectedAssets? {
selectedAssets.map { $0.nonFungibleResources[id: resource]?.tokens ?? [] }
func nftRowSelectedAssets(_ resource: ResourceAddress) -> OrderedSet<NonFungibleAssetList.Row.State.AssetID>? {
selectedAssets.map { $0.nonFungibleResources[id: resource]?.tokens.ids ?? [] }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@ extension NonFungibleAssetList {
public struct State: Sendable, Hashable, Identifiable {
public var id: ResourceAddress { resource.resourceAddress }

public typealias SelectedAssets = IdentifiedArrayOf<AccountPortfolio.NonFungibleResource.NonFungibleToken>
public typealias AssetID = AccountPortfolio.NonFungibleResource.NonFungibleToken.ID

public let resource: AccountPortfolio.NonFungibleResource
public var isExpanded = false
public var selectedAssets: SelectedAssets?
public var disabled: Set<AssetID> = []
public var selectedAssets: OrderedSet<AssetID>?

public init(
resource: AccountPortfolio.NonFungibleResource,
selectedAssets: SelectedAssets?
disabled: Set<AssetID> = [],
selectedAssets: OrderedSet<AssetID>?
) {
self.resource = resource
self.disabled = disabled
self.selectedAssets = selectedAssets
}
}

public enum ViewAction: Sendable, Equatable {
case isExpandedToggled
case assetTapped(AccountPortfolio.NonFungibleResource.NonFungibleToken.LocalID)
case assetTapped(State.AssetID)
}

public enum DelegateAction: Sendable, Equatable {
Expand All @@ -34,12 +37,13 @@ extension NonFungibleAssetList {
public func reduce(into state: inout State, viewAction: ViewAction) -> EffectTask<Action> {
switch viewAction {
case let .assetTapped(localID):
guard !state.disabled.contains(localID) else { return .none }
if state.selectedAssets != nil {
guard let token = state.resource.tokens[id: localID] else {
loggerGlobal.warning("Selected a missing token")
return .none
}
state.selectedAssets?.toggle(token)
state.selectedAssets?.toggle(token.id)
return .none
}
return .send(.delegate(.open(localID)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ extension NonFungibleAssetList.Row.View {
Spacer()
}
.padding(.horizontal, .medium1)
.padding(.vertical, .large3)
.frame(height: headerHeight)
.background(.app.white)
.roundedCorners(viewStore.isExpanded ? .top : .allCorners, radius: .small1)
.tokenRowShadow(!viewStore.isExpanded)
Expand All @@ -63,6 +63,8 @@ extension NonFungibleAssetList.Row.View {
}
return viewStore.resource.tokens
}

private var headerHeight: CGFloat { HitTargetSize.small.frame.height + 2 * .medium1 }
}

// MARK: - Private Computed Properties
Expand All @@ -73,6 +75,7 @@ extension NonFungibleAssetList.Row.View {
asset: AccountPortfolio.NonFungibleResource.NonFungibleToken,
index: Int
) -> some View {
let isDisabled = viewStore.disabled.contains(asset.id)
HStack {
NFTIDView(
id: asset.id.toUserFacingString,
Expand All @@ -81,10 +84,12 @@ extension NonFungibleAssetList.Row.View {
thumbnail: viewStore.isExpanded ? asset.keyImageURL : nil
)
if let selectedAssets = viewStore.selectedAssets {
CheckmarkView(appearance: .dark, isChecked: selectedAssets.contains(asset))
CheckmarkView(appearance: .dark, isChecked: selectedAssets.contains(asset.id))
}
}
.opacity(isDisabled ? 0.35 : 1)
.padding(.medium1)
.frame(minHeight: headerHeight)
.background(.app.white)
.roundedCorners(
.bottom,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Prelude/Extensions/Collection+Extra.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension Collection {
}
}

extension IdentifiedArray {
extension OrderedSet where Element: Hashable {
/// Add or remove the given element
public mutating func toggle(_ element: Element) {
if contains(element) {
Expand Down