Skip to content

Commit 8df4fe2

Browse files
committed
Refactor NFT collections to show unverified items in grid with count badge
1 parent 5ffa4e5 commit 8df4fe2

File tree

14 files changed

+152
-85
lines changed

14 files changed

+152
-85
lines changed

Features/Assets/Sources/Scenes/AssetScene.swift

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,7 @@ public struct AssetScene: View {
4040

4141
if model.showStatus {
4242
Section {
43-
NavigationCustomLink(with:
44-
ListItemImageView(
45-
title: Localized.Transaction.status,
46-
subtitle: model.scoreViewModel.status,
47-
subtitleStyle: model.scoreViewModel.statusStyle,
48-
assetImage: model.scoreViewModel.assetImage,
49-
infoAction: { model.onSelectTokenStatus() }
50-
)
51-
) {
52-
model.onSelectTokenStatus()
53-
}
43+
AssetStatusView(model: model.scoreViewModel, action: model.onSelectTokenStatus)
5444
}
5545
}
5646

Features/NFT/Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ let package = Package(
2424
.package(name: "Store", path: "../../Packages/Store"),
2525
.package(name: "Formatters", path: "../../Packages/Formatters"),
2626
.package(name: "ChainServices", path: "../../Packages/ChainServices"),
27+
.package(name: "InfoSheet", path: "../InfoSheet"),
2728
],
2829
targets: [
2930
.target(
@@ -41,7 +42,8 @@ let package = Package(
4142
.product(name: "WalletService", package: "FeatureServices"),
4243
"Formatters",
4344
.product(name: "ExplorerService", package: "ChainServices"),
44-
.product(name: "AvatarService", package: "FeatureServices")
45+
.product(name: "AvatarService", package: "FeatureServices"),
46+
"InfoSheet",
4547
],
4648
path: "Sources"
4749
),

Features/NFT/Sources/Protocols/CollectionsViewable.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public protocol CollectionsViewable: AnyObject, Observable {
1414

1515
var title: String { get }
1616
var columns: [GridItem] { get }
17-
var content: GridContent { get }
17+
var items: [GridPosterViewItem] { get }
1818
var emptyContentModel: EmptyContentTypeViewModel { get }
1919

2020
var wallet: Wallet { get set }
@@ -56,7 +56,8 @@ extension CollectionsViewable {
5656
id: data.id,
5757
destination: Scenes.Collection(id: data.collection.id, name: data.collection.name),
5858
assetImage: AssetImage(type: data.collection.name, imageURL: data.collection.images.preview.url.asURL),
59-
title: data.collection.name
59+
title: data.collection.name,
60+
count: data.assets.count
6061
)
6162
}
6263

Features/NFT/Sources/Scenes/CollectibleScene.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Components
88
import PrimitivesComponents
99
import Localization
1010
import ImageGalleryService
11+
import InfoSheet
1112

1213
public struct CollectibleScene: View {
1314
@State private var model: CollectibleViewModel
@@ -19,6 +20,9 @@ public struct CollectibleScene: View {
1920
public var body: some View {
2021
List {
2122
headerSectionView
23+
if model.showStatus {
24+
statusSectionView
25+
}
2226
assetInfoSectionView
2327
if model.showAttributes {
2428
attributesSectionView
@@ -33,6 +37,9 @@ public struct CollectibleScene: View {
3337
.alertSheet($model.isPresentingAlertMessage)
3438
.toast(message: $model.isPresentingToast)
3539
.safariSheet(url: $model.isPresentingTokenExplorerUrl)
40+
.sheet(item: $model.isPresentingInfoSheet) {
41+
InfoSheetScene(model: InfoSheetModelFactory.create(from: $0))
42+
}
3643
}
3744
}
3845

@@ -47,7 +54,7 @@ extension CollectibleScene {
4754
Spacer()
4855
} footer: {
4956
HeaderButtonsView(buttons: model.headerButtons, action: model.onSelectHeaderButton(type:))
50-
.padding(.top, .medium)
57+
.padding(.top, .small)
5158
}
5259
.frame(maxWidth: .infinity)
5360
.textCase(nil)
@@ -67,6 +74,12 @@ extension CollectibleScene {
6774
])
6875
}
6976

77+
private var statusSectionView: some View {
78+
Section {
79+
AssetStatusView(model: model.scoreViewModel, action: model.onSelectStatus)
80+
}
81+
}
82+
7083
private var assetInfoSectionView: some View {
7184
Section {
7285
ListItemView(

Features/NFT/Sources/Scenes/CollectionsScene.swift

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import Primitives
77
import Store
88
import Components
99
import Style
10-
import Localization
1110
import PrimitivesComponents
1211

1312
public struct CollectionsScene<ViewModel: CollectionsViewable>: View {
@@ -19,21 +18,14 @@ public struct CollectionsScene<ViewModel: CollectionsViewable>: View {
1918

2019
public var body: some View {
2120
ScrollView {
22-
VStack(spacing: Spacing.medium) {
23-
LazyVGrid(columns: model.columns) {
24-
gridItemsView
25-
}
26-
27-
if let unverifiedCount = model.content.unverifiedCount {
28-
unverifiedSection(unverifiedCount)
29-
}
30-
Spacer()
21+
LazyVGrid(columns: model.columns) {
22+
gridItemsView
3123
}
32-
.padding(.horizontal, .medium)
24+
.padding(.horizontal, Spacing.medium)
3325
}
3426
.observeQuery(request: $model.request, value: $model.nftDataList)
3527
.overlay {
36-
if model.content.items.isEmpty {
28+
if model.items.isEmpty {
3729
EmptyContentView(model: model.emptyContentModel)
3830
}
3931
}
@@ -49,27 +41,13 @@ public struct CollectionsScene<ViewModel: CollectionsViewable>: View {
4941

5042
extension CollectionsScene {
5143
private var gridItemsView: some View {
52-
ForEach(model.content.items) { gridItem in
53-
NavigationLink(value: gridItem.destination) {
44+
ForEach(model.items) { item in
45+
NavigationLink(value: item.destination) {
5446
GridPosterView(
55-
assetImage: gridItem.assetImage,
56-
title: gridItem.title
57-
)
58-
}
59-
}
60-
}
61-
62-
private func unverifiedSection(_ count: String) -> some View {
63-
NavigationLink(value: Scenes.UnverifiedCollections()) {
64-
HStack {
65-
ListItemView(
66-
title: Localized.Asset.Verification.unverified,
67-
subtitle: count,
68-
imageStyle: .list(assetImage: .image(Images.TokenStatus.warning))
47+
assetImage: item.assetImage,
48+
title: item.title,
49+
count: item.count
6950
)
70-
Spacer()
71-
Images.System.chevronRight
72-
.foregroundColor(Colors.gray)
7351
}
7452
}
7553
}

Features/NFT/Sources/Types/GridContent.swift

Lines changed: 0 additions & 13 deletions
This file was deleted.

Features/NFT/Sources/Types/GridPosterViewItem.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,19 @@ public struct GridPosterViewItem: Identifiable, Sendable {
88
public let destination: any Hashable & Sendable
99
public let assetImage: AssetImage
1010
public let title: String
11+
public let count: Int?
1112

12-
public init(id: String, destination: any Hashable & Sendable, assetImage: AssetImage, title: String) {
13+
public init(
14+
id: String,
15+
destination: any Hashable & Sendable,
16+
assetImage: AssetImage,
17+
title: String,
18+
count: Int? = nil
19+
) {
1320
self.id = id
1421
self.destination = destination
1522
self.assetImage = assetImage
1623
self.title = title
24+
self.count = count
1725
}
1826
}

Features/NFT/Sources/ViewModels/CollectibleViewModel.swift

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Photos
1212
import AvatarService
1313
import Formatters
1414
import ExplorerService
15+
import InfoSheet
1516

1617
@Observable
1718
@MainActor
@@ -25,6 +26,7 @@ public final class CollectibleViewModel {
2526
var isPresentingToast: ToastMessage?
2627
var isPresentingTokenExplorerUrl: URL?
2728
var isPresentingSelectedAssetInput: Binding<SelectedAssetInput?>
29+
var isPresentingInfoSheet: InfoSheetType?
2830

2931
public init(
3032
wallet: Wallet,
@@ -50,9 +52,7 @@ public final class CollectibleViewModel {
5052
var networkText: String { assetData.asset.chain.asset.name }
5153

5254
var contractTitle: String { Localized.Asset.contract }
53-
var contractValue: String {
54-
assetData.collection.contractAddress
55-
}
55+
var contractValue: String { assetData.collection.contractAddress }
5656

5757
var contractText: String? {
5858
if contractValue.isEmpty || contractValue == assetData.asset.tokenId {
@@ -94,7 +94,7 @@ public final class CollectibleViewModel {
9494
let enabledChainTypes: Set<ChainType> = [ChainType.ethereum]
9595

9696
var headerButtons: [HeaderButton] {
97-
return [
97+
[
9898
HeaderButton(
9999
type: .send,
100100
isEnabled: assetData.asset.chain.isNFTSupported && enabledChainTypes
@@ -115,11 +115,19 @@ public final class CollectibleViewModel {
115115
}
116116

117117
var showAttributes: Bool {
118-
!attributes.isEmpty
118+
attributes.isNotEmpty
119119
}
120120

121121
var showLinks: Bool {
122-
!assetData.collection.links.isEmpty
122+
assetData.collection.links.isNotEmpty
123+
}
124+
125+
var scoreViewModel: AssetScoreTypeViewModel {
126+
AssetScoreTypeViewModel(scoreType: .unverified)
127+
}
128+
129+
var showStatus: Bool {
130+
assetData.collection.isVerified == false
123131
}
124132

125133
var socialLinksViewModel: SocialLinksViewModel {
@@ -209,6 +217,10 @@ extension CollectibleViewModel {
209217
guard let url = URL(string: explorerLink.link) else { return }
210218
isPresentingTokenExplorerUrl = url
211219
}
220+
221+
func onSelectStatus() {
222+
isPresentingInfoSheet = .assetStatus(scoreViewModel.scoreType)
223+
}
212224
}
213225

214226
// MARK: - Private

Features/NFT/Sources/ViewModels/CollectionViewModel.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ public final class CollectionViewModel: CollectionsViewable, Sendable {
3131

3232
public var title: String { collectionName }
3333

34-
public var content: GridContent {
35-
let items = nftDataList.flatMap { data in
34+
public var items: [GridPosterViewItem] {
35+
nftDataList.flatMap { data in
3636
data.assets.map { buildGridItem(collection: data.collection, asset: $0) }
3737
}
38-
return GridContent(items: items)
3938
}
4039

4140
}

Features/NFT/Sources/ViewModels/CollectionsViewModel.swift

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import NFTService
66
import Primitives
77
import Store
88
import Localization
9-
import Style
109
import SwiftUI
1110
import PrimitivesComponents
1211
import WalletService
12+
import Style
1313

1414
@Observable
1515
@MainActor
@@ -41,16 +41,25 @@ public final class CollectionsViewModel: CollectionsViewable, Sendable {
4141
walletService.currentWallet
4242
}
4343

44-
public var content: GridContent {
45-
let items = nftDataList
44+
public var items: [GridPosterViewItem] {
45+
verifiedItems + unverifiedItem
46+
}
47+
48+
// MARK: - Private
49+
50+
private var verifiedItems: [GridPosterViewItem] {
51+
nftDataList
4652
.filter { $0.collection.isVerified }
4753
.map { buildGridItem(from: $0) }
54+
}
4855

49-
let unverifiedCount = nftDataList
56+
private var unverifiedItem: [GridPosterViewItem] {
57+
let ids = nftDataList
5058
.filter { $0.collection.isVerified == false }
51-
.count
59+
.map(\.collection.id)
5260

53-
return GridContent(items: items, unverifiedCount: unverifiedCount > 0 ? String(unverifiedCount) : nil)
61+
guard ids.isNotEmpty else { return [] }
62+
return [.unverified(collectionIds: ids)]
5463
}
5564

5665
// MARK: - Actions
@@ -64,3 +73,15 @@ public final class CollectionsViewModel: CollectionsViewable, Sendable {
6473
}
6574
}
6675
}
76+
77+
extension GridPosterViewItem {
78+
static func unverified(collectionIds: [String]) -> GridPosterViewItem {
79+
GridPosterViewItem(
80+
id: collectionIds.joined(separator: "-").hash.asString,
81+
destination: Scenes.UnverifiedCollections(),
82+
assetImage: .image(Images.TokenStatus.warning),
83+
title: Localized.Asset.Verification.unverified,
84+
count: collectionIds.count
85+
)
86+
}
87+
}

0 commit comments

Comments
 (0)