From 1da03ad8a110e74643253a03ed685daab4b7f637 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Fri, 16 Aug 2024 13:41:48 +0200 Subject: [PATCH 01/34] wip --- RadixWallet.xcodeproj/project.pbxproj | 28 +++++++++++++++--- .../ConfirmationView+Configuration.swift | 19 ++++++++++++ .../Components/ConfirmationView.swift} | 22 +++++++++----- .../AccountPreferences+Reducer.swift | 2 +- .../AccountPreferences+View.swift | 2 +- .../Details/FungibleTokenDetails+View.swift | 8 +++-- .../Components/HideAsset/HideAsset+View.swift | 19 ++++++++++++ .../Components/HideAsset/HideAsset.swift | 29 +++++++++++++++++++ 8 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift rename RadixWallet/{Features/AccountPreferencesFeature/Children/HideAccountView.swift => Core/DesignSystem/Components/ConfirmationView.swift} (72%) create mode 100644 RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift create mode 100644 RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 9f55f9d755..3e3ab66123 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -911,11 +911,14 @@ 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 */; }; - 5BBC7DA62C3EFA9700B04BD6 /* HideAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7DA52C3EFA9700B04BD6 /* HideAccountView.swift */; }; 5BBC7DA82C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7DA72C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift */; }; 5BBC7DAA2C403F3400B04BD6 /* AccountCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7DA92C403F3400B04BD6 /* AccountCard.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 */; }; + 5BEB9CD22C6F6B24001FD9D4 /* HideAsset+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD02C6F6B24001FD9D4 /* HideAsset+View.swift */; }; + 5BEB9CD32C6F6B24001FD9D4 /* HideAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */; }; + 5BEB9CD52C6F6CC9001FD9D4 /* ConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */; }; + 5BEB9CD72C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */; }; 830818482B9F1621002D8351 /* HTTPClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830818472B9F1621002D8351 /* HTTPClient+Live.swift */; }; 8308184A2B9F162B002D8351 /* HTTPClient+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830818492B9F162B002D8351 /* HTTPClient+Mock.swift */; }; 8308184C2B9F169B002D8351 /* TokenPriceClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8308184B2B9F169B002D8351 /* TokenPriceClient+Live.swift */; }; @@ -2061,11 +2064,14 @@ 5BBC7D9B2C3D390D00B04BD6 /* BootstrapClient+Interface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BootstrapClient+Interface.swift"; sourceTree = ""; }; 5BBC7D9C2C3D390D00B04BD6 /* BootstrapClient+Live.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BootstrapClient+Live.swift"; sourceTree = ""; }; 5BBC7DA32C3D442800B04BD6 /* URLRequest+Extra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Extra.swift"; sourceTree = ""; }; - 5BBC7DA52C3EFA9700B04BD6 /* HideAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideAccountView.swift; sourceTree = ""; }; 5BBC7DA72C40278E00B04BD6 /* NonFungibleResourceAsset+Reducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NonFungibleResourceAsset+Reducer.swift"; sourceTree = ""; }; 5BBC7DA92C403F3400B04BD6 /* AccountCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCard.swift; sourceTree = ""; }; 5BC82B6A2BED18A1009AC162 /* FactoryReset+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactoryReset+View.swift"; sourceTree = ""; }; 5BC82B6B2BED18A1009AC162 /* FactoryReset+Reducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactoryReset+Reducer.swift"; sourceTree = ""; }; + 5BEB9CD02C6F6B24001FD9D4 /* HideAsset+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HideAsset+View.swift"; sourceTree = ""; }; + 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideAsset.swift; sourceTree = ""; }; + 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationView.swift; sourceTree = ""; }; + 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationView+Configuration.swift"; sourceTree = ""; }; 830818472B9F1621002D8351 /* HTTPClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPClient+Live.swift"; sourceTree = ""; }; 830818492B9F162B002D8351 /* HTTPClient+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPClient+Mock.swift"; sourceTree = ""; }; 8308184B2B9F169B002D8351 /* TokenPriceClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TokenPriceClient+Live.swift"; sourceTree = ""; }; @@ -2615,7 +2621,6 @@ 48CFBC742ADC10D800E77A5C /* ThirdPartyDeposits */, 48CFBC722ADC10D800E77A5C /* UpdateAccountLabel+Reducer.swift */, 48CFBC732ADC10D800E77A5C /* UpdateAccountLabel+View.swift */, - 5BBC7DA52C3EFA9700B04BD6 /* HideAccountView.swift */, ); path = Children; sourceTree = ""; @@ -3843,6 +3848,7 @@ 48CFBE362ADC10D800E77A5C /* NonFungibleAssetList */, 48CFBE402ADC10D800E77A5C /* FungibleAssetList */, 48CFBE4C2ADC10D800E77A5C /* PoolUnitsList */, + 5BEB9CCF2C6F6AFD001FD9D4 /* HideAsset */, 48CFBE5F2ADC10D800E77A5C /* HelperViews */, 48CFBE662ADC10D800E77A5C /* DetailsContainerWithHeaderView.swift */, ); @@ -5120,6 +5126,8 @@ 5BBC43A82BBAC6B0005747B1 /* AppTextEditor.swift */, 5B6499B92BCFDB1E000F2176 /* ShareView.swift */, A4DCCC4A2C2DA08000438A7B /* GeometryExtensions.swift */, + 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */, + 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */, ); path = Components; sourceTree = ""; @@ -5700,6 +5708,15 @@ path = BootstrapClient; sourceTree = ""; }; + 5BEB9CCF2C6F6AFD001FD9D4 /* HideAsset */ = { + isa = PBXGroup; + children = ( + 5BEB9CD02C6F6B24001FD9D4 /* HideAsset+View.swift */, + 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */, + ); + path = HideAsset; + sourceTree = ""; + }; 830E43AD2C2D8B3800B6E95F /* Mobile */ = { isa = PBXGroup; children = ( @@ -6959,6 +6976,8 @@ 48D5F3982BD8DE0A000DE964 /* DebugInspectProfile.swift in Sources */, 48CFC2872ADC10D900E77A5C /* MinimumPercentageStepper+View.swift in Sources */, E76645A42C23138300065D9A /* Throwable.swift in Sources */, + 5BEB9CD22C6F6B24001FD9D4 /* HideAsset+View.swift in Sources */, + 5BEB9CD32C6F6B24001FD9D4 /* HideAsset.swift in Sources */, 5B272DD72C36E89600B74F1F /* AppEventsClient+Interface.swift in Sources */, 48CFC2A12ADC10D900E77A5C /* PersonaFeature.swift in Sources */, A4DCCC502C2DA32C00438A7B /* HomeCardsClient+Interface.swift in Sources */, @@ -6982,7 +7001,6 @@ 48CFC4F72ADC10DA00E77A5C /* PublicKeyHashType.swift in Sources */, 48CFC4AD2ADC10DA00E77A5C /* FungibleResourcesCollectionItemVaultAggregatedVaultItem.swift in Sources */, 83856D622B0279080026452A /* VerifyMnemonic+View.swift in Sources */, - 5BBC7DA62C3EFA9700B04BD6 /* HideAccountView.swift in Sources */, 48CFC4332ADC10DA00E77A5C /* Either+Extra.swift in Sources */, 48CFC2C72ADC10D900E77A5C /* MessageMode+View.swift in Sources */, E6FA984A2B04B9AC00748F20 /* ConfirmSkippingBDFS+View.swift in Sources */, @@ -7012,6 +7030,7 @@ 48CFC4022ADC10D900E77A5C /* KeychainClient+Interface.swift in Sources */, 48CFC44E2ADC10DA00E77A5C /* AppPreferencesClient+Interface.swift in Sources */, 48CFC5432ADC10DA00E77A5C /* EntityMetadataCollection.swift in Sources */, + 5BEB9CD52C6F6CC9001FD9D4 /* ConfirmationView.swift in Sources */, 48CFC3442ADC10D900E77A5C /* AccountPermission.swift in Sources */, 83EE5EE92BE3C16F00B1531D /* StatePackageCodePageRequest.swift in Sources */, 48CFC5E82ADC10DA00E77A5C /* FeatureReducer.swift in Sources */, @@ -7059,6 +7078,7 @@ A4EBB5D62BD0777C00D05FDE /* ConfigurationBackup+Reducer.swift in Sources */, A41557532B7645E70040AD4E /* TransactionHistory+Reducer.swift in Sources */, E6A2D9E72AFA7132001857EC /* Mnemonics+Utils.swift in Sources */, + 5BEB9CD72C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift in Sources */, 83EE5EF32BE3C16F00B1531D /* StateAccountResourcePreferencesPageResponse.swift in Sources */, 48CFC28A2ADC10D900E77A5C /* TransactionReviewGuarantees.swift in Sources */, 48CFC5772ADC10DA00E77A5C /* MetadataTypedValue.swift in Sources */, diff --git a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift new file mode 100644 index 0000000000..3f55171c8f --- /dev/null +++ b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift @@ -0,0 +1,19 @@ +import Foundation + +extension ConfirmationView.Configuration { + static var hideAccount: Self { + .init( + title: L10n.AccountSettings.HideAccount.title, + message: L10n.AccountSettings.HideAccount.message, + primaryAction: L10n.AccountSettings.HideAccount.button + ) + } + + static var hideAsset: Self { + .init( + title: "Hide Asset", + message: "Hide this asset in your Radix Wallet? You can always unhide it in your account settings.", + primaryAction: "Hide Asset" + ) + } +} diff --git a/RadixWallet/Features/AccountPreferencesFeature/Children/HideAccountView.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift similarity index 72% rename from RadixWallet/Features/AccountPreferencesFeature/Children/HideAccountView.swift rename to RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift index eff5940ccd..5f9192b8bc 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/Children/HideAccountView.swift +++ b/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift @@ -1,9 +1,10 @@ import SwiftUI -public typealias HideAccountAction = HideAccountView.Action +public typealias ConfirmationAction = ConfirmationView.Action -// MARK: - HideAccountView -public struct HideAccountView: View { +// MARK: - ConfirmationView +public struct ConfirmationView: View { + let configuration: Configuration let onAction: (Action) -> Void public var body: some View { @@ -26,11 +27,11 @@ public struct HideAccountView: View { .frame(.small) .foregroundColor(.app.gray3) - Text(L10n.AccountSettings.HideAccount.title) + Text(configuration.title) .textStyle(.sheetTitle) .foregroundColor(.app.gray1) - Text(L10n.AccountSettings.HideAccount.message) + Text(configuration.message) .textStyle(.body1Regular) .foregroundColor(.app.gray1) } @@ -44,7 +45,7 @@ public struct HideAccountView: View { } .buttonStyle(.secondaryRectangular) - Button(L10n.AccountSettings.HideAccount.button) { + Button(configuration.primaryAction) { onAction(.confirm) } .buttonStyle(.primaryRectangular) @@ -56,8 +57,13 @@ public struct HideAccountView: View { } } -// MARK: HideAccountView.Action -extension HideAccountView { +extension ConfirmationView { + public struct Configuration { + let title: String + let message: String + let primaryAction: String + } + public enum Action: Sendable { case cancel case confirm diff --git a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift index 5fba615ef3..466069882f 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift @@ -57,7 +57,7 @@ public struct AccountPreferences: Sendable, FeatureReducer { case updateAccountLabel(UpdateAccountLabel.Action) case thirdPartyDeposits(ManageThirdPartyDeposits.Action) case devPreferences(DevAccountPreferences.Action) - case hideAccount(HideAccountAction) + case hideAccount(ConfirmationAction) } public var body: some ReducerOf { diff --git a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift index 35c079954e..f0d2bf892b 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift @@ -153,7 +153,7 @@ private extension View { private func hideAccount(with destinationStore: PresentationStoreOf, store: StoreOf) -> some View { sheet(store: destinationStore.scope(state: \.hideAccount, action: \.hideAccount)) { _ in - HideAccountView { action in + ConfirmationView(configuration: .hideAccount) { action in store.send(.destination(.presented(.hideAccount(action)))) } } diff --git a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift index c5c783a940..372092af6b 100644 --- a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift @@ -61,8 +61,12 @@ extension FungibleTokenDetails { } thumbnailView: { Thumbnail(token: viewStore.thumbnail.wrappedValue ?? .other(nil), size: .veryLarge) } detailsView: { - AssetResourceDetailsSection(viewState: viewStore.details) - .padding(.bottom, .medium1) + VStack(spacing: .medium1) { + AssetResourceDetailsSection(viewState: viewStore.details) + +// HideAssetButton() + } + .padding(.bottom, .medium1) } .task { @MainActor in await viewStore.send(.task).finish() diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift new file mode 100644 index 0000000000..e8785590da --- /dev/null +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift @@ -0,0 +1,19 @@ + +// MARK: - HideAsset.View +public extension HideAsset { + struct View: SwiftUI.View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some SwiftUI.View { + Button("Hide Asset") { + store.send(.view(.buttonTapped)) + } + .buttonStyle(.secondaryRectangular(shouldExpand: true)) + .padding(.horizontal, .medium3) + } + } +} diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift new file mode 100644 index 0000000000..fbf177b2b9 --- /dev/null +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -0,0 +1,29 @@ +// MARK: - HideAsset +@Reducer +public struct HideAsset: Sendable, FeatureReducer { + @ObservableState + public struct State: Sendable, Hashable { + public init() {} + } + + public typealias Action = FeatureAction + + public enum ViewAction: Sendable, Equatable { + case buttonTapped + } + + public enum DelegateAction: Sendable, Equatable { + case didHideAsset + } + + public var body: some ReducerOf { + Reduce(core) + } + + public func reduce(into _: inout State, viewAction: ViewAction) -> Effect { + switch viewAction { + case .buttonTapped: + .none + } + } +} From 688b5075ea9abf48bb685a6b1d4e7e0c5366562e Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Fri, 16 Aug 2024 14:58:20 +0200 Subject: [PATCH 02/34] Bump Sargon --- RadixWallet.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 3e3ab66123..9a6300860c 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -8737,7 +8737,7 @@ repositoryURL = "https://github.com/radixdlt/sargon"; requirement = { kind = exactVersion; - version = 1.0.34; + version = 1.0.38; }; }; A415574E2B757C5E0040AD4E /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { diff --git a/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 88fbc5dd3f..a3585cbf05 100644 --- a/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -114,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/radixdlt/sargon", "state" : { - "revision" : "823d811c9e9d0093977ea9cb20ff4ba2f5e77747", - "version" : "1.0.34" + "revision" : "e028504a8f6a167c3eb0b7098a8b3a8b728483d3", + "version" : "1.0.38" } }, { From 4d93e418e3976435c18729c1e1f19f0138152c66 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Fri, 16 Aug 2024 16:20:57 +0200 Subject: [PATCH 03/34] wip --- .../ConfirmationView+Configuration.swift | 6 +- .../FungibleTokenDetails+Reducer.swift | 15 ++++ .../Details/FungibleTokenDetails+View.swift | 8 ++- .../Components/HideAsset/HideAsset+View.swift | 15 ++-- .../Components/HideAsset/HideAsset.swift | 70 ++++++++++++++++++- 5 files changed, 104 insertions(+), 10 deletions(-) diff --git a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift index 3f55171c8f..f9f9b79a59 100644 --- a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift +++ b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift @@ -11,9 +11,9 @@ extension ConfirmationView.Configuration { static var hideAsset: Self { .init( - title: "Hide Asset", - message: "Hide this asset in your Radix Wallet? You can always unhide it in your account settings.", - primaryAction: "Hide Asset" + title: L10n.AssetDetails.HideAsset.title, + message: L10n.AssetDetails.HideAsset.message, + primaryAction: L10n.AssetDetails.HideAsset.button ) } } diff --git a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift index 5d03c04244..3a64b8bbd6 100644 --- a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift @@ -9,6 +9,7 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { let isXRD: Bool let ownedFungibleResource: OnLedgerEntity.OwnedFungibleResource? let ledgerState: AtLedgerState? + var hideAsset: HideAsset.State public init( resourceAddress: ResourceAddress, @@ -22,6 +23,7 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { self.ownedFungibleResource = ownedFungibleResource self.isXRD = isXRD self.ledgerState = ledgerState + self.hideAsset = .init(asset: .fungible(resourceAddress)) } } @@ -34,12 +36,25 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { case resourceLoadResult(TaskResult) } + @CasePathable + public enum ChildAction: Sendable, Equatable { + case hideAsset(HideAsset.Action) + } + @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient @Dependency(\.errorQueue) var errorQueue @Dependency(\.dismiss) var dismiss public init() {} + public var body: some ReducerOf { + Scope(state: \.hideAsset, action: \.child.hideAsset) { + HideAsset() + } + + Reduce(core) + } + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .task: diff --git a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift index 372092af6b..c435c45b9c 100644 --- a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift @@ -64,7 +64,7 @@ extension FungibleTokenDetails { VStack(spacing: .medium1) { AssetResourceDetailsSection(viewState: viewStore.details) -// HideAssetButton() + HideAsset.View(store: store.hideAsset) } .padding(.bottom, .medium1) } @@ -75,3 +75,9 @@ extension FungibleTokenDetails { } } } + +private extension StoreOf { + var hideAsset: StoreOf { + scope(state: \.hideAsset, action: \.child.hideAsset) + } +} diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift index e8785590da..686b74b35c 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift @@ -9,11 +9,18 @@ public extension HideAsset { } public var body: some SwiftUI.View { - Button("Hide Asset") { - store.send(.view(.buttonTapped)) + WithPerceptionTracking { + if store.shouldShow { + Button(L10n.AssetDetails.HideAsset.button) { + store.send(.view(.buttonTapped)) + } + .buttonStyle(.secondaryRectangular(shouldExpand: true)) + .padding(.horizontal, .medium3) + } + } + .task { + await store.send(.view(.task)).finish() } - .buttonStyle(.secondaryRectangular(shouldExpand: true)) - .padding(.horizontal, .medium3) } } } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index fbf177b2b9..4d27482935 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -3,12 +3,28 @@ public struct HideAsset: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { - public init() {} + let asset: AssetAddress + var isAlreadyHidden = false + var isXrd = false + + public init(asset: AssetAddress) { + self.asset = asset + } + + var shouldShow: Bool { + !(isAlreadyHidden || isXrd) + } } public typealias Action = FeatureAction + public enum InternalAction: Sendable, Equatable { + case setIsXrd(Bool) + case setIsAlreadyHidden(Bool) + } + public enum ViewAction: Sendable, Equatable { + case task case buttonTapped } @@ -16,14 +32,64 @@ public struct HideAsset: Sendable, FeatureReducer { case didHideAsset } + @Dependency(\.appPreferencesClient) var appPreferencesClient + @Dependency(\.gatewaysClient) var gatewaysClient + public var body: some ReducerOf { Reduce(core) } - public func reduce(into _: inout State, viewAction: ViewAction) -> Effect { + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { + case .task: + isXrdEffect(state: state) + .merge(with: isAlreadyHiddenEffect(state: state)) + case .buttonTapped: .none } } + + public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { + switch internalAction { + case let .setIsXrd(value): + state.isXrd = value + return .none + case let .setIsAlreadyHidden(value): + state.isAlreadyHidden = value + return .none + } + } + + private func isXrdEffect(state: State) -> Effect { + .run { send in + switch state.asset { + case let .fungible(resource): + let networkId = await gatewaysClient.getCurrentNetworkID() + await send(.internal(.setIsXrd(resource.isXRD(on: networkId)))) + case .nonFungible, .poolUnit: + await send(.internal(.setIsXrd(false))) + } + } + } + + private func isAlreadyHiddenEffect(state: State) -> Effect { + .run { send in + let hiddenAssets = await appPreferencesClient.getPreferences().assets + let isAlreadyHidden = hiddenAssets.contains(where: { $0.assetAddress == state.asset }) + await send(.internal(.setIsAlreadyHidden(isAlreadyHidden))) + } + } +} + +extension AppPreferencesClient { + func shouldAllowHiding(asset: AssetAddress) async -> Bool { + switch asset { + case let .fungible(resourceAddress): + break + case .nonFungible, .poolUnit: + break + } + return false + } } From ec84bd06f427ad850ca03b799318c9f4c2276a48 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Fri, 16 Aug 2024 16:42:50 +0200 Subject: [PATCH 04/34] hide asset (need to filter on every list now) --- .../FungibleTokenDetails+Reducer.swift | 9 +++ .../Components/HideAsset/HideAsset+View.swift | 28 +++++++- .../Components/HideAsset/HideAsset.swift | 66 ++++++++++++++----- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift index 3a64b8bbd6..88a91e91fb 100644 --- a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift @@ -82,4 +82,13 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { return .none } } + + public func reduce(into state: inout State, childAction: ChildAction) -> Effect { + switch childAction { + case .hideAsset(.delegate(.didHideAsset)): + .run { _ in await dismiss() } + default: + .none + } + } } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift index 686b74b35c..dc27f69edf 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift @@ -1,5 +1,5 @@ - // MARK: - HideAsset.View + public extension HideAsset { struct View: SwiftUI.View { private let store: StoreOf @@ -21,6 +21,32 @@ public extension HideAsset { .task { await store.send(.view(.task)).finish() } + .destination(store: store) + } + } +} + +private extension StoreOf { + var destination: PresentationStoreOf { + func scopeState(state: State) -> PresentationState { + state.$destination + } + return scope(state: scopeState, action: Action.destination) + } +} + +@MainActor +private extension View { + func destination(store: StoreOf) -> some View { + let destinationStore = store.destination + return confirmation(with: destinationStore, store: store) + } + + private func confirmation(with destinationStore: PresentationStoreOf, store: StoreOf) -> some View { + sheet(store: destinationStore.scope(state: \.confirmation, action: \.confirmation)) { _ in + ConfirmationView(configuration: .hideAsset) { action in + store.send(.destination(.presented(.confirmation(action)))) + } } } } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index 4d27482935..7552534ac7 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -4,8 +4,11 @@ public struct HideAsset: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { let asset: AssetAddress - var isAlreadyHidden = false - var isXrd = false + fileprivate var isAlreadyHidden = false + fileprivate var isXrd = false + + @Presents + var destination: Destination.State? = nil public init(asset: AssetAddress) { self.asset = asset @@ -32,21 +35,44 @@ public struct HideAsset: Sendable, FeatureReducer { case didHideAsset } + // MARK: - Destination + public struct Destination: DestinationReducer { + @CasePathable + public enum State: Hashable, Sendable { + case confirmation + } + + @CasePathable + public enum Action: Equatable, Sendable { + case confirmation(ConfirmationAction) + } + + public var body: some ReducerOf { + EmptyReducer() + } + } + @Dependency(\.appPreferencesClient) var appPreferencesClient @Dependency(\.gatewaysClient) var gatewaysClient public var body: some ReducerOf { Reduce(core) + .ifLet(destinationPath, action: /Action.destination) { + Destination() + } } + private let destinationPath: WritableKeyPath> = \.$destination + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .task: - isXrdEffect(state: state) + return isXrdEffect(state: state) .merge(with: isAlreadyHiddenEffect(state: state)) case .buttonTapped: - .none + state.destination = .confirmation + return .none } } @@ -61,7 +87,20 @@ public struct HideAsset: Sendable, FeatureReducer { } } - private func isXrdEffect(state: State) -> Effect { + public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect { + switch presentedAction { + case .confirmation(.confirm): + state.destination = nil + return hideAssetEffect(state: state) + case .confirmation(.cancel): + state.destination = nil + return .none + } + } +} + +private extension HideAsset { + func isXrdEffect(state: State) -> Effect { .run { send in switch state.asset { case let .fungible(resource): @@ -73,23 +112,20 @@ public struct HideAsset: Sendable, FeatureReducer { } } - private func isAlreadyHiddenEffect(state: State) -> Effect { + func isAlreadyHiddenEffect(state: State) -> Effect { .run { send in let hiddenAssets = await appPreferencesClient.getPreferences().assets let isAlreadyHidden = hiddenAssets.contains(where: { $0.assetAddress == state.asset }) await send(.internal(.setIsAlreadyHidden(isAlreadyHidden))) } } -} -extension AppPreferencesClient { - func shouldAllowHiding(asset: AssetAddress) async -> Bool { - switch asset { - case let .fungible(resourceAddress): - break - case .nonFungible, .poolUnit: - break + func hideAssetEffect(state: State) -> Effect { + .run { send in + try await appPreferencesClient.updating { preferences in + preferences.assets.hideAsset(asset: state.asset) + } + await send(.delegate(.didHideAsset)) } - return false } } From dde886d1498e99df48b58b62b5d920788ff96631 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 12:24:08 +0200 Subject: [PATCH 05/34] filter fungible resources --- .../AccountPortfoliosClient+Live.swift | 13 ++++++++----- .../AccountPortfoliosClient+State.swift | 8 ++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index dcb419ae70..a707b324c5 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -80,13 +80,15 @@ extension AccountPortfoliosClient: DependencyKey { /// Explicetely load and set the currency target and visibility to make sure /// it is available for usage before resources are loaded - let preferences = await appPreferencesClient.getPreferences().display - await state.setSelectedCurrency(preferences.fiatCurrencyPriceTarget) - await state.setIsCurrencyAmountVisble(preferences.isCurrencyAmountVisible) + let preferences = await appPreferencesClient.getPreferences() + let display = preferences.display + await state.setSelectedCurrency(display.fiatCurrencyPriceTarget) + await state.setIsCurrencyAmountVisble(display.isCurrencyAmountVisible) let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses) + let hiddenAssets = preferences.assets.hiddenAssets - let portfolios = accounts.map { AccountPortfolio(account: $0) } + let portfolios = accounts.map { AccountPortfolio(account: $0, hiddenAssets: hiddenAssets) } await state.handlePortfoliosUpdate(portfolios) /// Put together all resources from already fetched and new accounts @@ -140,7 +142,8 @@ extension AccountPortfoliosClient: DependencyKey { } let account = try await onLedgerEntitiesClient.getAccount(accountAddress) - let portfolio = AccountPortfolio(account: account) + let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + let portfolio = AccountPortfolio(account: account, hiddenAssets: hiddenAssets) if case let .success(tokenPrices) = await state.tokenPrices { await applyTokenPrices( diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift index 606085b865..65bf09f93c 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift @@ -12,6 +12,14 @@ extension AccountPortfoliosClient { public var debugDescription: String { account.debugDescription } + + init(account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [AssetAddress]) { + var modified = account + modified.fungibleResources.nonXrdResources.removeAll(where: { resource in + hiddenAssets.contains(.fungible(resource.resourceAddress)) + }) + self.account = modified + } } /// Internal state that holds all loaded portfolios. From d1a2fc47ad6c49256401c3f92f854399aadf9c8e Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 13:00:08 +0200 Subject: [PATCH 06/34] show button on Pool Units and NFTs --- .../NonFungibleTokenDetails+Reducer.swift | 25 +++++++++++++++++ .../NonFungibleTokenDetails+View.swift | 5 ++++ .../Components/PoolUnitDetails+View.swift | 8 ++++++ .../Components/PoolUnitDetails.swift | 28 +++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift index dfbfd5e120..37dcd11d4d 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift @@ -11,6 +11,7 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { public let ledgerState: AtLedgerState public let stakeClaim: OnLedgerEntitiesClient.StakeClaim? public let isClaimStakeEnabled: Bool + var hideAsset: HideAsset.State? public init( resourceAddress: ResourceAddress, @@ -28,6 +29,9 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { self.ledgerState = ledgerState self.stakeClaim = stakeClaim self.isClaimStakeEnabled = isClaimStakeEnabled + if let id = token?.id, stakeClaim == nil { + hideAsset = .init(asset: .nonFungible(id)) + } } } @@ -45,11 +49,23 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { case tappedClaimStake(OnLedgerEntitiesClient.StakeClaim) } + @CasePathable + public enum ChildAction: Sendable, Equatable { + case hideAsset(HideAsset.Action) + } + @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient @Dependency(\.dismiss) var dismiss public init() {} + public var body: some ReducerOf { + Reduce(core) + .ifLet(\.hideAsset, action: \.child.hideAsset) { + HideAsset() + } + } + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .task: @@ -83,4 +99,13 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { return .none } } + + public func reduce(into state: inout State, childAction: ChildAction) -> Effect { + switch childAction { + case .hideAsset(.delegate(.didHideAsset)): + .run { _ in await dismiss() } + default: + .none + } + } } diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift index 39309e3518..a4860cee04 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift @@ -122,6 +122,11 @@ extension NonFungibleTokenDetails { } .padding(.vertical, .medium1) .background(.app.gray5, ignoresSafeAreaEdges: .bottom) + + IfLetStore(store.scope(state: \.hideAsset, action: \.child.hideAsset)) { store in + HideAsset.View(store: store) + .padding(.vertical, .medium1) + } } } .foregroundColor(.app.gray1) diff --git a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift index 2b7d1ca577..f36b2ecac7 100644 --- a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift @@ -61,6 +61,8 @@ extension PoolUnitDetails { .padding(.horizontal, .large2) AssetResourceDetailsSection(viewState: viewStore.resourceDetails) + + HideAsset.View(store: store.hideAsset) } .padding(.bottom, .medium1) } @@ -68,3 +70,9 @@ extension PoolUnitDetails { } } } + +private extension StoreOf { + var hideAsset: StoreOf { + scope(state: \.hideAsset, action: \.child.hideAsset) + } +} diff --git a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift index ac6018bda2..78e9c7a0d5 100644 --- a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift +++ b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift @@ -5,6 +5,12 @@ import SwiftUI public struct PoolUnitDetails: Sendable, FeatureReducer { public struct State: Sendable, Hashable { let resourcesDetails: OnLedgerEntitiesClient.OwnedResourcePoolDetails + var hideAsset: HideAsset.State + + public init(resourcesDetails: OnLedgerEntitiesClient.OwnedResourcePoolDetails) { + self.resourcesDetails = resourcesDetails + self.hideAsset = .init(asset: .poolUnit(resourcesDetails.address)) + } } @Dependency(\.dismiss) var dismiss @@ -13,6 +19,19 @@ public struct PoolUnitDetails: Sendable, FeatureReducer { case closeButtonTapped } + @CasePathable + public enum ChildAction: Sendable, Equatable { + case hideAsset(HideAsset.Action) + } + + public var body: some ReducerOf { + Scope(state: \.hideAsset, action: \.child.hideAsset) { + HideAsset() + } + + Reduce(core) + } + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .closeButtonTapped: @@ -21,4 +40,13 @@ public struct PoolUnitDetails: Sendable, FeatureReducer { } } } + + public func reduce(into state: inout State, childAction: ChildAction) -> Effect { + switch childAction { + case .hideAsset(.delegate(.didHideAsset)): + .run { _ in await dismiss() } + default: + .none + } + } } From dacddddf61dbbfcb26903cdcc9f733fc88d6e09c Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 13:41:17 +0200 Subject: [PATCH 07/34] filter pool units and nfts --- .../AccountPortfoliosClient+Live.swift | 8 +++--- .../AccountPortfoliosClient+State.swift | 27 +++++++++++++++++++ .../OnLedgerEntitiesClient+Interface.swift | 5 +++- .../SharedModels/Assets/OnLedgerEntity.swift | 4 +-- .../Row/NonFungbileAssetRow+Reducer.swift | 15 ++++++++++- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index a707b324c5..57f06ad0a6 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -27,10 +27,10 @@ extension AccountPortfoliosClient: DependencyKey { /// Fetches the pool and stake units details for a given account; Will update the portfolio accordingly @Sendable - func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { + func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [AssetAddress], cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { async let poolDetailsFetch = Task { do { - let poolUnitDetails = try await onLedgerEntitiesClient.getOwnedPoolUnitsDetails(account, cachingStrategy: cachingStrategy) + let poolUnitDetails = try await onLedgerEntitiesClient.getOwnedPoolUnitsDetails(account, hiddenAssets: hiddenAssets, cachingStrategy: cachingStrategy) await state.set(poolDetails: .success(poolUnitDetails), forAccount: account.address) } catch { await state.set(poolDetails: .failure(error), forAccount: account.address) @@ -126,7 +126,7 @@ extension AccountPortfoliosClient: DependencyKey { // Load additional details _ = await accounts.map(\.nonEmptyVaults).parallelMap { - await fetchPoolAndStakeUnitsDetails($0, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) + await fetchPoolAndStakeUnitsDetails($0, hiddenAssets: hiddenAssets, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) } return Array(state.portfoliosSubject.value.wrappedValue!.values) @@ -153,7 +153,7 @@ extension AccountPortfoliosClient: DependencyKey { } await state.handlePortfolioUpdate(portfolio) - await fetchPoolAndStakeUnitsDetails(account.nonEmptyVaults, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) + await fetchPoolAndStakeUnitsDetails(account.nonEmptyVaults, hiddenAssets: hiddenAssets, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) return portfolio } diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift index 65bf09f93c..ae9afa3a4e 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift @@ -4,6 +4,7 @@ import Foundation extension AccountPortfoliosClient { public struct AccountPortfolio: Sendable, Hashable, CustomDebugStringConvertible { public var account: OnLedgerEntity.OnLedgerAccount + public let hiddenAssets: [AssetAddress] public var poolUnitDetails: Loadable<[OnLedgerEntitiesClient.OwnedResourcePoolDetails]> = .idle public var stakeUnitDetails: Loadable> = .idle @@ -15,10 +16,36 @@ extension AccountPortfoliosClient { init(account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [AssetAddress]) { var modified = account + + // Remove every hidden fungible resource modified.fungibleResources.nonXrdResources.removeAll(where: { resource in hiddenAssets.contains(.fungible(resource.resourceAddress)) }) + + // Remove every hidden pool unit + modified.poolUnitResources.poolUnits.removeAll(where: { poolUnit in + hiddenAssets.contains(.poolUnit(poolUnit.resourcePoolAddress)) + }) + + // We cannot remove the NFTs, since at this moment we haven't loaded them yet + // All we can do is decrease the count for each resource + var nonFungibleResources: [OnLedgerEntity.OwnedNonFungibleResource] = [] + for var item in modified.nonFungibleResources { + let countHidden = hiddenAssets.filter { asset in + switch asset { + case let .nonFungible(globalId): + globalId.resourceAddress == item.resourceAddress + case .fungible, .poolUnit: + false + } + }.count + item.nonFungibleIdsCount -= countHidden + nonFungibleResources.append(item) + } + modified.nonFungibleResources = nonFungibleResources + self.account = modified + self.hiddenAssets = hiddenAssets } } diff --git a/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift b/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift index c07bd4bb75..fb06533d6e 100644 --- a/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift +++ b/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift @@ -534,9 +534,12 @@ extension OnLedgerEntitiesClient { @Sendable public func getOwnedPoolUnitsDetails( _ account: OnLedgerEntity.OnLedgerAccount, + hiddenAssets: [AssetAddress], cachingStrategy: CachingStrategy = .useCache ) async throws -> [OwnedResourcePoolDetails] { - let ownedPoolUnits = account.poolUnitResources.poolUnits + let ownedPoolUnits = account.poolUnitResources.poolUnits.filter { poolUnit in + !hiddenAssets.contains(.poolUnit(poolUnit.resourcePoolAddress)) + } let pools = try await getEntities( ownedPoolUnits.map(\.resourcePoolAddress).map(\.asGeneral), [.dappDefinition], diff --git a/RadixWallet/Core/SharedModels/Assets/OnLedgerEntity.swift b/RadixWallet/Core/SharedModels/Assets/OnLedgerEntity.swift index 567e2497b9..4d01ad1697 100644 --- a/RadixWallet/Core/SharedModels/Assets/OnLedgerEntity.swift +++ b/RadixWallet/Core/SharedModels/Assets/OnLedgerEntity.swift @@ -384,7 +384,7 @@ extension OnLedgerEntity { public let resourceAddress: ResourceAddress public let atLedgerState: AtLedgerState public let metadata: Metadata - public let nonFungibleIdsCount: Int + public var nonFungibleIdsCount: Int /// The vault where the owned ids are stored public let vaultAddress: VaultAddress @@ -519,7 +519,7 @@ extension OnLedgerEntity { extension OnLedgerEntity.OnLedgerAccount { public struct PoolUnitResources: Sendable, Hashable, Codable { public var radixNetworkStakes: IdentifiedArrayOf - public let poolUnits: [PoolUnit] + public var poolUnits: [PoolUnit] } public struct RadixNetworkStake: Sendable, Hashable, Codable, Identifiable, CustomDebugStringConvertible { diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift index 3fc900817b..e5c43307e8 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift @@ -1,6 +1,7 @@ import ComposableArchitecture import SwiftUI +// MARK: - NonFungibleAssetList.Row extension NonFungibleAssetList { public struct Row: Sendable, FeatureReducer { public struct State: Sendable, Hashable, Identifiable { @@ -63,6 +64,7 @@ extension NonFungibleAssetList { } @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient + @Dependency(\.appPreferencesClient) var appPreferencesClient @Dependency(\.errorQueue) var errorQueue public init() {} @@ -135,7 +137,8 @@ extension NonFungibleAssetList { return .run { [resource = state.resource, accountAddress = state.accountAddress] send in let result = await TaskResult { let data = try await onLedgerEntitiesClient.getAccountOwnedNonFungibleTokenData(.init(accountAddress: accountAddress, resource: resource, mode: .loadPage(pageCursor: cursor))) - return InternalAction.TokensLoadResult(tokens: data.tokens, nextPageCursor: data.nextPageCursor, previousTokenIndex: previousTokenIndex) + let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + return InternalAction.TokensLoadResult(tokens: data.tokens, hiddenAssets: hiddenAssets, nextPageCursor: data.nextPageCursor, previousTokenIndex: previousTokenIndex) } await send(.internal(.tokensLoaded(result))) } @@ -146,3 +149,13 @@ extension NonFungibleAssetList { } } } + +private extension NonFungibleAssetList.Row.InternalAction.TokensLoadResult { + init(tokens: [OnLedgerEntity.NonFungibleToken], hiddenAssets: [AssetAddress], nextPageCursor: String?, previousTokenIndex: Int) { + self.tokens = tokens.filter { token in + !hiddenAssets.contains(.nonFungible(token.id)) + } + self.nextPageCursor = nextPageCursor + self.previousTokenIndex = previousTokenIndex + } +} From 38e2bd88868e5f2d67fdc9d3b807c2e5540f30d5 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 13:51:31 +0200 Subject: [PATCH 08/34] keep only NFTs with at least one visible id --- .../AccountPortfoliosClient+State.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift index ae9afa3a4e..0184e2a0ef 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift @@ -40,7 +40,9 @@ extension AccountPortfoliosClient { } }.count item.nonFungibleIdsCount -= countHidden - nonFungibleResources.append(item) + if item.nonFungibleIdsCount > 0 { + nonFungibleResources.append(item) + } } modified.nonFungibleResources = nonFungibleResources From 9eca9233fe7833f27cf0215687e0db90b769a9e5 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 17:32:50 +0200 Subject: [PATCH 09/34] wip fetch nft and pool unit --- RadixWallet.xcodeproj/project.pbxproj | 16 +++ .../Components/PlainListRow.swift | 15 +- .../HiddenAssets/HiddenAssets+View.swift | 128 +++++++++++++++++ .../HiddenAssets/HiddenAssets.swift | 129 ++++++++++++++++++ .../Preferences/Preferences+View.swift | 41 +++--- .../Preferences/Preferences.swift | 18 ++- 6 files changed, 321 insertions(+), 26 deletions(-) create mode 100644 RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift create mode 100644 RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 9a6300860c..50cd10e572 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -919,6 +919,8 @@ 5BEB9CD32C6F6B24001FD9D4 /* HideAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */; }; 5BEB9CD52C6F6CC9001FD9D4 /* ConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */; }; 5BEB9CD72C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */; }; + 5BFA4FBA2C736B740030B517 /* HiddenAssets+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */; }; + 5BFA4FBB2C736B740030B517 /* HiddenAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFA4FB92C736B740030B517 /* HiddenAssets.swift */; }; 830818482B9F1621002D8351 /* HTTPClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830818472B9F1621002D8351 /* HTTPClient+Live.swift */; }; 8308184A2B9F162B002D8351 /* HTTPClient+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830818492B9F162B002D8351 /* HTTPClient+Mock.swift */; }; 8308184C2B9F169B002D8351 /* TokenPriceClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8308184B2B9F169B002D8351 /* TokenPriceClient+Live.swift */; }; @@ -2072,6 +2074,8 @@ 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideAsset.swift; sourceTree = ""; }; 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationView.swift; sourceTree = ""; }; 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationView+Configuration.swift"; sourceTree = ""; }; + 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HiddenAssets+View.swift"; sourceTree = ""; }; + 5BFA4FB92C736B740030B517 /* HiddenAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenAssets.swift; sourceTree = ""; }; 830818472B9F1621002D8351 /* HTTPClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPClient+Live.swift"; sourceTree = ""; }; 830818492B9F162B002D8351 /* HTTPClient+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPClient+Mock.swift"; sourceTree = ""; }; 8308184B2B9F169B002D8351 /* TokenPriceClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TokenPriceClient+Live.swift"; sourceTree = ""; }; @@ -5664,6 +5668,7 @@ 5B758D552BCE7AEC00348722 /* Preferences+View.swift */, 5B758D562BCE7AEC00348722 /* Preferences.swift */, 48CFBD8F2ADC10D800E77A5C /* DefaultDepositGuarantees */, + 5BFA4FB72C736B500030B517 /* HiddenAssets */, ); path = Preferences; sourceTree = ""; @@ -5717,6 +5722,15 @@ path = HideAsset; sourceTree = ""; }; + 5BFA4FB72C736B500030B517 /* HiddenAssets */ = { + isa = PBXGroup; + children = ( + 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */, + 5BFA4FB92C736B740030B517 /* HiddenAssets.swift */, + ); + path = HiddenAssets; + sourceTree = ""; + }; 830E43AD2C2D8B3800B6E95F /* Mobile */ = { isa = PBXGroup; children = ( @@ -6980,6 +6994,8 @@ 5BEB9CD32C6F6B24001FD9D4 /* HideAsset.swift in Sources */, 5B272DD72C36E89600B74F1F /* AppEventsClient+Interface.swift in Sources */, 48CFC2A12ADC10D900E77A5C /* PersonaFeature.swift in Sources */, + 5BFA4FBA2C736B740030B517 /* HiddenAssets+View.swift in Sources */, + 5BFA4FBB2C736B740030B517 /* HiddenAssets.swift in Sources */, A4DCCC502C2DA32C00438A7B /* HomeCardsClient+Interface.swift in Sources */, 48CFC4162ADC10DA00E77A5C /* PasteboardClient+Test.swift in Sources */, 48CFC34E2ADC10D900E77A5C /* Login+View.swift in Sources */, diff --git a/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift b/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift index 7468ae9a3f..c287c32724 100644 --- a/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift +++ b/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift @@ -196,12 +196,14 @@ private extension PlainListRowCore.ViewState { .secondaryHeader case .settings, .dappAndPersona: .body1Header + case .hiddenAsset: + .body1HighImportance } } var subtitleTextStyle: TextStyle { switch context { - case .toggle: + case .toggle, .hiddenAsset: .body2Regular case .settings, .dappAndPersona: detail == nil ? .body1Regular : .body2Regular @@ -210,7 +212,7 @@ private extension PlainListRowCore.ViewState { var subtitleForegroundColor: Color { switch context { - case .toggle: + case .toggle, .hiddenAsset: .app.gray2 case .settings, .dappAndPersona: .app.gray1 @@ -219,7 +221,7 @@ private extension PlainListRowCore.ViewState { var titleLineLimit: Int? { switch context { - case .settings, .dappAndPersona: + case .settings, .dappAndPersona, .hiddenAsset: 1 case .toggle: nil @@ -228,7 +230,7 @@ private extension PlainListRowCore.ViewState { var subtitleLineLimit: Int { switch context { - case .toggle: + case .toggle, .hiddenAsset: 2 case .settings, .dappAndPersona: 3 @@ -243,12 +245,14 @@ private extension PlainListRowCore.ViewState { .medium1 case .dappAndPersona: .medium3 + case .hiddenAsset: + .medium3 } } var horizontalPadding: CGFloat { switch context { - case .toggle, .settings: + case .toggle, .settings, .hiddenAsset: .medium3 case .dappAndPersona: .medium1 @@ -262,6 +266,7 @@ extension PlainListRowCore.ViewState { case settings case toggle case dappAndPersona + case hiddenAsset } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift new file mode 100644 index 0000000000..881bd55d53 --- /dev/null +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -0,0 +1,128 @@ +import SwiftUI + +// MARK: - HiddenAssets.View +extension HiddenAssets { + public struct View: SwiftUI.View { + public let store: StoreOf + + public var body: some SwiftUI.View { + WithPerceptionTracking { + ScrollView { + LazyVStack(alignment: .leading, spacing: .large3) { + Text("You have hidden the following assets. While hidden, you will not see these in any of your Accounts.") + .textStyle(.body1HighImportance) + .foregroundColor(.app.gray2) + + header("Tokens") + fungible + + header("NFTs") + nonFungible + + header("Pool Units") + poolUnit + } + .padding(.medium3) + } + .background(Color.app.gray5) + .radixToolbar(title: "Hidden Assets") + } + .task { + store.send(.view(.task)) + } + } + + private func header(_ value: String) -> some SwiftUI.View { + Text(value) + .textStyle(.secondaryHeader) + .foregroundColor(.app.gray2) + } + + @ViewBuilder + private var fungible: some SwiftUI.View { + if store.fungible.isEmpty { + empty + } else { + VStack(spacing: .medium3) { + ForEachStatic(store.fungible) { resource in + Card { + PlainListRow(viewState: .init( + rowCoreViewState: .init(context: .hiddenAsset, title: resource.fungibleResourceName), + accessory: { unhideButton(asset: .fungible(resource.resourceAddress)) }, + icon: { Thumbnail(.token(.other), url: resource.metadata.iconURL) } + )) + } + } + } + } + } + + @ViewBuilder + private var nonFungible: some SwiftUI.View { + if store.nonFungible.isEmpty { + empty + } else { + VStack(spacing: .medium3) { + ForEachStatic(store.nonFungible) { token in + Card { + PlainListRow(viewState: .init( + rowCoreViewState: token.rowCoreViewState, + accessory: { unhideButton(asset: .nonFungible(token.id)) }, + icon: { Thumbnail(.nft, url: token.data?.keyImageURL) } + )) + } + } + } + } + } + + @ViewBuilder + private var poolUnit: some SwiftUI.View { + if store.poolUnit.isEmpty { + empty + } else { + VStack(spacing: .medium3) { + ForEachStatic(store.poolUnit) { resource in + Card { + PlainListRow(viewState: .init( + rowCoreViewState: .init(context: .hiddenAsset, title: resource.metadata.name ?? "Pool Unit"), + accessory: { unhideButton(asset: .poolUnit(resource.address)) }, + icon: { Thumbnail(.poolUnit, url: resource.metadata.iconURL) } + )) + } + } + } + } + } + + private func unhideButton(asset: AssetAddress) -> some SwiftUI.View { + Button("Unhide") { + store.send(.view(.unhideTapped(asset))) + } + .buttonStyle(.secondaryRectangular(shouldExpand: false)) + } + + private var empty: some SwiftUI.View { + ZStack { + PlainListRow(viewState: .init( + rowCoreViewState: .init(context: .hiddenAsset, title: "dummy"), + accessory: { unhideButton(asset: .fungible(.mainnetXRD)) }, + icon: { Thumbnail(.token(.other), url: nil) } + )) + .hidden() + + Text("None") + .textStyle(.secondaryHeader) + .foregroundColor(.app.gray2) + } + .background(Color.app.gray4) + .clipShape(RoundedRectangle(cornerRadius: .medium3)) + } + } +} + +private extension OnLedgerEntity.NonFungibleToken { + var rowCoreViewState: PlainListRowCore.ViewState { + .init(context: .hiddenAsset, title: data?.name, subtitle: id.nonFungibleLocalId.formatted()) + } +} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift new file mode 100644 index 0000000000..30f0755569 --- /dev/null +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -0,0 +1,129 @@ +import ComposableArchitecture + +// MARK: - HiddenAssets +@Reducer +public struct HiddenAssets: Sendable, FeatureReducer { + @ObservableState + public struct State: Sendable, Hashable { + var fungible: [OnLedgerEntity.Resource] = [] + var nonFungible: [OnLedgerEntity.NonFungibleToken] = [] + var poolUnit: [OnLedgerEntity.ResourcePool] = [] + } + + public typealias Action = FeatureAction + + @CasePathable + public enum ViewAction: Sendable, Equatable { + case task + case unhideTapped(AssetAddress) + } + + public enum InternalAction: Sendable, Equatable { + case loadAssets([AssetAddress]) + case setFungible([OnLedgerEntity.Resource]) + case setNonFungible([OnLedgerEntity.NonFungibleToken]) + case setPoolUnit([OnLedgerEntity.ResourcePool]) + } + + @Dependency(\.appPreferencesClient) var appPreferencesClient + @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient + + public init() {} + + public var body: some ReducerOf { + Reduce(core) + } + + public func reduce(into _: inout State, viewAction: ViewAction) -> Effect { + switch viewAction { + case .task: + .run { send in + let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + await send(.internal(.loadAssets(hiddenAssets))) + } + case .unhideTapped: + .none + } + } + + public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { + switch internalAction { + case let .loadAssets(hiddenAssets): + return fungibleEffect(hiddenAssets: hiddenAssets) + .merge(with: nonFungibleEffect(hiddenAssets: hiddenAssets)) + .merge(with: poolUnitEffect(hiddenAssets: hiddenAssets)) + case let .setFungible(values): + state.fungible = values + return .none + case let .setNonFungible(values): + state.nonFungible = values + return .none + case let .setPoolUnit(values): + state.poolUnit = values + return .none + } + } + + private func fungibleEffect(hiddenAssets: [AssetAddress]) -> Effect { + .run { send in + let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.fungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) + await send(.internal(.setFungible(resources))) + } + } + + private func nonFungibleEffect(hiddenAssets: [AssetAddress]) -> Effect { + .run { send in + let tokens = try await hiddenAssets.nonFungibleDictionary.parallelMap { resource, nonFungibleIds in + try await onLedgerEntitiesClient.getNonFungibleTokenData(.init(resource: resource, nonFungibleIds: nonFungibleIds)) + } + .flatMap { $0 } + .sorted(by: \.id.resourceAddress.address) + await send(.internal(.setNonFungible(tokens))) + } + } + + private func poolUnitEffect(hiddenAssets: [AssetAddress]) -> Effect { + .run { send in + let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.poolUnitAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resourcePool) + await send(.internal(.setPoolUnit(resources))) + } + } +} + +private extension [AssetAddress] { + var fungibleAddresses: [Address] { + compactMap { item in + switch item { + case let .fungible(resourceAddress): + resourceAddress.asGeneral + case .nonFungible, .poolUnit: + nil + } + } + } + + var nonFungibleDictionary: [ResourceAddress: [NonFungibleGlobalId]] { + let nonFungibleIds = self.compactMap { item in + switch item { + case let .nonFungible(id): + id + case .fungible, .poolUnit: + nil + } + } + return Dictionary(grouping: nonFungibleIds) { + $0.resourceAddress + } + } + + var poolUnitAddresses: [Address] { + compactMap { item in + switch item { + case let .poolUnit(poolAddress): + poolAddress.asGeneral + case .fungible, .nonFungible: + nil + } + } + } +} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift index 4d209c27ae..196f8a5d31 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift @@ -85,12 +85,19 @@ extension Preferences.View { icon: .asset(AssetResource.depositGuarantees), action: .depositGuaranteesButtonTapped ), + .header("Display"), .model( title: L10n.Preferences.HiddenEntities.title, subtitle: L10n.Preferences.HiddenEntities.subtitle, icon: .systemImage("eye.fill"), action: .hiddenEntitiesButtonTapped ), + .model( + title: "Hidden Assets", + subtitle: "Manage hidden Tokens, NFTs and other asset types", + icon: .systemImage("eye.fill"), + action: .hiddenAssetsButtonTapped + ), .header(L10n.Preferences.advancedPreferences), .model( title: L10n.Preferences.gateways, @@ -151,33 +158,31 @@ private extension View { let destinationStore = store.destination return depositGuarantees(with: destinationStore) .hiddenEntities(with: destinationStore) + .hiddenAssets(with: destinationStore) .gateways(with: destinationStore) } private func depositGuarantees(with destinationStore: PresentationStoreOf) -> some View { - navigationDestination( - store: destinationStore, - state: /Preferences.Destination.State.depositGuarantees, - action: Preferences.Destination.Action.depositGuarantees, - destination: { DefaultDepositGuarantees.View(store: $0) } - ) + navigationDestination(store: destinationStore.scope(state: \.depositGuarantees, action: \.depositGuarantees)) { + DefaultDepositGuarantees.View(store: $0) + } } private func hiddenEntities(with destinationStore: PresentationStoreOf) -> some View { - navigationDestination( - store: destinationStore, - state: /Preferences.Destination.State.hiddenEntities, - action: Preferences.Destination.Action.hiddenEntities, - destination: { AccountAndPersonaHiding.View(store: $0) } - ) + navigationDestination(store: destinationStore.scope(state: \.hiddenEntities, action: \.hiddenEntities)) { + AccountAndPersonaHiding.View(store: $0) + } + } + + private func hiddenAssets(with destinationStore: PresentationStoreOf) -> some View { + navigationDestination(store: destinationStore.scope(state: \.hiddenAssets, action: \.hiddenAssets)) { + HiddenAssets.View(store: $0) + } } private func gateways(with destinationStore: PresentationStoreOf) -> some View { - navigationDestination( - store: destinationStore, - state: /Preferences.Destination.State.gateways, - action: Preferences.Destination.Action.gateways, - destination: { GatewaySettings.View(store: $0) } - ) + navigationDestination(store: destinationStore.scope(state: \.gateways, action: \.gateways)) { + GatewaySettings.View(store: $0) + } } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift b/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift index 965dfd4961..d3bd995cd8 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift @@ -15,6 +15,7 @@ public struct Preferences: Sendable, FeatureReducer { case appeared case depositGuaranteesButtonTapped case hiddenEntitiesButtonTapped + case hiddenAssetsButtonTapped case gatewaysButtonTapped case developerModeToogled(Bool) case exportLogsButtonTapped @@ -26,26 +27,33 @@ public struct Preferences: Sendable, FeatureReducer { } public struct Destination: DestinationReducer { + @CasePathable public enum State: Sendable, Hashable { case depositGuarantees(DefaultDepositGuarantees.State) case hiddenEntities(AccountAndPersonaHiding.State) + case hiddenAssets(HiddenAssets.State) case gateways(GatewaySettings.State) } + @CasePathable public enum Action: Sendable, Equatable { case depositGuarantees(DefaultDepositGuarantees.Action) case hiddenEntities(AccountAndPersonaHiding.Action) + case hiddenAssets(HiddenAssets.Action) case gateways(GatewaySettings.Action) } public var body: some ReducerOf { - Scope(state: /State.depositGuarantees, action: /Action.depositGuarantees) { + Scope(state: \.depositGuarantees, action: \.depositGuarantees) { DefaultDepositGuarantees() } - Scope(state: /State.hiddenEntities, action: /Action.hiddenEntities) { + Scope(state: \.hiddenEntities, action: \.hiddenEntities) { AccountAndPersonaHiding() } - Scope(state: /State.gateways, action: /Action.gateways) { + Scope(state: \.hiddenAssets, action: \.hiddenAssets) { + HiddenAssets() + } + Scope(state: \.gateways, action: \.gateways) { GatewaySettings() } } @@ -81,6 +89,10 @@ public struct Preferences: Sendable, FeatureReducer { state.destination = .hiddenEntities(.init()) return .none + case .hiddenAssetsButtonTapped: + state.destination = .hiddenAssets(.init()) + return .none + case .gatewaysButtonTapped: state.destination = .gateways(.init()) return .none From b0710234f1b9b70c8d250633edf4a9d41a12712e Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 18:46:18 +0200 Subject: [PATCH 10/34] Allow to unhide --- .../Components/HideAsset/HideAsset.swift | 4 +- .../HiddenAssets/HiddenAssets+View.swift | 1 + .../HiddenAssets/HiddenAssets.swift | 77 ++++++++++++++++++- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index 7552534ac7..254a76b3fb 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -114,8 +114,8 @@ private extension HideAsset { func isAlreadyHiddenEffect(state: State) -> Effect { .run { send in - let hiddenAssets = await appPreferencesClient.getPreferences().assets - let isAlreadyHidden = hiddenAssets.contains(where: { $0.assetAddress == state.asset }) + let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + let isAlreadyHidden = hiddenAssets.contains(state.asset) await send(.internal(.setIsAlreadyHidden(isAlreadyHidden))) } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 881bd55d53..b0665285be 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -30,6 +30,7 @@ extension HiddenAssets { .task { store.send(.view(.task)) } + .alert(store: store.scope(state: \.$destination.unhideAlert, action: \.destination.unhideAlert)) } private func header(_ value: String) -> some SwiftUI.View { diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 30f0755569..da78ff4310 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -8,6 +8,9 @@ public struct HiddenAssets: Sendable, FeatureReducer { var fungible: [OnLedgerEntity.Resource] = [] var nonFungible: [OnLedgerEntity.NonFungibleToken] = [] var poolUnit: [OnLedgerEntity.ResourcePool] = [] + + @Presents + var destination: Destination.State? = nil } public typealias Action = FeatureAction @@ -23,6 +26,28 @@ public struct HiddenAssets: Sendable, FeatureReducer { case setFungible([OnLedgerEntity.Resource]) case setNonFungible([OnLedgerEntity.NonFungibleToken]) case setPoolUnit([OnLedgerEntity.ResourcePool]) + case didUnhideAsset(AssetAddress) + } + + public struct Destination: DestinationReducer { + @CasePathable + public enum State: Sendable, Hashable { + case unhideAlert(AlertState) + } + + @CasePathable + public enum Action: Sendable, Equatable { + case unhideAlert(UnhideAlert) + + public enum UnhideAlert: Hashable, Sendable { + case confirmTapped(AssetAddress) + case cancelTapped + } + } + + public var body: some ReducerOf { + EmptyReducer() + } } @Dependency(\.appPreferencesClient) var appPreferencesClient @@ -34,15 +59,26 @@ public struct HiddenAssets: Sendable, FeatureReducer { Reduce(core) } - public func reduce(into _: inout State, viewAction: ViewAction) -> Effect { + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .task: - .run { send in + return .run { send in let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets await send(.internal(.loadAssets(hiddenAssets))) } - case .unhideTapped: - .none + case let .unhideTapped(asset): + state.destination = .unhideAlert(.init( + title: { TextState("Make this asset visible in your Accounts again?") }, + actions: { + ButtonState(role: .cancel, action: .cancelTapped) { + TextState(L10n.Common.cancel) + } + ButtonState(action: .confirmTapped(asset)) { + TextState(L10n.Common.confirm) + } + } + )) + return .none } } @@ -52,15 +88,48 @@ public struct HiddenAssets: Sendable, FeatureReducer { return fungibleEffect(hiddenAssets: hiddenAssets) .merge(with: nonFungibleEffect(hiddenAssets: hiddenAssets)) .merge(with: poolUnitEffect(hiddenAssets: hiddenAssets)) + case let .setFungible(values): state.fungible = values return .none + case let .setNonFungible(values): state.nonFungible = values return .none + case let .setPoolUnit(values): state.poolUnit = values return .none + + case let .didUnhideAsset(asset): + switch asset { + case let .fungible(resourceAddress): + state.fungible.removeAll(where: { $0.resourceAddress == resourceAddress }) + case let .nonFungible(globalId): + state.nonFungible.removeAll(where: { $0.id == globalId }) + case let .poolUnit(poolAddress): + state.poolUnit.removeAll(where: { $0.address == poolAddress }) + } + state.destination = nil + return .none + } + } + + public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect { + switch presentedAction { + case let .unhideAlert(action): + switch action { + case let .confirmTapped(asset): + return .run { send in + try await appPreferencesClient.updating { preferences in + preferences.assets.unhideAsset(asset: asset) + } + await send(.internal(.didUnhideAsset(asset))) + } + case .cancelTapped: + state.destination = nil + return .none + } } } From 9acbc93683f705dcc3afd83cac2ef37041b9c41a Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 19:09:53 +0200 Subject: [PATCH 11/34] lint --- .../Components/HideAsset/HideAsset.swift | 42 +++++++------------ .../HiddenAssets/HiddenAssets.swift | 4 +- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index 254a76b3fb..54c86d0c75 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -4,8 +4,7 @@ public struct HideAsset: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { let asset: AssetAddress - fileprivate var isAlreadyHidden = false - fileprivate var isXrd = false + var shouldShow = true @Presents var destination: Destination.State? = nil @@ -13,17 +12,12 @@ public struct HideAsset: Sendable, FeatureReducer { public init(asset: AssetAddress) { self.asset = asset } - - var shouldShow: Bool { - !(isAlreadyHidden || isXrd) - } } public typealias Action = FeatureAction public enum InternalAction: Sendable, Equatable { - case setIsXrd(Bool) - case setIsAlreadyHidden(Bool) + case setShouldShow(Bool) } public enum ViewAction: Sendable, Equatable { @@ -67,8 +61,7 @@ public struct HideAsset: Sendable, FeatureReducer { public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .task: - return isXrdEffect(state: state) - .merge(with: isAlreadyHiddenEffect(state: state)) + return shouldShowEffect(state: state) case .buttonTapped: state.destination = .confirmation @@ -78,11 +71,8 @@ public struct HideAsset: Sendable, FeatureReducer { public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { switch internalAction { - case let .setIsXrd(value): - state.isXrd = value - return .none - case let .setIsAlreadyHidden(value): - state.isAlreadyHidden = value + case let .setShouldShow(value): + state.shouldShow = value return .none } } @@ -100,23 +90,23 @@ public struct HideAsset: Sendable, FeatureReducer { } private extension HideAsset { - func isXrdEffect(state: State) -> Effect { + func shouldShowEffect(state: State) -> Effect { .run { send in + let isXrd: Bool switch state.asset { case let .fungible(resource): let networkId = await gatewaysClient.getCurrentNetworkID() - await send(.internal(.setIsXrd(resource.isXRD(on: networkId)))) + isXrd = resource.isXRD(on: networkId) case .nonFungible, .poolUnit: - await send(.internal(.setIsXrd(false))) + isXrd = false + } + if isXrd { + await send(.internal(.setShouldShow(false))) + } else { + let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + let isAlreadyHidden = hiddenAssets.contains(state.asset) + await send(.internal(.setShouldShow(!isAlreadyHidden))) } - } - } - - func isAlreadyHiddenEffect(state: State) -> Effect { - .run { send in - let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets - let isAlreadyHidden = hiddenAssets.contains(state.asset) - await send(.internal(.setIsAlreadyHidden(isAlreadyHidden))) } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index da78ff4310..0d7ca76b5d 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -124,7 +124,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { try await appPreferencesClient.updating { preferences in preferences.assets.unhideAsset(asset: asset) } - await send(.internal(.didUnhideAsset(asset))) + await send(.internal(.didUnhideAsset(asset)), animation: .default) } case .cancelTapped: state.destination = nil @@ -159,6 +159,8 @@ public struct HiddenAssets: Sendable, FeatureReducer { } } +// MARK: - Helpers + private extension [AssetAddress] { var fungibleAddresses: [Address] { compactMap { item in From 0ae045e39ccc7d0ceee2db2edf56a1ec925f690f Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 19 Aug 2024 19:35:06 +0200 Subject: [PATCH 12/34] Update portfolio when assets are hidden/unhidden --- .../AccountPortfoliosClient+Live.swift | 9 +++++++++ .../Preferences/HiddenAssets/HiddenAssets+View.swift | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index 57f06ad0a6..f57fd670b5 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -25,6 +25,15 @@ extension AccountPortfoliosClient: DependencyKey { } } + /// Update when hidden assets change + Task { + for try await _ in await appPreferencesClient.appPreferenceUpdates().map(\.assets.hiddenAssets) { + guard !Task.isCancelled else { return } + let accountAddresses = state.portfoliosSubject.value.wrappedValue.map { $0.map(\.key) } ?? [] + _ = try await fetchAccountPortfolios(accountAddresses, false) + } + } + /// Fetches the pool and stake units details for a given account; Will update the portfolio accordingly @Sendable func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [AssetAddress], cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index b0665285be..991c78e11f 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -26,11 +26,11 @@ extension HiddenAssets { } .background(Color.app.gray5) .radixToolbar(title: "Hidden Assets") + .task { + store.send(.view(.task)) + } + .alert(store: store.scope(state: \.$destination.unhideAlert, action: \.destination.unhideAlert)) } - .task { - store.send(.view(.task)) - } - .alert(store: store.scope(state: \.$destination.unhideAlert, action: \.destination.unhideAlert)) } private func header(_ value: String) -> some SwiftUI.View { From c50f6a40511be6f4597c3007f8a23b3ca1c51fb0 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 10:20:58 +0200 Subject: [PATCH 13/34] comment --- .../AccountPortfoliosClient+State.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift index 0184e2a0ef..8ed93bbcff 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift @@ -27,8 +27,9 @@ extension AccountPortfoliosClient { hiddenAssets.contains(.poolUnit(poolUnit.resourcePoolAddress)) }) - // We cannot remove the NFTs, since at this moment we haven't loaded them yet - // All we can do is decrease the count for each resource + // We cannot remove the NFTs, since at this moment we haven't loaded them yet. + // All we can do is update the `nonFungibleIdsCount` to remove the hidden ones. + // Then, when loading the actual tokens, we will filter those that are hidden. var nonFungibleResources: [OnLedgerEntity.OwnedNonFungibleResource] = [] for var item in modified.nonFungibleResources { let countHidden = hiddenAssets.filter { asset in From 49acc2fdc33dddf65c4939bf000854fe24d45f03 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 10:39:15 +0200 Subject: [PATCH 14/34] localisation --- .../SettingsFeature/Preferences/Preferences+View.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift index 196f8a5d31..bed6d39f54 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift @@ -85,7 +85,7 @@ extension Preferences.View { icon: .asset(AssetResource.depositGuarantees), action: .depositGuaranteesButtonTapped ), - .header("Display"), + .header(L10n.Preferences.displayPreferences), .model( title: L10n.Preferences.HiddenEntities.title, subtitle: L10n.Preferences.HiddenEntities.subtitle, @@ -93,8 +93,8 @@ extension Preferences.View { action: .hiddenEntitiesButtonTapped ), .model( - title: "Hidden Assets", - subtitle: "Manage hidden Tokens, NFTs and other asset types", + title: L10n.Preferences.HiddenAssets.title, + subtitle: L10n.Preferences.HiddenAssets.subtitle, icon: .systemImage("eye.fill"), action: .hiddenAssetsButtonTapped ), From f7346bbeb2ba5ebda7f62478115fe43d35fc9ef0 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 13:37:36 +0200 Subject: [PATCH 15/34] Helper methods --- .../AccountPortfoliosClient+Live.swift | 2 +- .../AppPreferencesClient+Interface.swift | 8 ++++++++ .../AssetsFeature/Components/HideAsset/HideAsset.swift | 3 +-- .../Components/Row/NonFungbileAssetRow+Reducer.swift | 2 +- .../Preferences/HiddenAssets/HiddenAssets.swift | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index f57fd670b5..e3eeb90fef 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -151,7 +151,7 @@ extension AccountPortfoliosClient: DependencyKey { } let account = try await onLedgerEntitiesClient.getAccount(accountAddress) - let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + let hiddenAssets = await appPreferencesClient.getHiddenAssets() let portfolio = AccountPortfolio(account: account, hiddenAssets: hiddenAssets) if case let .success(tokenPrices) = await state.tokenPrices { diff --git a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift index 5edfb39cb7..11e1978483 100644 --- a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift +++ b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift @@ -70,6 +70,14 @@ extension AppPreferencesClient { display.isCurrencyAmountVisible.toggle() } } + + public func getHiddenAssets() async -> [AssetAddress] { + await getPreferences().assets.hiddenAssets + } + + public func isAssetHidden(asset: AssetAddress) async -> Bool { + await getHiddenAssets().contains(asset) + } } // MARK: AppPreferencesClient.Error diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index 54c86d0c75..59ef4c7e37 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -103,8 +103,7 @@ private extension HideAsset { if isXrd { await send(.internal(.setShouldShow(false))) } else { - let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets - let isAlreadyHidden = hiddenAssets.contains(state.asset) + let isAlreadyHidden = await appPreferencesClient.isAssetHidden(asset: state.asset) await send(.internal(.setShouldShow(!isAlreadyHidden))) } } diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift index e5c43307e8..fa9557ced6 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift @@ -137,7 +137,7 @@ extension NonFungibleAssetList { return .run { [resource = state.resource, accountAddress = state.accountAddress] send in let result = await TaskResult { let data = try await onLedgerEntitiesClient.getAccountOwnedNonFungibleTokenData(.init(accountAddress: accountAddress, resource: resource, mode: .loadPage(pageCursor: cursor))) - let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + let hiddenAssets = await appPreferencesClient.getHiddenAssets() return InternalAction.TokensLoadResult(tokens: data.tokens, hiddenAssets: hiddenAssets, nextPageCursor: data.nextPageCursor, previousTokenIndex: previousTokenIndex) } await send(.internal(.tokensLoaded(result))) diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 0d7ca76b5d..705f2fe799 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -63,7 +63,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { switch viewAction { case .task: return .run { send in - let hiddenAssets = await appPreferencesClient.getPreferences().assets.hiddenAssets + let hiddenAssets = await appPreferencesClient.getHiddenAssets() await send(.internal(.loadAssets(hiddenAssets))) } case let .unhideTapped(asset): From da12226fb6c0adbf114aac05c5f0b9faa08d4abb Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 14:59:20 +0200 Subject: [PATCH 16/34] Display warning in TX review --- .../OnLedgerEntitiesClient+ComplexTypes.swift | 31 ++++++++++++++----- .../ResourceBalance/ResourceBalance.swift | 7 ++++- .../ResourceBalanceButton.swift | 28 ++++++++++++----- .../TransactionReview+Sections.swift | 2 +- .../TransactionReviewAccount+View.swift | 9 +++++- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift b/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift index 3448de9793..a07238959c 100644 --- a/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift +++ b/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift @@ -29,6 +29,9 @@ extension OnLedgerEntitiesClient { let amount = resourceQuantifier.amount let resourceAddress = resource.resourceAddress + @Dependency(\.appPreferencesClient) var appPreferencesClient + let hiddenAssets = await appPreferencesClient.getHiddenAssets() + let guarantee: TransactionGuarantee? = { () -> TransactionGuarantee? in guard case let .predicted(predictedAmount) = resourceQuantifier else { return nil } let guaranteedAmount = defaultDepositGuarantee * predictedAmount.value @@ -50,7 +53,8 @@ extension OnLedgerEntitiesClient { entities: entities, resourceAssociatedDapps: resourceAssociatedDapps, networkID: networkID, - guarantee: guarantee + guarantee: guarantee, + hiddenAssets: hiddenAssets ) } @@ -64,11 +68,13 @@ extension OnLedgerEntitiesClient { guarantee: guarantee ) } + // Normal fungible resource let isXRD = resourceAddress.isXRD(on: networkID) + let isHidden = hiddenAssets.contains(.fungible(resourceAddress)) let details: ResourceBalance.Fungible = .init(isXRD: isXRD, amount: .init(nominalAmount: amount), guarantee: guarantee) - return .init(resource: resource, details: .fungible(details)) + return .init(resource: resource, details: .fungible(details), isHidden: isHidden) } private func poolUnit( @@ -78,7 +84,8 @@ extension OnLedgerEntitiesClient { entities: TransactionReview.ResourcesInfo = [:], resourceAssociatedDapps: TransactionReview.ResourceAssociatedDapps? = nil, networkID: NetworkID, - guarantee: TransactionGuarantee? + guarantee: TransactionGuarantee?, + hiddenAssets: [AssetAddress] ) async throws -> ResourceBalance { let resourceAddress = resource.resourceAddress @@ -104,6 +111,8 @@ extension OnLedgerEntitiesClient { } } + let isHidden = hiddenAssets.contains(.poolUnit(poolContribution.poolAddress)) + return .init( resource: resource, details: .poolUnit(.init( @@ -115,19 +124,23 @@ extension OnLedgerEntitiesClient { nonXrdResources: nonXrdResources ), guarantee: guarantee - )) + )), + isHidden: isHidden ) } else { guard let details = try await getPoolUnitDetails(resource, forAmount: amount) else { throw FailedToGetPoolUnitDetails() } + let isHidden = hiddenAssets.contains(.poolUnit(details.address)) + return .init( resource: resource, details: .poolUnit(.init( details: details, guarantee: guarantee - )) + )), + isHidden: isHidden ) } } @@ -187,6 +200,9 @@ extension OnLedgerEntitiesClient { switch resourceInfo { case let .left(resource): + @Dependency(\.appPreferencesClient) var appPreferencesClient + let hiddenAssets = await appPreferencesClient.getHiddenAssets() + let existingTokenIds = ids.filter { id in !newlyCreatedNonFungibles.contains { newId in newId.resourceAddress == resourceAddress && newId.nonFungibleLocalId == id @@ -220,7 +236,8 @@ extension OnLedgerEntitiesClient { )] } else { result = tokens.map { token in - ResourceBalance(resource: resource, details: .nonFungible(token)) + let isHidden = hiddenAssets.contains(.nonFungible(token.id)) + return ResourceBalance(resource: resource, details: .nonFungible(token), isHidden: isHidden) } guard result.count == ids.count else { @@ -241,7 +258,7 @@ extension OnLedgerEntitiesClient { ) } .map { id in - ResourceBalance(resource: resource, details: .nonFungible(.init(id: id, data: nil))) + ResourceBalance(resource: resource, details: .nonFungible(.init(id: id, data: nil)), isHidden: false) } guard result.count == ids.count else { diff --git a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift index 24f6f411d4..04668493bc 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift @@ -11,9 +11,14 @@ public struct ResourceBalance: Sendable, Hashable { public let resource: OnLedgerEntity.Resource public var details: Details - public init(resource: OnLedgerEntity.Resource, details: Details) { + /// Indicates whether the reosurce is hidden in user's profile. + /// Value is optional since we won't check for cases that it doesn't matter. + public let isHidden: Bool? + + public init(resource: OnLedgerEntity.Resource, details: Details, isHidden: Bool? = nil) { self.resource = resource self.details = details + self.isHidden = isHidden } public enum Details: Sendable, Hashable { diff --git a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift index b1998da7ba..67c9a400f7 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift @@ -5,6 +5,7 @@ public struct ResourceBalanceButton: View { public let viewState: ResourceBalance.ViewState public let appearance: Appearance public let isSelected: Bool? + public let warning: String? public let onTap: () -> Void public enum Appearance { @@ -12,23 +13,34 @@ public struct ResourceBalanceButton: View { case transactionReview } - init(_ viewState: ResourceBalance.ViewState, appearance: Appearance, isSelected: Bool? = nil, onTap: @escaping () -> Void) { + init( + _ viewState: ResourceBalance.ViewState, + appearance: Appearance, + isSelected: Bool? = nil, + warning: String? = nil, + onTap: @escaping () -> Void + ) { self.viewState = viewState self.appearance = appearance self.isSelected = isSelected + self.warning = warning self.onTap = onTap } public var body: some View { - HStack(alignment: .center, spacing: .small2) { - Button(action: onTap) { + Button(action: onTap) { + VStack(alignment: .leading, spacing: .small2) { ResourceBalanceView(viewState, appearance: .standard, isSelected: isSelected) - .padding(.top, topPadding) - .padding(.bottom, bottomPadding) - .padding(.horizontal, horizontalSpacing) - .contentShape(Rectangle()) - .background(background) + + if let warning { + WarningErrorView(text: warning, type: .warning, useNarrowSpacing: true) + } } + .padding(.top, topPadding) + .padding(.bottom, bottomPadding) + .padding(.horizontal, horizontalSpacing) + .contentShape(Rectangle()) + .background(background) } } diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift index a98d352ecf..61f03954e9 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift @@ -645,7 +645,7 @@ extension TransactionReview { guarantee: nil ) - return [.init(resource: resource, details: .fungible(details))] + return [.init(resource: resource, details: .fungible(details), isHidden: false)] } case let .nonFungible(_, indicator): diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift index a6851a606d..a25451fd0f 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift @@ -99,7 +99,7 @@ struct TransactionReviewResourceView: View { var body: some View { switch transfer.details { case .fungible, .nonFungible, .liquidStakeUnit, .poolUnit: - ResourceBalanceButton(transfer.viewState, appearance: .transactionReview) { + ResourceBalanceButton(transfer.viewState, appearance: .transactionReview, warning: warning) { onTap(nil) } case let .stakeClaimNFT(details): @@ -108,6 +108,13 @@ struct TransactionReviewResourceView: View { } } } + + private var warning: String? { + guard let isHidden = transfer.isHidden, isHidden else { + return nil + } + return "This asset is hidden and will not be visible in your Account" + } } extension [ResourceBalance.ViewState.Fungible] { // FIXME: GK use full From 88d57cce2a3f031c735dce8bf590bf532c951f90 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 17:28:37 +0200 Subject: [PATCH 17/34] Get complete details for pool units --- .../HiddenAssets/HiddenAssets+View.swift | 14 +++++++--- .../HiddenAssets/HiddenAssets.swift | 26 +++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 991c78e11f..b3adae71f1 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -83,12 +83,12 @@ extension HiddenAssets { empty } else { VStack(spacing: .medium3) { - ForEachStatic(store.poolUnit) { resource in + ForEachStatic(store.poolUnit) { poolUnit in Card { PlainListRow(viewState: .init( - rowCoreViewState: .init(context: .hiddenAsset, title: resource.metadata.name ?? "Pool Unit"), - accessory: { unhideButton(asset: .poolUnit(resource.address)) }, - icon: { Thumbnail(.poolUnit, url: resource.metadata.iconURL) } + rowCoreViewState: poolUnit.rowCoreViewState, + accessory: { unhideButton(asset: .poolUnit(poolUnit.details.address)) }, + icon: { Thumbnail(.poolUnit, url: poolUnit.resource.metadata.iconURL) } )) } } @@ -127,3 +127,9 @@ private extension OnLedgerEntity.NonFungibleToken { .init(context: .hiddenAsset, title: data?.name, subtitle: id.nonFungibleLocalId.formatted()) } } + +private extension HiddenAssets.State.PoolUnitDetails { + var rowCoreViewState: PlainListRowCore.ViewState { + .init(context: .hiddenAsset, title: "-", subtitle: details.dAppName ?? "-") + } +} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 705f2fe799..5155ee7cbd 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -7,10 +7,15 @@ public struct HiddenAssets: Sendable, FeatureReducer { public struct State: Sendable, Hashable { var fungible: [OnLedgerEntity.Resource] = [] var nonFungible: [OnLedgerEntity.NonFungibleToken] = [] - var poolUnit: [OnLedgerEntity.ResourcePool] = [] + var poolUnit: [PoolUnitDetails] = [] @Presents var destination: Destination.State? = nil + + public struct PoolUnitDetails: Sendable, Hashable { + let resource: OnLedgerEntity.Resource + let details: OnLedgerEntitiesClient.OwnedResourcePoolDetails + } } public typealias Action = FeatureAction @@ -25,7 +30,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case loadAssets([AssetAddress]) case setFungible([OnLedgerEntity.Resource]) case setNonFungible([OnLedgerEntity.NonFungibleToken]) - case setPoolUnit([OnLedgerEntity.ResourcePool]) + case setPoolUnit([State.PoolUnitDetails]) case didUnhideAsset(AssetAddress) } @@ -108,7 +113,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case let .nonFungible(globalId): state.nonFungible.removeAll(where: { $0.id == globalId }) case let .poolUnit(poolAddress): - state.poolUnit.removeAll(where: { $0.address == poolAddress }) + state.poolUnit.removeAll(where: { $0.details.address == poolAddress }) } state.destination = nil return .none @@ -153,8 +158,19 @@ public struct HiddenAssets: Sendable, FeatureReducer { private func poolUnitEffect(hiddenAssets: [AssetAddress]) -> Effect { .run { send in - let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.poolUnitAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resourcePool) - await send(.internal(.setPoolUnit(resources))) + let resourcePools = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.poolUnitAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resourcePool) + let resources = try await resourcePools.parallelMap { + try await onLedgerEntitiesClient.getResource($0.poolUnitResourceAddress) + } + let poolUnitDetails = try await resources.parallelMap { resource in + if let details = try await onLedgerEntitiesClient.getPoolUnitDetails(resource, forAmount: .one) { + State.PoolUnitDetails(resource: resource, details: details) + } else { + nil + } + } + .compactMap { $0 } + await send(.internal(.setPoolUnit(poolUnitDetails))) } } } From 24ef5b5b86cfaddb91bea9e7315b64d70847281d Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 17:31:04 +0200 Subject: [PATCH 18/34] localisation --- .../HiddenAssets/HiddenAssets+View.swift | 26 +++++++++---------- .../HiddenAssets/HiddenAssets.swift | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index b3adae71f1..4fc735420a 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -9,23 +9,23 @@ extension HiddenAssets { WithPerceptionTracking { ScrollView { LazyVStack(alignment: .leading, spacing: .large3) { - Text("You have hidden the following assets. While hidden, you will not see these in any of your Accounts.") + Text(L10n.HiddenAssets.text) .textStyle(.body1HighImportance) .foregroundColor(.app.gray2) - header("Tokens") - fungible + header(L10n.HiddenAssets.fungibles) + fungibles - header("NFTs") - nonFungible + header(L10n.HiddenAssets.nonFungibles) + nonFungibles - header("Pool Units") - poolUnit + header(L10n.HiddenAssets.poolUnits) + poolUnits } .padding(.medium3) } .background(Color.app.gray5) - .radixToolbar(title: "Hidden Assets") + .radixToolbar(title: L10n.HiddenAssets.title) .task { store.send(.view(.task)) } @@ -40,7 +40,7 @@ extension HiddenAssets { } @ViewBuilder - private var fungible: some SwiftUI.View { + private var fungibles: some SwiftUI.View { if store.fungible.isEmpty { empty } else { @@ -59,7 +59,7 @@ extension HiddenAssets { } @ViewBuilder - private var nonFungible: some SwiftUI.View { + private var nonFungibles: some SwiftUI.View { if store.nonFungible.isEmpty { empty } else { @@ -78,7 +78,7 @@ extension HiddenAssets { } @ViewBuilder - private var poolUnit: some SwiftUI.View { + private var poolUnits: some SwiftUI.View { if store.poolUnit.isEmpty { empty } else { @@ -97,7 +97,7 @@ extension HiddenAssets { } private func unhideButton(asset: AssetAddress) -> some SwiftUI.View { - Button("Unhide") { + Button(L10n.HiddenAssets.unhide) { store.send(.view(.unhideTapped(asset))) } .buttonStyle(.secondaryRectangular(shouldExpand: false)) @@ -112,7 +112,7 @@ extension HiddenAssets { )) .hidden() - Text("None") + Text(L10n.Common.none) .textStyle(.secondaryHeader) .foregroundColor(.app.gray2) } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 5155ee7cbd..793a1d39b7 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -73,7 +73,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { } case let .unhideTapped(asset): state.destination = .unhideAlert(.init( - title: { TextState("Make this asset visible in your Accounts again?") }, + title: { TextState(L10n.HiddenAssets.unhideConfirmation) }, actions: { ButtonState(role: .cancel, action: .cancelTapped) { TextState(L10n.Common.cancel) From 2615cca466f3fa471f790fbc3e9a4e7bc5ef7222 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 17:54:52 +0200 Subject: [PATCH 19/34] Get complete details for NFTs --- .../HiddenAssets/HiddenAssets+View.swift | 16 ++++++++----- .../HiddenAssets/HiddenAssets.swift | 23 +++++++++++++------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 4fc735420a..8d22af4880 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -64,12 +64,12 @@ extension HiddenAssets { empty } else { VStack(spacing: .medium3) { - ForEachStatic(store.nonFungible) { token in + ForEachStatic(store.nonFungible) { details in Card { PlainListRow(viewState: .init( - rowCoreViewState: token.rowCoreViewState, - accessory: { unhideButton(asset: .nonFungible(token.id)) }, - icon: { Thumbnail(.nft, url: token.data?.keyImageURL) } + rowCoreViewState: details.rowCoreViewState, + accessory: { unhideButton(asset: .nonFungible(details.token.id)) }, + icon: { Thumbnail(.nft, url: details.resource.metadata.iconURL) } )) } } @@ -122,9 +122,13 @@ extension HiddenAssets { } } -private extension OnLedgerEntity.NonFungibleToken { +private extension HiddenAssets.State.NonFungibleDetails { var rowCoreViewState: PlainListRowCore.ViewState { - .init(context: .hiddenAsset, title: data?.name, subtitle: id.nonFungibleLocalId.formatted()) + .init( + context: .hiddenAsset, + title: resource.metadata.name ?? token.id.resourceAddress.formatted(), + subtitle: token.data?.name ?? token.id.localID.formatted() + ) } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 793a1d39b7..6ee634fea7 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -6,12 +6,17 @@ public struct HiddenAssets: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { var fungible: [OnLedgerEntity.Resource] = [] - var nonFungible: [OnLedgerEntity.NonFungibleToken] = [] + var nonFungible: [NonFungibleDetails] = [] var poolUnit: [PoolUnitDetails] = [] @Presents var destination: Destination.State? = nil + public struct NonFungibleDetails: Sendable, Hashable { + let resource: OnLedgerEntity.Resource + let token: OnLedgerEntity.NonFungibleToken + } + public struct PoolUnitDetails: Sendable, Hashable { let resource: OnLedgerEntity.Resource let details: OnLedgerEntitiesClient.OwnedResourcePoolDetails @@ -29,7 +34,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { public enum InternalAction: Sendable, Equatable { case loadAssets([AssetAddress]) case setFungible([OnLedgerEntity.Resource]) - case setNonFungible([OnLedgerEntity.NonFungibleToken]) + case setNonFungible([State.NonFungibleDetails]) case setPoolUnit([State.PoolUnitDetails]) case didUnhideAsset(AssetAddress) } @@ -111,7 +116,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case let .fungible(resourceAddress): state.fungible.removeAll(where: { $0.resourceAddress == resourceAddress }) case let .nonFungible(globalId): - state.nonFungible.removeAll(where: { $0.id == globalId }) + state.nonFungible.removeAll(where: { $0.token.id == globalId }) case let .poolUnit(poolAddress): state.poolUnit.removeAll(where: { $0.details.address == poolAddress }) } @@ -147,12 +152,16 @@ public struct HiddenAssets: Sendable, FeatureReducer { private func nonFungibleEffect(hiddenAssets: [AssetAddress]) -> Effect { .run { send in - let tokens = try await hiddenAssets.nonFungibleDictionary.parallelMap { resource, nonFungibleIds in - try await onLedgerEntitiesClient.getNonFungibleTokenData(.init(resource: resource, nonFungibleIds: nonFungibleIds)) + let nonFungibleDetails = try await hiddenAssets.nonFungibleDictionary.parallelMap { resourceAddress, nonFungibleIds in + let resource = try await onLedgerEntitiesClient.getResource(resourceAddress) + let tokens = try await onLedgerEntitiesClient.getNonFungibleTokenData(.init(resource: resourceAddress, nonFungibleIds: nonFungibleIds)) + return tokens.map { + State.NonFungibleDetails(resource: resource, token: $0) + } } .flatMap { $0 } - .sorted(by: \.id.resourceAddress.address) - await send(.internal(.setNonFungible(tokens))) + + await send(.internal(.setNonFungible(nonFungibleDetails))) } } From 445bcb970773323e48f083df4836bc93861044bd Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 18:00:38 +0200 Subject: [PATCH 20/34] localisation --- .../HelperViews/ResourceBalance/ResourceBalance.swift | 2 +- .../TransactionReviewAccount+View.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift index 04668493bc..d37ab0c966 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalance.swift @@ -11,7 +11,7 @@ public struct ResourceBalance: Sendable, Hashable { public let resource: OnLedgerEntity.Resource public var details: Details - /// Indicates whether the reosurce is hidden in user's profile. + /// Indicates whether the resource is hidden in user's profile. /// Value is optional since we won't check for cases that it doesn't matter. public let isHidden: Bool? diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift index a25451fd0f..5e9c1d1d33 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift @@ -113,7 +113,7 @@ struct TransactionReviewResourceView: View { guard let isHidden = transfer.isHidden, isHidden else { return nil } - return "This asset is hidden and will not be visible in your Account" + return L10n.TransactionReview.hiddenAsset } } From 539f6083ccf685f449c52dd9047cf6f3064ae873 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 20 Aug 2024 20:14:47 +0200 Subject: [PATCH 21/34] Fix prices after unhiding asset --- .../AccountPortfoliosClient+Live.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index e3eeb90fef..2e344133dd 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -30,7 +30,7 @@ extension AccountPortfoliosClient: DependencyKey { for try await _ in await appPreferencesClient.appPreferenceUpdates().map(\.assets.hiddenAssets) { guard !Task.isCancelled else { return } let accountAddresses = state.portfoliosSubject.value.wrappedValue.map { $0.map(\.key) } ?? [] - _ = try await fetchAccountPortfolios(accountAddresses, false) + _ = try await fetchAccountPortfolios(accountAddresses, forceRefreshEntities: false, forceRefreshPrices: true) } } @@ -77,11 +77,12 @@ extension AccountPortfoliosClient: DependencyKey { @Sendable func fetchAccountPortfolios( _ accountAddresses: [AccountAddress], - _ forceRefresh: Bool + forceRefreshEntities: Bool, + forceRefreshPrices: Bool ) async throws -> [AccountPortfolio] { let gateway = await gatewaysClient.getCurrentGateway() await state.setRadixGateway(gateway) - if forceRefresh { + if forceRefreshEntities { for accountAddress in accountAddresses { cacheClient.removeFolder(.init(address: accountAddress)) } @@ -131,11 +132,11 @@ extension AccountPortfoliosClient: DependencyKey { } }() - await applyTokenPrices(Array(allResources), forceRefresh: forceRefresh) + await applyTokenPrices(Array(allResources), forceRefresh: forceRefreshPrices) // Load additional details _ = await accounts.map(\.nonEmptyVaults).parallelMap { - await fetchPoolAndStakeUnitsDetails($0, hiddenAssets: hiddenAssets, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) + await fetchPoolAndStakeUnitsDetails($0, hiddenAssets: hiddenAssets, cachingStrategy: forceRefreshEntities ? .forceUpdate : .useCache) } return Array(state.portfoliosSubject.value.wrappedValue!.values) @@ -170,7 +171,7 @@ extension AccountPortfoliosClient: DependencyKey { return AccountPortfoliosClient( fetchAccountPortfolios: { accountAddresses, forceRefresh in try await Task.detached { - try await fetchAccountPortfolios(accountAddresses, forceRefresh) + try await fetchAccountPortfolios(accountAddresses, forceRefreshEntities: forceRefresh, forceRefreshPrices: forceRefresh) }.value }, fetchAccountPortfolio: { accountAddress, forceRefresh in From d7eeb34e5c808b565b8f7be99d8d8ec75735f0ad Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Wed, 21 Aug 2024 09:41:50 +0200 Subject: [PATCH 22/34] feedback changes --- .../Components/HideAsset/HideAsset+View.swift | 1 - .../Components/Row/NonFungbileAssetRow+Reducer.swift | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift index dc27f69edf..1d8d33fc31 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift @@ -1,5 +1,4 @@ // MARK: - HideAsset.View - public extension HideAsset { struct View: SwiftUI.View { private let store: StoreOf diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift index fa9557ced6..28634f294c 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift @@ -151,7 +151,12 @@ extension NonFungibleAssetList { } private extension NonFungibleAssetList.Row.InternalAction.TokensLoadResult { - init(tokens: [OnLedgerEntity.NonFungibleToken], hiddenAssets: [AssetAddress], nextPageCursor: String?, previousTokenIndex: Int) { + init( + tokens: [OnLedgerEntity.NonFungibleToken], + hiddenAssets: [AssetAddress], + nextPageCursor: String?, + previousTokenIndex: Int + ) { self.tokens = tokens.filter { token in !hiddenAssets.contains(.nonFungible(token.id)) } From 6d801f3790cc9a181466af3dcc083088fc4dd98d Mon Sep 17 00:00:00 2001 From: matiasbzurovski <164921079+matiasbzurovski@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:40:52 +0200 Subject: [PATCH 23/34] [ABW-3553] Entities hiding (#1294) --- RadixWallet.xcodeproj/project.pbxproj | 44 +++--- .../EntitiesVisibilityClient+Interface.swift | 48 +++---- .../EntitiesVisibilityClient+Live.swift | 25 ++-- .../EntitiesVisibilityClient+Test.swift | 18 +-- .../AccountCard/AccountCard.swift | 5 + .../AccountAndPersonaHiding+Reducer.swift | 111 -------------- .../AccountAndPersonaHiding+View.swift | 86 ----------- .../HiddenEntities/HiddenEntities+View.swift | 99 +++++++++++++ .../HiddenEntities/HiddenEntities.swift | 135 ++++++++++++++++++ .../Preferences/Preferences+View.swift | 2 +- .../Preferences/Preferences.swift | 6 +- ...Stage2MigrateToSargon+ProfileNetwork.swift | 46 +++--- .../AccountAndPersonaHidingTests.swift | 111 -------------- .../AccountPreferencesTests.swift | 10 +- .../ProfileTests/EntitiesHidingTests.swift | 4 +- .../TestExtensions/TestUtils.swift | 4 +- 16 files changed, 331 insertions(+), 423 deletions(-) delete mode 100644 RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+Reducer.swift delete mode 100644 RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+View.swift create mode 100644 RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift create mode 100644 RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities.swift delete mode 100644 RadixWalletTests/Features/AccountAndPersonaHidingTests/AccountAndPersonaHidingTests.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 50cd10e572..ea7d0b1a15 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -871,6 +871,8 @@ 48FFFB082ADC6FD300B2B213 /* SwiftUINavigationCore in Frameworks */ = {isa = PBXBuildFile; productRef = 48FFFB072ADC6FD300B2B213 /* SwiftUINavigationCore */; }; 48FFFB0A2ADC721800B2B213 /* Atomics in Frameworks */ = {isa = PBXBuildFile; productRef = 48FFFB092ADC721800B2B213 /* Atomics */; }; 48FFFB0D2ADC744700B2B213 /* TextBuilder in Frameworks */ = {isa = PBXBuildFile; productRef = 48FFFB0C2ADC744700B2B213 /* TextBuilder */; }; + 5B135B392C7636DA004AAD2E /* HiddenEntities+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B135B382C7636DA004AAD2E /* HiddenEntities+View.swift */; }; + 5B135B3B2C7636FD004AAD2E /* HiddenEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B135B3A2C7636FD004AAD2E /* HiddenEntities.swift */; }; 5B1C4FD52BBB0B0C00B9436F /* AppsFlyerLib-Strict in Frameworks */ = {isa = PBXBuildFile; productRef = 5B1C4FD42BBB0B0C00B9436F /* AppsFlyerLib-Strict */; }; 5B1C4FD82BBB0C1E00B9436F /* AppsFlyerClient+Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C4FD72BBB0C1E00B9436F /* AppsFlyerClient+Interface.swift */; }; 5B1C4FDA2BBB0DCF00B9436F /* AppsFlyerClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C4FD92BBB0DCF00B9436F /* AppsFlyerClient+Live.swift */; }; @@ -926,10 +928,7 @@ 8308184C2B9F169B002D8351 /* TokenPriceClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8308184B2B9F169B002D8351 /* TokenPriceClient+Live.swift */; }; 8308184E2B9F16AD002D8351 /* TokenPriceClient+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8308184D2B9F16AD002D8351 /* TokenPriceClient+Mock.swift */; }; 830E43AF2C2D8B5100B6E95F /* SessionStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830E43AE2C2D8B5100B6E95F /* SessionStorageTests.swift */; }; - 830EA9D62AE94033004C8051 /* AccountAndPersonaHiding+Reducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9D52AE94033004C8051 /* AccountAndPersonaHiding+Reducer.swift */; }; 830EA9DB2AEA8770004C8051 /* EntitiesVisibilityClient+Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9DA2AEA8770004C8051 /* EntitiesVisibilityClient+Interface.swift */; }; - 830EA9DD2AEB8197004C8051 /* AccountAndPersonaHiding+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9DC2AEB8197004C8051 /* AccountAndPersonaHiding+View.swift */; }; - 830EA9E02AEB8219004C8051 /* AccountAndPersonaHidingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9DF2AEB8219004C8051 /* AccountAndPersonaHidingTests.swift */; }; 830EA9E52AEB9088004C8051 /* DefaultValueDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9E42AEB9088004C8051 /* DefaultValueDecodable.swift */; }; 830EA9E72AEBA793004C8051 /* EntitiesVisibilityClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9E62AEBA793004C8051 /* EntitiesVisibilityClient+Live.swift */; }; 830EA9E92AEBA7C5004C8051 /* EntitiesVisibilityClient+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830EA9E82AEBA7C5004C8051 /* EntitiesVisibilityClient+Test.swift */; }; @@ -2027,6 +2026,8 @@ 48DD0FF62BCBCBB900C54C43 /* Stage1MigrateToSargon+AssetException.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Stage1MigrateToSargon+AssetException.swift"; sourceTree = ""; }; 48FF43142AE43C7C00C568B9 /* TimeLimit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeLimit.swift; sourceTree = ""; }; 48FFFAF12ADC23AC00B2B213 /* Exports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exports.swift; sourceTree = ""; }; + 5B135B382C7636DA004AAD2E /* HiddenEntities+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HiddenEntities+View.swift"; sourceTree = ""; }; + 5B135B3A2C7636FD004AAD2E /* HiddenEntities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenEntities.swift; sourceTree = ""; }; 5B1C4FD72BBB0C1E00B9436F /* AppsFlyerClient+Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppsFlyerClient+Interface.swift"; sourceTree = ""; }; 5B1C4FD92BBB0DCF00B9436F /* AppsFlyerClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppsFlyerClient+Live.swift"; sourceTree = ""; }; 5B272DD62C36E89600B74F1F /* AppEventsClient+Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppEventsClient+Interface.swift"; sourceTree = ""; }; @@ -2081,10 +2082,7 @@ 8308184B2B9F169B002D8351 /* TokenPriceClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TokenPriceClient+Live.swift"; sourceTree = ""; }; 8308184D2B9F16AD002D8351 /* TokenPriceClient+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TokenPriceClient+Mock.swift"; sourceTree = ""; }; 830E43AE2C2D8B5100B6E95F /* SessionStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionStorageTests.swift; sourceTree = ""; }; - 830EA9D52AE94033004C8051 /* AccountAndPersonaHiding+Reducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountAndPersonaHiding+Reducer.swift"; sourceTree = ""; }; 830EA9DA2AEA8770004C8051 /* EntitiesVisibilityClient+Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EntitiesVisibilityClient+Interface.swift"; sourceTree = ""; }; - 830EA9DC2AEB8197004C8051 /* AccountAndPersonaHiding+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountAndPersonaHiding+View.swift"; sourceTree = ""; }; - 830EA9DF2AEB8219004C8051 /* AccountAndPersonaHidingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountAndPersonaHidingTests.swift; sourceTree = ""; }; 830EA9E22AEB8933004C8051 /* EntitiesHidingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntitiesHidingTests.swift; sourceTree = ""; }; 830EA9E42AEB9088004C8051 /* DefaultValueDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultValueDecodable.swift; sourceTree = ""; }; 830EA9E62AEBA793004C8051 /* EntitiesVisibilityClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EntitiesVisibilityClient+Live.swift"; sourceTree = ""; }; @@ -2563,7 +2561,6 @@ 5B45E2F32BC45706007C4C84 /* FactorSourceAccess */, A41557512B7645D40040AD4E /* TransactionHistory */, 48AE39D82B0CBE3900813CF3 /* AccountRecoveryScan */, - 830EA9D42AE94014004C8051 /* AccountAndPersonaHidingFeature */, 48CFBC6C2ADC10D800E77A5C /* AccountPreferencesFeature */, 48CFBC7D2ADC10D800E77A5C /* GatewaySettingsFeature */, 48CFBC8B2ADC10D800E77A5C /* SplashFeature */, @@ -5597,6 +5594,15 @@ path = KeychainClientTests; sourceTree = ""; }; + 5B135B372C7636B6004AAD2E /* HiddenEntities */ = { + isa = PBXGroup; + children = ( + 5B135B382C7636DA004AAD2E /* HiddenEntities+View.swift */, + 5B135B3A2C7636FD004AAD2E /* HiddenEntities.swift */, + ); + path = HiddenEntities; + sourceTree = ""; + }; 5B1C4FD62BBB0C0100B9436F /* AppsFlyerClient */ = { isa = PBXGroup; children = ( @@ -5668,6 +5674,7 @@ 5B758D552BCE7AEC00348722 /* Preferences+View.swift */, 5B758D562BCE7AEC00348722 /* Preferences.swift */, 48CFBD8F2ADC10D800E77A5C /* DefaultDepositGuarantees */, + 5B135B372C7636B6004AAD2E /* HiddenEntities */, 5BFA4FB72C736B500030B517 /* HiddenAssets */, ); path = Preferences; @@ -5739,15 +5746,6 @@ path = Mobile; sourceTree = ""; }; - 830EA9D42AE94014004C8051 /* AccountAndPersonaHidingFeature */ = { - isa = PBXGroup; - children = ( - 830EA9D52AE94033004C8051 /* AccountAndPersonaHiding+Reducer.swift */, - 830EA9DC2AEB8197004C8051 /* AccountAndPersonaHiding+View.swift */, - ); - path = AccountAndPersonaHidingFeature; - sourceTree = ""; - }; 830EA9D92AEA8750004C8051 /* EntitiesVisibilityClient */ = { isa = PBXGroup; children = ( @@ -5758,14 +5756,6 @@ path = EntitiesVisibilityClient; sourceTree = ""; }; - 830EA9DE2AEB8201004C8051 /* AccountAndPersonaHidingTests */ = { - isa = PBXGroup; - children = ( - 830EA9DF2AEB8219004C8051 /* AccountAndPersonaHidingTests.swift */, - ); - path = AccountAndPersonaHidingTests; - sourceTree = ""; - }; 831F0CF02C2576AE00D6F5BF /* DappOriginVerification */ = { isa = PBXGroup; children = ( @@ -6214,7 +6204,6 @@ isa = PBXGroup; children = ( 83EE474E2AF027CE00155F03 /* AssetTransferTests */, - 830EA9DE2AEB8201004C8051 /* AccountAndPersonaHidingTests */, 8328806A2AE6724B0014FBF3 /* AccountPreferencesTests */, E6DBA2782ADEBFB200A38425 /* SettingsFeatureTests */, E6DBA27B2ADEBFB200A38425 /* AppFeatureTests */, @@ -6891,7 +6880,6 @@ E6DBA31C2ADEBFB300A38425 /* RadixConnectE2E.swift in Sources */, E6DBA3282ADEBFB300A38425 /* Misc+Extensions.swift in Sources */, E6DBA3432ADEBFB300A38425 /* UserDefaultsClientTests.swift in Sources */, - 830EA9E02AEB8219004C8051 /* AccountAndPersonaHidingTests.swift in Sources */, E6DBA31A2ADEBFB300A38425 /* DataChannelClientTests.swift in Sources */, E6DBA32B2ADEBFB300A38425 /* JSON+Sendable.swift in Sources */, E6DBA3202ADEBFB300A38425 /* PeerConnectionMocks.swift in Sources */, @@ -7161,7 +7149,6 @@ 48CFC50A2ADC10DA00E77A5C /* InvalidTransactionError.swift in Sources */, 48CFC2BF2ADC10D900E77A5C /* ImportMnemonicsFlowCoordinator.swift in Sources */, 48CFC3632ADC10D900E77A5C /* AssetsView+Reducer.swift in Sources */, - 830EA9DD2AEB8197004C8051 /* AccountAndPersonaHiding+View.swift in Sources */, 48CFC2792ADC10D900E77A5C /* P2PLinkRow+Reducer.swift in Sources */, 48CFC30F2ADC10D900E77A5C /* ImportOlympiaWalletCoordinator.swift in Sources */, E68878592BDBFD42003F3393 /* Stage2MigrateToSargon+LedgerHardwareWalletFactorSource+New.swift in Sources */, @@ -7249,6 +7236,7 @@ 48CFC65C2ADC10DB00E77A5C /* Profile+Persona+Add.swift in Sources */, 48CFC5B52ADC10DA00E77A5C /* AppTextField.swift in Sources */, 48CFC4B42ADC10DA00E77A5C /* TransactionPreviewRequest.swift in Sources */, + 5B135B3B2C7636FD004AAD2E /* HiddenEntities.swift in Sources */, 48CFC5E32ADC10DA00E77A5C /* AttributedString+Extra.swift in Sources */, 48CFC4812ADC10DA00E77A5C /* ROLAFailure.swift in Sources */, 48CFC3662ADC10D900E77A5C /* NonFungibleTokenDetails+Reducer.swift in Sources */, @@ -7343,7 +7331,6 @@ 48CFC6022ADC10DA00E77A5C /* P2P+LedgerHardwareWallet.swift in Sources */, 48CFC46A2ADC10DA00E77A5C /* ImportLegacyWalletClient+Live.swift in Sources */, 5B1C4FD82BBB0C1E00B9436F /* AppsFlyerClient+Interface.swift in Sources */, - 830EA9D62AE94033004C8051 /* AccountAndPersonaHiding+Reducer.swift in Sources */, 48CFC5052ADC10DA00E77A5C /* TransactionPreviewResponseLogsInner.swift in Sources */, 5B1C4FDA2BBB0DCF00B9436F /* AppsFlyerClient+Live.swift in Sources */, 48CFC56B2ADC10DA00E77A5C /* MetadataU8ArrayValue.swift in Sources */, @@ -7364,6 +7351,7 @@ 83EE5EE72BE3C16F00B1531D /* PackageCodeCollection.swift in Sources */, 48CFC36B2ADC10D900E77A5C /* FungibleTokenDetails+Reducer.swift in Sources */, 5BBC7D9F2C3D390E00B04BD6 /* BootstrapClient+Live.swift in Sources */, + 5B135B392C7636DA004AAD2E /* HiddenEntities+View.swift in Sources */, 4855B1C72BCAC82200DD0A47 /* Stage1MigrateToSargon+SignatureOfEntity.swift in Sources */, 83EE5EDD2BE3C16F00B1531D /* StatePackageBlueprintPageRequest.swift in Sources */, 48CFC3322ADC10D900E77A5C /* ImportMnemonic.swift in Sources */, diff --git a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift index 1b1d1705bf..608d4098a8 100644 --- a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift +++ b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift @@ -1,46 +1,32 @@ // MARK: - EntitiesVisibilityClient /// Controls the visibility of the entities in the Wallet public struct EntitiesVisibilityClient: Sendable { - public var hideAccounts: HideAccounts - public var hidePersonas: HidePersonas - public var unhideAllEntities: UnhideAllEntities - public var getHiddenEntityCounts: GetHiddenEntityCounts + public var hideAccount: HideAccount + public var hidePersona: HidePersona + public var unhideAccount: UnhideAccount + public var unhidePersona: UnhidePersona + public var getHiddenEntities: GetHiddenEntities } extension EntitiesVisibilityClient { - public struct HiddenEntityCounts: Hashable, Sendable { - public let hiddenAccountsCount: Int - public let hiddenPersonasCount: Int + public struct HiddenEntities: Hashable, Sendable { + public let accounts: Accounts + public let personas: Personas } - public typealias HideAccounts = @Sendable (Set) async throws -> Void - public typealias HidePersonas = @Sendable (Set) async throws -> Void - public typealias UnhideAllEntities = @Sendable () async throws -> Void - public typealias GetHiddenEntityCounts = @Sendable () async throws -> HiddenEntityCounts + public typealias HideAccount = @Sendable (Account.ID) async throws -> Void + public typealias HidePersona = @Sendable (Persona.ID) async throws -> Void + public typealias UnhideAccount = @Sendable (Account.ID) async throws -> Void + public typealias UnhidePersona = @Sendable (Persona.ID) async throws -> Void + public typealias GetHiddenEntities = @Sendable () async throws -> HiddenEntities } extension EntitiesVisibilityClient { - public func hideAccounts(ids: some Collection) async throws { - try await hideAccounts(Set(ids)) + func hide(account: Account) async throws { + try await hideAccount(account.id) } - public func hidePersonas(ids: some Collection) async throws { - try await hidePersonas(Set(ids)) - } - - public func hide(accounts: some Collection) async throws { - try await hideAccounts(ids: accounts.map(\.id)) - } - - public func hide(personas: some Collection) async throws { - try await hidePersonas(ids: personas.map(\.id)) - } - - public func hide(account: Account) async throws { - try await hide(accounts: [account]) - } - - public func hide(persona: Persona) async throws { - try await hide(personas: [persona]) + func hide(persona: Persona) async throws { + try await hidePersona(persona.id) } } diff --git a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Live.swift b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Live.swift index dd7d22fe48..b2fba7af4c 100644 --- a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Live.swift +++ b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Live.swift @@ -7,28 +7,29 @@ extension EntitiesVisibilityClient: DependencyKey { profileStore: ProfileStore = .shared ) -> Self { .init( - hideAccounts: { idsOfAccounts in + hideAccount: { id in try await profileStore.updatingOnCurrentNetwork { network in - network.hideAccounts(ids: idsOfAccounts) + network.hideAccount(id: id) } }, - hidePersonas: { idsOfPersonas in + hidePersona: { id in try await profileStore.updatingOnCurrentNetwork { network in - network.hidePersonas(ids: idsOfPersonas) + network.hidePersona(id: id) } }, - unhideAllEntities: { + unhideAccount: { id in try await profileStore.updatingOnCurrentNetwork { network in - network.unhideAllEntities() + network.unhideAccount(id: id) } }, - getHiddenEntityCounts: { + unhidePersona: { id in + try await profileStore.updatingOnCurrentNetwork { network in + network.unhidePersona(id: id) + } + }, + getHiddenEntities: { let network = try await profileStore.network() - - return .init( - hiddenAccountsCount: network.getHiddenAccounts().count, - hiddenPersonasCount: network.getHiddenPersonas().count - ) + return .init(accounts: network.getHiddenAccounts(), personas: network.getHiddenPersonas()) } ) } diff --git a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Test.swift b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Test.swift index ec214c6e49..3c6269cacf 100644 --- a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Test.swift +++ b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Test.swift @@ -10,16 +10,18 @@ extension EntitiesVisibilityClient: TestDependencyKey { public static let previewValue = Self.noop public static let noop = Self( - hideAccounts: { _ in throw NoopError() }, - hidePersonas: { _ in throw NoopError() }, - unhideAllEntities: { throw NoopError() }, - getHiddenEntityCounts: { throw NoopError() } + hideAccount: { _ in throw NoopError() }, + hidePersona: { _ in throw NoopError() }, + unhideAccount: { _ in throw NoopError() }, + unhidePersona: { _ in throw NoopError() }, + getHiddenEntities: { throw NoopError() } ) public static let testValue = Self( - hideAccounts: unimplemented("\(Self.self).hideAccounts"), - hidePersonas: unimplemented("\(Self.self).hidePersonas"), - unhideAllEntities: unimplemented("\(Self.self).unhideAllEntities"), - getHiddenEntityCounts: unimplemented("\(Self.self).getHiddenEntityCounts") + hideAccount: unimplemented("\(Self.self).hideAccount"), + hidePersona: unimplemented("\(Self.self).hidePersona"), + unhideAccount: unimplemented("\(Self.self).unhideAccount"), + unhidePersona: unimplemented("\(Self.self).unhidePersona"), + getHiddenEntities: unimplemented("\(Self.self).getHiddenEntities") ) } diff --git a/RadixWallet/Core/FeaturePrelude/AccountCard/AccountCard.swift b/RadixWallet/Core/FeaturePrelude/AccountCard/AccountCard.swift index 17420aaff7..362649cf12 100644 --- a/RadixWallet/Core/FeaturePrelude/AccountCard/AccountCard.swift +++ b/RadixWallet/Core/FeaturePrelude/AccountCard/AccountCard.swift @@ -130,6 +130,11 @@ extension AccountCard.Kind { static var innerCompact: Self { .compact(addCornerRadius: false) } + + /// Behaves same way as `.selection` but it is never selected. + static var hiddenEntity: Self { + .selection(isSelected: false) + } } private extension AccountCard.Kind { diff --git a/RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+Reducer.swift b/RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+Reducer.swift deleted file mode 100644 index c1f65d6177..0000000000 --- a/RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+Reducer.swift +++ /dev/null @@ -1,111 +0,0 @@ -// MARK: - AccountAndPersonaHiding - -public struct AccountAndPersonaHiding: Sendable, FeatureReducer { - // MARK: - State - - public struct State: Hashable, Sendable { - public var hiddenEntityCounts: EntitiesVisibilityClient.HiddenEntityCounts? - - @PresentationState - public var destination: Destination.State? = nil - } - - public enum ViewAction: Hashable, Sendable { - case task - case unhideAllTapped - } - - public enum InternalAction: Hashable, Sendable { - case hiddenEntityCountsLoaded(EntitiesVisibilityClient.HiddenEntityCounts) - case didUnhideAllEntities - } - - public struct Destination: DestinationReducer { - @CasePathable - public enum State: Sendable, Hashable { - case confirmUnhideAllAlert(AlertState) - } - - @CasePathable - public enum Action: Sendable, Equatable { - case confirmUnhideAllAlert(ConfirmUnhideAllAlert) - - public enum ConfirmUnhideAllAlert: Hashable, Sendable { - case confirmTapped - case cancelTapped - } - } - - public var body: some ReducerOf { - EmptyReducer() - } - } - - // MARK: - Reducer - - @Dependency(\.entitiesVisibilityClient) var entitiesVisibilityClient - @Dependency(\.overlayWindowClient) var overlayWindowClient - @Dependency(\.errorQueue) var errorQueue - - public var body: some ReducerOf { - Reduce(core) - .ifLet(destinationPath, action: /Action.destination) { - Destination() - } - } - - private let destinationPath: WritableKeyPath> = \.$destination - - public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { - switch viewAction { - case .task: - return .run { send in - let counts = try await entitiesVisibilityClient.getHiddenEntityCounts() - await send(.internal(.hiddenEntityCountsLoaded(counts))) - } - case .unhideAllTapped: - state.destination = .confirmUnhideAllAlert(.init( - title: .init(L10n.AppSettings.EntityHiding.unhideAllSection), - message: .init(L10n.AppSettings.EntityHiding.unhideAllConfirmation), - buttons: [ - .default(.init(L10n.Common.continue), action: .send(.confirmTapped)), - .cancel(.init(L10n.Common.cancel), action: .send(.cancelTapped)), - ] - )) - return .none - } - } - - public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { - switch internalAction { - case let .hiddenEntityCountsLoaded(counts): - state.hiddenEntityCounts = counts - return .none - case .didUnhideAllEntities: - state.hiddenEntityCounts = .init(hiddenAccountsCount: 0, hiddenPersonasCount: 0) - return .none - } - } - - public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect { - switch presentedAction { - case let .confirmUnhideAllAlert(confirmUnhideAllAlert): - defer { - state.destination = nil - } - - switch confirmUnhideAllAlert { - case .confirmTapped: - return .run { send in - try await entitiesVisibilityClient.unhideAllEntities() - overlayWindowClient.scheduleHUD(.updatedAccount) - await send(.internal(.didUnhideAllEntities)) - } catch: { error, _ in - errorQueue.schedule(error) - } - case .cancelTapped: - return .none - } - } - } -} diff --git a/RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+View.swift b/RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+View.swift deleted file mode 100644 index 7ea9e357a5..0000000000 --- a/RadixWallet/Features/AccountAndPersonaHidingFeature/AccountAndPersonaHiding+View.swift +++ /dev/null @@ -1,86 +0,0 @@ -extension AccountAndPersonaHiding.State { - var viewState: AccountAndPersonaHiding.ViewState { - .init( - hiddenAccountsCount: hiddenEntityCounts?.hiddenAccountsCount ?? 0, - hiddenPersonasCount: hiddenEntityCounts?.hiddenPersonasCount ?? 0 - ) - } -} - -extension AccountAndPersonaHiding { - public struct ViewState: Equatable { - public let hiddenAccountsCount: Int - public let hiddenPersonasCount: Int - - public var hiddenAccountsText: String { - if hiddenAccountsCount == 1 { - L10n.AppSettings.EntityHiding.hiddenAccount(1) - } else { - L10n.AppSettings.EntityHiding.hiddenAccounts(hiddenAccountsCount) - } - } - - public var hiddenPersonasText: String { - if hiddenPersonasCount == 1 { - L10n.AppSettings.EntityHiding.hiddenPersona(1) - } else { - L10n.AppSettings.EntityHiding.hiddenPersonas(hiddenPersonasCount) - } - } - - public var unhideAllButtonControlState: ControlState { - if hiddenAccountsCount > 0 || hiddenPersonasCount > 0 { - .enabled - } else { - .disabled - } - } - } - - public struct View: SwiftUI.View { - public let store: StoreOf - - public var body: some SwiftUI.View { - WithViewStore(store, observe: \.viewState) { viewStore in - VStack(spacing: .medium3) { - Text(L10n.AppSettings.EntityHiding.text) - .textStyle(.body1Regular) - .foregroundColor(.app.gray2) - .flushedLeft - .padding(.top, .medium3) - - VStack(alignment: .leading, spacing: .small3) { - Text(viewStore.hiddenAccountsText) - - Text(viewStore.hiddenPersonasText) - } - .textStyle(.body1Header) - .foregroundColor(.app.gray2) - .centered - - Text(L10n.AppSettings.EntityHiding.unhideAllSection) - .textStyle(.body1HighImportance) - .foregroundColor(.app.gray2) - .flushedLeft - - Button(L10n.AppSettings.EntityHiding.unhideAllButton) { - viewStore.send(.view(.unhideAllTapped)) - } - .buttonStyle(.secondaryRectangular(shouldExpand: true)) - .controlState(viewStore.unhideAllButtonControlState) - - Spacer() - } - .padding(.horizontal, .medium3) - .task { @MainActor in - await viewStore.send(.view(.task)).finish() - } - .alert(store: store.scope( - state: \.$destination.confirmUnhideAllAlert, - action: \.destination.confirmUnhideAllAlert - )) - } - .radixToolbar(title: L10n.AppSettings.EntityHiding.title) - } - } -} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift new file mode 100644 index 0000000000..fcbadc22e7 --- /dev/null +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift @@ -0,0 +1,99 @@ +import SwiftUI + +// MARK: - HiddenEntities.View +extension HiddenEntities { + public struct View: SwiftUI.View { + public let store: StoreOf + + public var body: some SwiftUI.View { + WithPerceptionTracking { + ScrollView { + LazyVStack(alignment: .leading, spacing: .large3) { + Text(L10n.HiddenEntities.text) + .textStyle(.body1HighImportance) + .foregroundColor(.app.gray2) + + header(L10n.HiddenEntities.personas) + personas + + header(L10n.HiddenEntities.accounts) + accounts + } + .padding(.medium3) + } + .background(Color.app.gray5) + .radixToolbar(title: L10n.HiddenEntities.title) + .task { + store.send(.view(.task)) + } + .alert(store: store.scope(state: \.$destination.unhideAlert, action: \.destination.unhideAlert)) + } + } + + private func header(_ value: String) -> some SwiftUI.View { + Text(value) + .textStyle(.secondaryHeader) + .foregroundColor(.app.gray2) + } + + @ViewBuilder + private var personas: some SwiftUI.View { + if store.personas.isEmpty { + empty + } else { + VStack(spacing: .medium3) { + ForEachStatic(store.personas) { persona in + Card { + PlainListRow(viewState: .init( + rowCoreViewState: .init(context: .hiddenAsset, title: persona.displayName.value), + accessory: { unhideButton(action: .unhidePersonaTapped(persona.id)) }, + icon: { Thumbnail(.persona, url: nil) } + )) + } + } + } + } + } + + @ViewBuilder + private var accounts: some SwiftUI.View { + if store.accounts.isEmpty { + empty + } else { + VStack(spacing: .medium3) { + ForEachStatic(store.accounts) { account in + Card { + AccountCard(kind: .hiddenEntity, account: account) { + unhideButton(action: .unhideAccountTapped(account.id)) + } + } + } + } + } + } + + private func unhideButton(action: ViewAction) -> some SwiftUI.View { + Button(L10n.HiddenEntities.unhide) { + store.send(.view(action)) + } + .buttonStyle(.secondaryRectangular(shouldExpand: false)) + } + + private var empty: some SwiftUI.View { + ZStack { + PlainListRow(viewState: .init( + rowCoreViewState: .init(context: .hiddenAsset, title: "dummy"), + accessory: { unhideButton(action: .task) }, + icon: { Thumbnail(.persona, url: nil) } + )) + .hidden() + + Text(L10n.Common.none) + .textStyle(.secondaryHeader) + .foregroundColor(.app.gray2) + } + .background(Color.app.gray4) + .clipShape(RoundedRectangle(cornerRadius: .medium3)) + } + } +} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities.swift new file mode 100644 index 0000000000..72e381b2a4 --- /dev/null +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities.swift @@ -0,0 +1,135 @@ +import ComposableArchitecture + +// MARK: - HiddenEntities +@Reducer +public struct HiddenEntities: Sendable, FeatureReducer { + @ObservableState + public struct State: Sendable, Hashable { + var personas: Personas = [] + var accounts: Accounts = [] + + @Presents + var destination: Destination.State? = nil + } + + public typealias Action = FeatureAction + + @CasePathable + public enum ViewAction: Sendable, Equatable { + case task + case unhidePersonaTapped(Persona.ID) + case unhideAccountTapped(Account.ID) + } + + public enum InternalAction: Sendable, Equatable { + case setEntities(EntitiesVisibilityClient.HiddenEntities) + case didUnhidePersona(Persona.ID) + case didUnhideAccount(Account.ID) + } + + public struct Destination: DestinationReducer { + @CasePathable + public enum State: Sendable, Hashable { + case unhideAlert(AlertState) + } + + @CasePathable + public enum Action: Sendable, Equatable { + case unhideAlert(UnhideAlert) + + public enum UnhideAlert: Hashable, Sendable { + case confirmPersonaTapped(Persona.ID) + case confirmAccountTapped(Account.ID) + case cancelTapped + } + } + + public var body: some ReducerOf { + EmptyReducer() + } + } + + @Dependency(\.entitiesVisibilityClient) var entitiesVisibilityClient + + public init() {} + + public var body: some ReducerOf { + Reduce(core) + } + + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { + switch viewAction { + case .task: + return .run { send in + let hiddenEntities = try await entitiesVisibilityClient.getHiddenEntities() + await send(.internal(.setEntities(hiddenEntities))) + } + case let .unhidePersonaTapped(personaId): + state.destination = .unhideAlert(.init( + title: { TextState(L10n.HiddenEntities.unhidePersonasConfirmation) }, + actions: { + ButtonState(role: .cancel, action: .cancelTapped) { + TextState(L10n.Common.cancel) + } + ButtonState(action: .confirmPersonaTapped(personaId)) { + TextState(L10n.Common.confirm) + } + } + )) + return .none + case let .unhideAccountTapped(accountId): + state.destination = .unhideAlert(.init( + title: { TextState(L10n.HiddenEntities.unhideAccountsConfirmation) }, + actions: { + ButtonState(role: .cancel, action: .cancelTapped) { + TextState(L10n.Common.cancel) + } + ButtonState(action: .confirmAccountTapped(accountId)) { + TextState(L10n.Common.confirm) + } + } + )) + return .none + } + } + + public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { + switch internalAction { + case let .setEntities(hiddenEntities): + state.personas = hiddenEntities.personas + state.accounts = hiddenEntities.accounts + return .none + + case let .didUnhidePersona(personaId): + state.personas.remove(id: personaId) + state.destination = nil + return .none + + case let .didUnhideAccount(accountId): + state.accounts.remove(id: accountId) + state.destination = nil + return .none + } + } + + public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect { + switch presentedAction { + case let .unhideAlert(action): + switch action { + case let .confirmPersonaTapped(personaId): + return .run { send in + try await entitiesVisibilityClient.unhidePersona(personaId) + await send(.internal(.didUnhidePersona(personaId)), animation: .default) + } + case let .confirmAccountTapped(accountId): + return .run { send in + try await entitiesVisibilityClient.unhideAccount(accountId) + await send(.internal(.didUnhideAccount(accountId)), animation: .default) + } + case .cancelTapped: + state.destination = nil + return .none + } + } + } +} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift index bed6d39f54..c8035cee4f 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/Preferences+View.swift @@ -170,7 +170,7 @@ private extension View { private func hiddenEntities(with destinationStore: PresentationStoreOf) -> some View { navigationDestination(store: destinationStore.scope(state: \.hiddenEntities, action: \.hiddenEntities)) { - AccountAndPersonaHiding.View(store: $0) + HiddenEntities.View(store: $0) } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift b/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift index d3bd995cd8..0c57b48622 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/Preferences.swift @@ -30,7 +30,7 @@ public struct Preferences: Sendable, FeatureReducer { @CasePathable public enum State: Sendable, Hashable { case depositGuarantees(DefaultDepositGuarantees.State) - case hiddenEntities(AccountAndPersonaHiding.State) + case hiddenEntities(HiddenEntities.State) case hiddenAssets(HiddenAssets.State) case gateways(GatewaySettings.State) } @@ -38,7 +38,7 @@ public struct Preferences: Sendable, FeatureReducer { @CasePathable public enum Action: Sendable, Equatable { case depositGuarantees(DefaultDepositGuarantees.Action) - case hiddenEntities(AccountAndPersonaHiding.Action) + case hiddenEntities(HiddenEntities.Action) case hiddenAssets(HiddenAssets.Action) case gateways(GatewaySettings.Action) } @@ -48,7 +48,7 @@ public struct Preferences: Sendable, FeatureReducer { DefaultDepositGuarantees() } Scope(state: \.hiddenEntities, action: \.hiddenEntities) { - AccountAndPersonaHiding() + HiddenEntities() } Scope(state: \.hiddenAssets, action: \.hiddenAssets) { HiddenAssets() diff --git a/RadixWallet/MIGRATE_TO_SARGON/Stage2/Atoms/Stage2MigrateToSargon+ProfileNetwork.swift b/RadixWallet/MIGRATE_TO_SARGON/Stage2/Atoms/Stage2MigrateToSargon+ProfileNetwork.swift index a3862a42ee..0325974ae7 100644 --- a/RadixWallet/MIGRATE_TO_SARGON/Stage2/Atoms/Stage2MigrateToSargon+ProfileNetwork.swift +++ b/RadixWallet/MIGRATE_TO_SARGON/Stage2/Atoms/Stage2MigrateToSargon+ProfileNetwork.swift @@ -55,19 +55,23 @@ extension ProfileNetwork { accounts = identified.elements } - public mutating func hideAccounts(ids idsOfAccountsToHide: Set) { + public mutating func hideAccount(id: Account.ID) { var identified = accounts.asIdentified() - for id in idsOfAccountsToHide { - identified[id: id]?.hide() - authorizedDapps.mutateAll { dapp in - dapp.referencesToAuthorizedPersonas.mutateAll { persona in - persona.sharedAccounts?.ids.removeAll(where: { $0 == id }) - } + identified[id: id]?.hide() + authorizedDapps.mutateAll { dapp in + dapp.referencesToAuthorizedPersonas.mutateAll { persona in + persona.sharedAccounts?.ids.removeAll(where: { $0 == id }) } } accounts = identified.elements } + public mutating func unhideAccount(id: Account.ID) { + var identifiedAccounts = accounts.asIdentified() + identifiedAccounts[id: id]?.unhide() + accounts = identifiedAccounts.elements + } + public func getPersonas() -> Personas { personas.asIdentified().nonHidden } @@ -102,19 +106,18 @@ extension ProfileNetwork { self.personas = identifiedPersonas.elements } - public mutating func hidePersonas(ids idsOfPersonaToHide: Set) { + public mutating func hidePersona(id: Persona.ID) { var identifiedPersonas = personas.asIdentified() var identifiedAuthorizedDapps = authorizedDapps.asIdentified() - for id in idsOfPersonaToHide { - /// Hide the personas themselves - identifiedPersonas[id: id]?.hide() - - /// Remove the persona reference on any authorized dapp - identifiedAuthorizedDapps.mutateAll { dapp in - var referencesToAuthorizedPersonas = dapp.referencesToAuthorizedPersonas.asIdentified() - referencesToAuthorizedPersonas.remove(id: id) - dapp.referencesToAuthorizedPersonas = referencesToAuthorizedPersonas.elements - } + + /// Hide the persona themself + identifiedPersonas[id: id]?.hide() + + /// Remove the persona reference on any authorized dapp + identifiedAuthorizedDapps.mutateAll { dapp in + var referencesToAuthorizedPersonas = dapp.referencesToAuthorizedPersonas.asIdentified() + referencesToAuthorizedPersonas.remove(id: id) + dapp.referencesToAuthorizedPersonas = referencesToAuthorizedPersonas.elements } self.personas = identifiedPersonas.elements @@ -123,12 +126,9 @@ extension ProfileNetwork { self.authorizedDapps = identifiedAuthorizedDapps.elements } - public mutating func unhideAllEntities() { - var identifiedAccounts = accounts.asIdentified() + public mutating func unhidePersona(id: Persona.ID) { var identifiedPersonas = personas.asIdentified() - identifiedAccounts.mutateAll { $0.unhide() } - identifiedPersonas.mutateAll { $0.unhide() } - accounts = identifiedAccounts.elements + identifiedPersonas[id: id]?.unhide() personas = identifiedPersonas.elements } diff --git a/RadixWalletTests/Features/AccountAndPersonaHidingTests/AccountAndPersonaHidingTests.swift b/RadixWalletTests/Features/AccountAndPersonaHidingTests/AccountAndPersonaHidingTests.swift deleted file mode 100644 index 39c3a9eb2e..0000000000 --- a/RadixWalletTests/Features/AccountAndPersonaHidingTests/AccountAndPersonaHidingTests.swift +++ /dev/null @@ -1,111 +0,0 @@ -@testable import Radix_Wallet_Dev -import Sargon -import XCTest - -// MARK: - AccountAndPersonaHidingTests -@MainActor -final class AccountAndPersonaHidingTests: TestCase { - func test_unhideAll_happyflow() async throws { - let counts = EntitiesVisibilityClient.HiddenEntityCounts(hiddenAccountsCount: 5, hiddenPersonasCount: 4) - - let store = TestStore( - initialState: AccountAndPersonaHiding.State(), - reducer: AccountAndPersonaHiding.init - ) - - store.dependencies.entitiesVisibilityClient.getHiddenEntityCounts = { - counts - } - - let unhideAllEntitiesExpectation = expectation(description: "Waiting to unhide all entities") - store.dependencies.entitiesVisibilityClient.unhideAllEntities = { - unhideAllEntitiesExpectation.fulfill() - } - - let scheduleCompletionHUD = ActorIsolated(nil) - store.dependencies.overlayWindowClient.scheduleHUD = { hud in - Task { - await scheduleCompletionHUD.setValue(hud) - } - } - - await store.send(.view(.task)) - - await store.receive(.internal(.hiddenEntityCountsLoaded(counts))) { - $0.hiddenEntityCounts = counts - } - - await store.send(.view(.unhideAllTapped)) { - $0.destination = .confirmUnhideAllAlert(.init( - title: .init(L10n.AppSettings.EntityHiding.unhideAllSection), - message: .init(L10n.AppSettings.EntityHiding.unhideAllConfirmation), - buttons: [ - .default(.init(L10n.Common.continue), action: .send(.confirmTapped)), - .cancel(.init(L10n.Common.cancel), action: .send(.cancelTapped)), - ] - )) - } - - await store.send(.destination(.presented(.confirmUnhideAllAlert(.confirmTapped)))) { - $0.destination = nil - } - - wait(for: [unhideAllEntitiesExpectation], timeout: 1.0) - - let scheduledCompletionHUD = await scheduleCompletionHUD.value - XCTAssertEqual(scheduledCompletionHUD, .updatedAccount) - - await store.receive(.internal(.didUnhideAllEntities)) { - $0.hiddenEntityCounts = .init(hiddenAccountsCount: 0, hiddenPersonasCount: 0) - } - } - - func test_unhideAll_failedToUnhide_stateRemainsUnchanged() async throws { - let counts = EntitiesVisibilityClient.HiddenEntityCounts(hiddenAccountsCount: 5, hiddenPersonasCount: 4) - - let store = TestStore( - initialState: AccountAndPersonaHiding.State(), - reducer: AccountAndPersonaHiding.init - ) - - store.dependencies.entitiesVisibilityClient.getHiddenEntityCounts = { - counts - } - - store.dependencies.entitiesVisibilityClient.unhideAllEntities = { - throw NSError.anyError - } - - let scheduleErrorExpectation = expectation(description: "Wait for Error is to bescheduled") - store.dependencies.errorQueue.schedule = { _ in - scheduleErrorExpectation.fulfill() - } - - await store.send(.view(.task)) - - await store.receive(.internal(.hiddenEntityCountsLoaded(counts))) { - $0.hiddenEntityCounts = counts - } - - await store.send(.view(.unhideAllTapped)) { - $0.destination = .confirmUnhideAllAlert(.init( - title: .init(L10n.AppSettings.EntityHiding.unhideAllSection), - message: .init(L10n.AppSettings.EntityHiding.unhideAllConfirmation), - buttons: [ - .default(.init(L10n.Common.continue), action: .send(.confirmTapped)), - .cancel(.init(L10n.Common.cancel), action: .send(.cancelTapped)), - ] - )) - } - - await store.send(.destination(.presented(.confirmUnhideAllAlert(.confirmTapped)))) { - $0.destination = nil - } - - wait(for: [scheduleErrorExpectation], timeout: 1.0) - } -} - -extension NSError { - static let anyError = NSError(domain: "test", code: 1) -} diff --git a/RadixWalletTests/Features/AccountPreferencesTests/AccountPreferencesTests.swift b/RadixWalletTests/Features/AccountPreferencesTests/AccountPreferencesTests.swift index f8df8a25ef..ba6e16a337 100644 --- a/RadixWalletTests/Features/AccountPreferencesTests/AccountPreferencesTests.swift +++ b/RadixWalletTests/Features/AccountPreferencesTests/AccountPreferencesTests.swift @@ -16,9 +16,9 @@ final class AccountPreferencesTests: TestCase { state.destination = .hideAccount } - let idsOfUpdatedAccounts = ActorIsolated?>(nil) - store.dependencies.entitiesVisibilityClient.hideAccounts = { accounts in - await idsOfUpdatedAccounts.setValue(accounts) + let idOfUpdatedAccount = ActorIsolated(nil) + store.dependencies.entitiesVisibilityClient.hideAccount = { account in + await idOfUpdatedAccount.setValue(account) } let scheduleCompletionHUD = ActorIsolated(nil) @@ -32,8 +32,8 @@ final class AccountPreferencesTests: TestCase { state.destination = nil } - let idsOfUpdatedAccounts_ = await idsOfUpdatedAccounts.value - XCTAssertEqual([account.id], idsOfUpdatedAccounts_) + let idOfUpdatedAccount_ = await idOfUpdatedAccount.value + XCTAssertEqual(account.id, idOfUpdatedAccount_) let scheduledCompletionHUD = await scheduleCompletionHUD.value XCTAssertEqual(scheduledCompletionHUD, .accountHidden) diff --git a/RadixWalletTests/ProfileTests/EntitiesHidingTests.swift b/RadixWalletTests/ProfileTests/EntitiesHidingTests.swift index c20fe49007..758f0a713c 100644 --- a/RadixWalletTests/ProfileTests/EntitiesHidingTests.swift +++ b/RadixWalletTests/ProfileTests/EntitiesHidingTests.swift @@ -139,10 +139,10 @@ final class EntitiesHidingTests: TestCase { extension ProfileNetwork { mutating func hide(account: Account) { - hideAccounts(ids: [account.id]) + hideAccount(id: account.id) } mutating func hide(persona: Persona) { - hidePersonas(ids: [persona.id]) + hidePersona(id: persona.id) } } diff --git a/RadixWalletTests/TestExtensions/TestUtils.swift b/RadixWalletTests/TestExtensions/TestUtils.swift index 060f804178..e0fc315e70 100644 --- a/RadixWalletTests/TestExtensions/TestUtils.swift +++ b/RadixWalletTests/TestExtensions/TestUtils.swift @@ -147,8 +147,8 @@ private func configureTestClients( ) { d.device.$name = "Test" d.device.$model = "Test" - d.entitiesVisibilityClient.hideAccounts = { _ in } - d.entitiesVisibilityClient.hidePersonas = { _ in } + d.entitiesVisibilityClient.hideAccount = { _ in } + d.entitiesVisibilityClient.hidePersona = { _ in } d.uuid = .incrementing d.date = .constant(Date(timeIntervalSince1970: 0)) d.mnemonicClient.generate = { _, _ in Mnemonic.sample } From 1d1382425573d0f8488b9eb91ce043eaac84e214 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 13:08:34 +0200 Subject: [PATCH 24/34] wip --- RadixWallet.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../AccountPortfoliosClient+Live.swift | 6 +- .../AccountPortfoliosClient+State.swift | 29 ++----- .../AppPreferencesClient+Interface.swift | 6 +- .../GatewayAPIClient+Interface.swift | 2 - .../OnLedgerEntitiesClient+ComplexTypes.swift | 12 +-- .../OnLedgerEntitiesClient+Interface.swift | 2 +- .../Components/HideAsset/HideAsset.swift | 6 +- .../NonFungibleTokenDetails+Reducer.swift | 4 +- .../Row/NonFungbileAssetRow+Reducer.swift | 18 +---- .../HiddenAssets/HiddenAssets+View.swift | 14 ++-- .../HiddenAssets/HiddenAssets.swift | 76 +++++++++---------- 13 files changed, 72 insertions(+), 109 deletions(-) diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index ea7d0b1a15..4a08a4d85f 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -8741,7 +8741,7 @@ repositoryURL = "https://github.com/radixdlt/sargon"; requirement = { kind = exactVersion; - version = 1.0.38; + version = 1.0.39; }; }; A415574E2B757C5E0040AD4E /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { diff --git a/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a3585cbf05..9db64a4e66 100644 --- a/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RadixWallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -114,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/radixdlt/sargon", "state" : { - "revision" : "e028504a8f6a167c3eb0b7098a8b3a8b728483d3", - "version" : "1.0.38" + "revision" : "36fdd3f46239e063e94b732dd59ef69d4a17db2e", + "version" : "1.0.39" } }, { diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index 2e344133dd..62483579a1 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -27,7 +27,7 @@ extension AccountPortfoliosClient: DependencyKey { /// Update when hidden assets change Task { - for try await _ in await appPreferencesClient.appPreferenceUpdates().map(\.assets.hiddenAssets) { + for try await _ in await appPreferencesClient.appPreferenceUpdates().map(\.resources.hiddenResources) { guard !Task.isCancelled else { return } let accountAddresses = state.portfoliosSubject.value.wrappedValue.map { $0.map(\.key) } ?? [] _ = try await fetchAccountPortfolios(accountAddresses, forceRefreshEntities: false, forceRefreshPrices: true) @@ -36,7 +36,7 @@ extension AccountPortfoliosClient: DependencyKey { /// Fetches the pool and stake units details for a given account; Will update the portfolio accordingly @Sendable - func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [AssetAddress], cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { + func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [ResourceIdentifier], cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { async let poolDetailsFetch = Task { do { let poolUnitDetails = try await onLedgerEntitiesClient.getOwnedPoolUnitsDetails(account, hiddenAssets: hiddenAssets, cachingStrategy: cachingStrategy) @@ -96,7 +96,7 @@ extension AccountPortfoliosClient: DependencyKey { await state.setIsCurrencyAmountVisble(display.isCurrencyAmountVisible) let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses) - let hiddenAssets = preferences.assets.hiddenAssets + let hiddenAssets = preferences.resources.hiddenResources let portfolios = accounts.map { AccountPortfolio(account: $0, hiddenAssets: hiddenAssets) } await state.handlePortfoliosUpdate(portfolios) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift index 8ed93bbcff..72bee13ce0 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift @@ -4,7 +4,7 @@ import Foundation extension AccountPortfoliosClient { public struct AccountPortfolio: Sendable, Hashable, CustomDebugStringConvertible { public var account: OnLedgerEntity.OnLedgerAccount - public let hiddenAssets: [AssetAddress] + public let hiddenAssets: [ResourceIdentifier] public var poolUnitDetails: Loadable<[OnLedgerEntitiesClient.OwnedResourcePoolDetails]> = .idle public var stakeUnitDetails: Loadable> = .idle @@ -14,7 +14,7 @@ extension AccountPortfoliosClient { account.debugDescription } - init(account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [AssetAddress]) { + init(account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [ResourceIdentifier]) { var modified = account // Remove every hidden fungible resource @@ -22,31 +22,16 @@ extension AccountPortfoliosClient { hiddenAssets.contains(.fungible(resource.resourceAddress)) }) + // Remove every hidden non fungible resource + modified.nonFungibleResources.removeAll(where: { resource in + hiddenAssets.contains(.nonFungible(resource.resourceAddress)) + }) + // Remove every hidden pool unit modified.poolUnitResources.poolUnits.removeAll(where: { poolUnit in hiddenAssets.contains(.poolUnit(poolUnit.resourcePoolAddress)) }) - // We cannot remove the NFTs, since at this moment we haven't loaded them yet. - // All we can do is update the `nonFungibleIdsCount` to remove the hidden ones. - // Then, when loading the actual tokens, we will filter those that are hidden. - var nonFungibleResources: [OnLedgerEntity.OwnedNonFungibleResource] = [] - for var item in modified.nonFungibleResources { - let countHidden = hiddenAssets.filter { asset in - switch asset { - case let .nonFungible(globalId): - globalId.resourceAddress == item.resourceAddress - case .fungible, .poolUnit: - false - } - }.count - item.nonFungibleIdsCount -= countHidden - if item.nonFungibleIdsCount > 0 { - nonFungibleResources.append(item) - } - } - modified.nonFungibleResources = nonFungibleResources - self.account = modified self.hiddenAssets = hiddenAssets } diff --git a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift index 11e1978483..607d3a38a3 100644 --- a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift +++ b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift @@ -71,11 +71,11 @@ extension AppPreferencesClient { } } - public func getHiddenAssets() async -> [AssetAddress] { - await getPreferences().assets.hiddenAssets + public func getHiddenAssets() async -> [ResourceIdentifier] { + await getPreferences().resources.hiddenResources } - public func isAssetHidden(asset: AssetAddress) async -> Bool { + public func isAssetHidden(asset: ResourceIdentifier) async -> Bool { await getHiddenAssets().contains(asset) } } diff --git a/RadixWallet/Clients/GatewayAPI/GatewayAPIClient/GatewayAPIClient+Interface.swift b/RadixWallet/Clients/GatewayAPI/GatewayAPIClient/GatewayAPIClient+Interface.swift index 235fa36ae1..cb05a07afe 100644 --- a/RadixWallet/Clients/GatewayAPI/GatewayAPIClient/GatewayAPIClient+Interface.swift +++ b/RadixWallet/Clients/GatewayAPI/GatewayAPIClient/GatewayAPIClient+Interface.swift @@ -1,8 +1,6 @@ import Algorithms import Sargon -public typealias ResourceIdentifier = String - // MARK: - GatewayAPIClient public struct GatewayAPIClient: Sendable, DependencyKey { // MARK: Request diff --git a/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift b/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift index a07238959c..151a2dcedc 100644 --- a/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift +++ b/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift @@ -85,7 +85,7 @@ extension OnLedgerEntitiesClient { resourceAssociatedDapps: TransactionReview.ResourceAssociatedDapps? = nil, networkID: NetworkID, guarantee: TransactionGuarantee?, - hiddenAssets: [AssetAddress] + hiddenAssets: [ResourceIdentifier] ) async throws -> ResourceBalance { let resourceAddress = resource.resourceAddress @@ -160,8 +160,8 @@ extension OnLedgerEntitiesClient { worth = amount * validator.xrdVaultBalance / totalSupply } else { - if let stake = try validatorStakes.first(where: { $0.validatorAddress == validator.address }) { - guard try stake.liquidStakeUnitAddress == validator.stakeUnitResourceAddress else { + if let stake = validatorStakes.first(where: { $0.validatorAddress == validator.address }) { + guard stake.liquidStakeUnitAddress == validator.stakeUnitResourceAddress else { throw StakeUnitAddressMismatch() } // Distribute the worth in proportion to the amounts, if needed @@ -209,12 +209,12 @@ extension OnLedgerEntitiesClient { } } - let newTokens = try ids.filter { id in + let newTokens = ids.filter { id in newlyCreatedNonFungibles.contains { newId in newId.resourceAddress == resourceAddress && newId.nonFungibleLocalId == id } }.map { - try OnLedgerEntity.NonFungibleToken(resourceAddress: resourceAddress, nftID: $0, nftData: nil) + OnLedgerEntity.NonFungibleToken(resourceAddress: resourceAddress, nftID: $0, nftData: nil) } let tokens = try await getNonFungibleTokenData(.init( @@ -236,7 +236,7 @@ extension OnLedgerEntitiesClient { )] } else { result = tokens.map { token in - let isHidden = hiddenAssets.contains(.nonFungible(token.id)) + let isHidden = hiddenAssets.contains(.nonFungible(token.id.resourceAddress)) return ResourceBalance(resource: resource, details: .nonFungible(token), isHidden: isHidden) } diff --git a/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift b/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift index fb06533d6e..ef8b1f9064 100644 --- a/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift +++ b/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift @@ -534,7 +534,7 @@ extension OnLedgerEntitiesClient { @Sendable public func getOwnedPoolUnitsDetails( _ account: OnLedgerEntity.OnLedgerAccount, - hiddenAssets: [AssetAddress], + hiddenAssets: [ResourceIdentifier], cachingStrategy: CachingStrategy = .useCache ) async throws -> [OwnedResourcePoolDetails] { let ownedPoolUnits = account.poolUnitResources.poolUnits.filter { poolUnit in diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index 59ef4c7e37..78a4ab1f95 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -3,13 +3,13 @@ public struct HideAsset: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { - let asset: AssetAddress + let asset: ResourceIdentifier var shouldShow = true @Presents var destination: Destination.State? = nil - public init(asset: AssetAddress) { + public init(asset: ResourceIdentifier) { self.asset = asset } } @@ -112,7 +112,7 @@ private extension HideAsset { func hideAssetEffect(state: State) -> Effect { .run { send in try await appPreferencesClient.updating { preferences in - preferences.assets.hideAsset(asset: state.asset) + preferences.resources.hideResource(resource: state.asset) } await send(.delegate(.didHideAsset)) } diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift index 37dcd11d4d..825dcf04a5 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift @@ -29,8 +29,8 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { self.ledgerState = ledgerState self.stakeClaim = stakeClaim self.isClaimStakeEnabled = isClaimStakeEnabled - if let id = token?.id, stakeClaim == nil { - hideAsset = .init(asset: .nonFungible(id)) + if stakeClaim == nil { + hideAsset = .init(asset: .nonFungible(resourceAddress)) } } } diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift index 28634f294c..b64cce7bac 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungbileAssetRow+Reducer.swift @@ -137,8 +137,7 @@ extension NonFungibleAssetList { return .run { [resource = state.resource, accountAddress = state.accountAddress] send in let result = await TaskResult { let data = try await onLedgerEntitiesClient.getAccountOwnedNonFungibleTokenData(.init(accountAddress: accountAddress, resource: resource, mode: .loadPage(pageCursor: cursor))) - let hiddenAssets = await appPreferencesClient.getHiddenAssets() - return InternalAction.TokensLoadResult(tokens: data.tokens, hiddenAssets: hiddenAssets, nextPageCursor: data.nextPageCursor, previousTokenIndex: previousTokenIndex) + return InternalAction.TokensLoadResult(tokens: data.tokens, nextPageCursor: data.nextPageCursor, previousTokenIndex: previousTokenIndex) } await send(.internal(.tokensLoaded(result))) } @@ -149,18 +148,3 @@ extension NonFungibleAssetList { } } } - -private extension NonFungibleAssetList.Row.InternalAction.TokensLoadResult { - init( - tokens: [OnLedgerEntity.NonFungibleToken], - hiddenAssets: [AssetAddress], - nextPageCursor: String?, - previousTokenIndex: Int - ) { - self.tokens = tokens.filter { token in - !hiddenAssets.contains(.nonFungible(token.id)) - } - self.nextPageCursor = nextPageCursor - self.previousTokenIndex = previousTokenIndex - } -} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 8d22af4880..17a39066af 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -64,13 +64,13 @@ extension HiddenAssets { empty } else { VStack(spacing: .medium3) { - ForEachStatic(store.nonFungible) { details in + ForEachStatic(store.nonFungible) { _ in Card { - PlainListRow(viewState: .init( - rowCoreViewState: details.rowCoreViewState, - accessory: { unhideButton(asset: .nonFungible(details.token.id)) }, - icon: { Thumbnail(.nft, url: details.resource.metadata.iconURL) } - )) +// PlainListRow(viewState: .init( +// rowCoreViewState: details.rowCoreViewState, +// accessory: { unhideButton(asset: .nonFungible(details.token.id)) }, +// icon: { Thumbnail(.nft, url: details.resource.metadata.iconURL) } +// )) } } } @@ -96,7 +96,7 @@ extension HiddenAssets { } } - private func unhideButton(asset: AssetAddress) -> some SwiftUI.View { + private func unhideButton(asset: ResourceIdentifier) -> some SwiftUI.View { Button(L10n.HiddenAssets.unhide) { store.send(.view(.unhideTapped(asset))) } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 6ee634fea7..ae66a1092e 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -28,15 +28,15 @@ public struct HiddenAssets: Sendable, FeatureReducer { @CasePathable public enum ViewAction: Sendable, Equatable { case task - case unhideTapped(AssetAddress) + case unhideTapped(ResourceIdentifier) } public enum InternalAction: Sendable, Equatable { - case loadAssets([AssetAddress]) + case loadAssets([ResourceIdentifier]) case setFungible([OnLedgerEntity.Resource]) case setNonFungible([State.NonFungibleDetails]) case setPoolUnit([State.PoolUnitDetails]) - case didUnhideAsset(AssetAddress) + case didUnhideAsset(ResourceIdentifier) } public struct Destination: DestinationReducer { @@ -50,7 +50,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case unhideAlert(UnhideAlert) public enum UnhideAlert: Hashable, Sendable { - case confirmTapped(AssetAddress) + case confirmTapped(ResourceIdentifier) case cancelTapped } } @@ -115,8 +115,8 @@ public struct HiddenAssets: Sendable, FeatureReducer { switch asset { case let .fungible(resourceAddress): state.fungible.removeAll(where: { $0.resourceAddress == resourceAddress }) - case let .nonFungible(globalId): - state.nonFungible.removeAll(where: { $0.token.id == globalId }) + case let .nonFungible(resourceAddress): + state.nonFungible.removeAll(where: { $0.token.id.resourceAddress == resourceAddress }) case let .poolUnit(poolAddress): state.poolUnit.removeAll(where: { $0.details.address == poolAddress }) } @@ -132,7 +132,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case let .confirmTapped(asset): return .run { send in try await appPreferencesClient.updating { preferences in - preferences.assets.unhideAsset(asset: asset) + preferences.resources.unhideResource(resource: asset) } await send(.internal(.didUnhideAsset(asset)), animation: .default) } @@ -143,29 +143,29 @@ public struct HiddenAssets: Sendable, FeatureReducer { } } - private func fungibleEffect(hiddenAssets: [AssetAddress]) -> Effect { + private func fungibleEffect(hiddenAssets: [ResourceIdentifier]) -> Effect { .run { send in let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.fungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) await send(.internal(.setFungible(resources))) } } - private func nonFungibleEffect(hiddenAssets: [AssetAddress]) -> Effect { - .run { send in - let nonFungibleDetails = try await hiddenAssets.nonFungibleDictionary.parallelMap { resourceAddress, nonFungibleIds in - let resource = try await onLedgerEntitiesClient.getResource(resourceAddress) - let tokens = try await onLedgerEntitiesClient.getNonFungibleTokenData(.init(resource: resourceAddress, nonFungibleIds: nonFungibleIds)) - return tokens.map { - State.NonFungibleDetails(resource: resource, token: $0) - } - } - .flatMap { $0 } - - await send(.internal(.setNonFungible(nonFungibleDetails))) + private func nonFungibleEffect(hiddenAssets: [ResourceIdentifier]) -> Effect { + .run { _ in +// let nonFungibleDetails = try await hiddenAssets.nonFungibleDictionary.parallelMap { resourceAddress, nonFungibleIds in +// let resource = try await onLedgerEntitiesClient.getResource(resourceAddress) +// let tokens = try await onLedgerEntitiesClient.getNonFungibleTokenData(.init(resource: resourceAddress, nonFungibleIds: nonFungibleIds)) +// return tokens.map { +// State.NonFungibleDetails(resource: resource, token: $0) +// } +// } +// .flatMap { $0 } +// +// await send(.internal(.setNonFungible(nonFungibleDetails))) } } - private func poolUnitEffect(hiddenAssets: [AssetAddress]) -> Effect { + private func poolUnitEffect(hiddenAssets: [ResourceIdentifier]) -> Effect { .run { send in let resourcePools = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.poolUnitAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resourcePool) let resources = try await resourcePools.parallelMap { @@ -186,7 +186,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { // MARK: - Helpers -private extension [AssetAddress] { +private extension [ResourceIdentifier] { var fungibleAddresses: [Address] { compactMap { item in switch item { @@ -198,28 +198,24 @@ private extension [AssetAddress] { } } - var nonFungibleDictionary: [ResourceAddress: [NonFungibleGlobalId]] { - let nonFungibleIds = self.compactMap { item in - switch item { - case let .nonFungible(id): - id - case .fungible, .poolUnit: - nil - } - } - return Dictionary(grouping: nonFungibleIds) { - $0.resourceAddress - } - } +// var nonFungibleDictionary: [ResourceAddress: [NonFungibleGlobalId]] { +// let nonFungibleIds = self.compactMap { item -> ResourceAddress? in +// guard case let .nonFungible(id) = item else { +// return nil +// } +// return id +// } +// return Dictionary(grouping: nonFungibleIds) { +// $0.resourceAddress +// } +// } var poolUnitAddresses: [Address] { compactMap { item in - switch item { - case let .poolUnit(poolAddress): - poolAddress.asGeneral - case .fungible, .nonFungible: - nil + guard case let .poolUnit(poolAddress) = item else { + return nil } + return poolAddress.asGeneral } } } From 864f91acc6acf8d95078e86677ebeec7503e7c0d Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 13:30:33 +0200 Subject: [PATCH 25/34] WIP --- .../AccountPortfoliosClient+Live.swift | 16 ++-- .../AccountPortfoliosClient+State.swift | 12 +-- .../AppPreferencesClient+Interface.swift | 6 +- .../OnLedgerEntitiesClient+ComplexTypes.swift | 16 ++-- .../OnLedgerEntitiesClient+Interface.swift | 4 +- .../Components/HideAsset/HideAsset.swift | 2 +- .../HiddenAssets/HiddenAssets+View.swift | 32 +++---- .../HiddenAssets/HiddenAssets.swift | 92 ++++++++----------- 8 files changed, 76 insertions(+), 104 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index 62483579a1..d0ca12f7ca 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -36,10 +36,10 @@ extension AccountPortfoliosClient: DependencyKey { /// Fetches the pool and stake units details for a given account; Will update the portfolio accordingly @Sendable - func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [ResourceIdentifier], cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { + func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.OnLedgerAccount, hiddenResources: [ResourceIdentifier], cachingStrategy: OnLedgerEntitiesClient.CachingStrategy) async { async let poolDetailsFetch = Task { do { - let poolUnitDetails = try await onLedgerEntitiesClient.getOwnedPoolUnitsDetails(account, hiddenAssets: hiddenAssets, cachingStrategy: cachingStrategy) + let poolUnitDetails = try await onLedgerEntitiesClient.getOwnedPoolUnitsDetails(account, hiddenResources: hiddenResources, cachingStrategy: cachingStrategy) await state.set(poolDetails: .success(poolUnitDetails), forAccount: account.address) } catch { await state.set(poolDetails: .failure(error), forAccount: account.address) @@ -96,9 +96,9 @@ extension AccountPortfoliosClient: DependencyKey { await state.setIsCurrencyAmountVisble(display.isCurrencyAmountVisible) let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses) - let hiddenAssets = preferences.resources.hiddenResources + let hiddenResources = preferences.resources.hiddenResources - let portfolios = accounts.map { AccountPortfolio(account: $0, hiddenAssets: hiddenAssets) } + let portfolios = accounts.map { AccountPortfolio(account: $0, hiddenResources: hiddenResources) } await state.handlePortfoliosUpdate(portfolios) /// Put together all resources from already fetched and new accounts @@ -136,7 +136,7 @@ extension AccountPortfoliosClient: DependencyKey { // Load additional details _ = await accounts.map(\.nonEmptyVaults).parallelMap { - await fetchPoolAndStakeUnitsDetails($0, hiddenAssets: hiddenAssets, cachingStrategy: forceRefreshEntities ? .forceUpdate : .useCache) + await fetchPoolAndStakeUnitsDetails($0, hiddenResources: hiddenResources, cachingStrategy: forceRefreshEntities ? .forceUpdate : .useCache) } return Array(state.portfoliosSubject.value.wrappedValue!.values) @@ -152,8 +152,8 @@ extension AccountPortfoliosClient: DependencyKey { } let account = try await onLedgerEntitiesClient.getAccount(accountAddress) - let hiddenAssets = await appPreferencesClient.getHiddenAssets() - let portfolio = AccountPortfolio(account: account, hiddenAssets: hiddenAssets) + let hiddenResources = await appPreferencesClient.getHiddenResources() + let portfolio = AccountPortfolio(account: account, hiddenResources: hiddenResources) if case let .success(tokenPrices) = await state.tokenPrices { await applyTokenPrices( @@ -163,7 +163,7 @@ extension AccountPortfoliosClient: DependencyKey { } await state.handlePortfolioUpdate(portfolio) - await fetchPoolAndStakeUnitsDetails(account.nonEmptyVaults, hiddenAssets: hiddenAssets, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) + await fetchPoolAndStakeUnitsDetails(account.nonEmptyVaults, hiddenResources: hiddenResources, cachingStrategy: forceRefresh ? .forceUpdate : .useCache) return portfolio } diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift index 72bee13ce0..0172a283c5 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+State.swift @@ -4,7 +4,7 @@ import Foundation extension AccountPortfoliosClient { public struct AccountPortfolio: Sendable, Hashable, CustomDebugStringConvertible { public var account: OnLedgerEntity.OnLedgerAccount - public let hiddenAssets: [ResourceIdentifier] + public let hiddenResources: [ResourceIdentifier] public var poolUnitDetails: Loadable<[OnLedgerEntitiesClient.OwnedResourcePoolDetails]> = .idle public var stakeUnitDetails: Loadable> = .idle @@ -14,26 +14,26 @@ extension AccountPortfoliosClient { account.debugDescription } - init(account: OnLedgerEntity.OnLedgerAccount, hiddenAssets: [ResourceIdentifier]) { + init(account: OnLedgerEntity.OnLedgerAccount, hiddenResources: [ResourceIdentifier]) { var modified = account // Remove every hidden fungible resource modified.fungibleResources.nonXrdResources.removeAll(where: { resource in - hiddenAssets.contains(.fungible(resource.resourceAddress)) + hiddenResources.contains(.fungible(resource.resourceAddress)) }) // Remove every hidden non fungible resource modified.nonFungibleResources.removeAll(where: { resource in - hiddenAssets.contains(.nonFungible(resource.resourceAddress)) + hiddenResources.contains(.nonFungible(resource.resourceAddress)) }) // Remove every hidden pool unit modified.poolUnitResources.poolUnits.removeAll(where: { poolUnit in - hiddenAssets.contains(.poolUnit(poolUnit.resourcePoolAddress)) + hiddenResources.contains(.poolUnit(poolUnit.resourcePoolAddress)) }) self.account = modified - self.hiddenAssets = hiddenAssets + self.hiddenResources = hiddenResources } } diff --git a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift index 607d3a38a3..8be2817bc9 100644 --- a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift +++ b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift @@ -71,12 +71,12 @@ extension AppPreferencesClient { } } - public func getHiddenAssets() async -> [ResourceIdentifier] { + public func getHiddenResources() async -> [ResourceIdentifier] { await getPreferences().resources.hiddenResources } - public func isAssetHidden(asset: ResourceIdentifier) async -> Bool { - await getHiddenAssets().contains(asset) + public func isResourceHidden(resource: ResourceIdentifier) async -> Bool { + await getHiddenResources().contains(resource) } } diff --git a/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift b/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift index 151a2dcedc..63d3fb1e0a 100644 --- a/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift +++ b/RadixWallet/Clients/OnLedgerEntitiesClient/Helpers/OnLedgerEntitiesClient+ComplexTypes.swift @@ -30,7 +30,7 @@ extension OnLedgerEntitiesClient { let resourceAddress = resource.resourceAddress @Dependency(\.appPreferencesClient) var appPreferencesClient - let hiddenAssets = await appPreferencesClient.getHiddenAssets() + let hiddenResources = await appPreferencesClient.getHiddenResources() let guarantee: TransactionGuarantee? = { () -> TransactionGuarantee? in guard case let .predicted(predictedAmount) = resourceQuantifier else { return nil } @@ -54,7 +54,7 @@ extension OnLedgerEntitiesClient { resourceAssociatedDapps: resourceAssociatedDapps, networkID: networkID, guarantee: guarantee, - hiddenAssets: hiddenAssets + hiddenResources: hiddenResources ) } @@ -71,7 +71,7 @@ extension OnLedgerEntitiesClient { // Normal fungible resource let isXRD = resourceAddress.isXRD(on: networkID) - let isHidden = hiddenAssets.contains(.fungible(resourceAddress)) + let isHidden = hiddenResources.contains(.fungible(resourceAddress)) let details: ResourceBalance.Fungible = .init(isXRD: isXRD, amount: .init(nominalAmount: amount), guarantee: guarantee) return .init(resource: resource, details: .fungible(details), isHidden: isHidden) @@ -85,7 +85,7 @@ extension OnLedgerEntitiesClient { resourceAssociatedDapps: TransactionReview.ResourceAssociatedDapps? = nil, networkID: NetworkID, guarantee: TransactionGuarantee?, - hiddenAssets: [ResourceIdentifier] + hiddenResources: [ResourceIdentifier] ) async throws -> ResourceBalance { let resourceAddress = resource.resourceAddress @@ -111,7 +111,7 @@ extension OnLedgerEntitiesClient { } } - let isHidden = hiddenAssets.contains(.poolUnit(poolContribution.poolAddress)) + let isHidden = hiddenResources.contains(.poolUnit(poolContribution.poolAddress)) return .init( resource: resource, @@ -132,7 +132,7 @@ extension OnLedgerEntitiesClient { throw FailedToGetPoolUnitDetails() } - let isHidden = hiddenAssets.contains(.poolUnit(details.address)) + let isHidden = hiddenResources.contains(.poolUnit(details.address)) return .init( resource: resource, @@ -201,7 +201,7 @@ extension OnLedgerEntitiesClient { switch resourceInfo { case let .left(resource): @Dependency(\.appPreferencesClient) var appPreferencesClient - let hiddenAssets = await appPreferencesClient.getHiddenAssets() + let hiddenResources = await appPreferencesClient.getHiddenResources() let existingTokenIds = ids.filter { id in !newlyCreatedNonFungibles.contains { newId in @@ -236,7 +236,7 @@ extension OnLedgerEntitiesClient { )] } else { result = tokens.map { token in - let isHidden = hiddenAssets.contains(.nonFungible(token.id.resourceAddress)) + let isHidden = hiddenResources.contains(.nonFungible(token.id.resourceAddress)) return ResourceBalance(resource: resource, details: .nonFungible(token), isHidden: isHidden) } diff --git a/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift b/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift index ef8b1f9064..8a7352ff50 100644 --- a/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift +++ b/RadixWallet/Clients/OnLedgerEntitiesClient/OnLedgerEntitiesClient+Interface.swift @@ -534,11 +534,11 @@ extension OnLedgerEntitiesClient { @Sendable public func getOwnedPoolUnitsDetails( _ account: OnLedgerEntity.OnLedgerAccount, - hiddenAssets: [ResourceIdentifier], + hiddenResources: [ResourceIdentifier], cachingStrategy: CachingStrategy = .useCache ) async throws -> [OwnedResourcePoolDetails] { let ownedPoolUnits = account.poolUnitResources.poolUnits.filter { poolUnit in - !hiddenAssets.contains(.poolUnit(poolUnit.resourcePoolAddress)) + !hiddenResources.contains(.poolUnit(poolUnit.resourcePoolAddress)) } let pools = try await getEntities( ownedPoolUnits.map(\.resourcePoolAddress).map(\.asGeneral), diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift index 78a4ab1f95..506002e953 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift @@ -103,7 +103,7 @@ private extension HideAsset { if isXrd { await send(.internal(.setShouldShow(false))) } else { - let isAlreadyHidden = await appPreferencesClient.isAssetHidden(asset: state.asset) + let isAlreadyHidden = await appPreferencesClient.isResourceHidden(resource: state.asset) await send(.internal(.setShouldShow(!isAlreadyHidden))) } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 17a39066af..e48a5ce151 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -49,7 +49,7 @@ extension HiddenAssets { Card { PlainListRow(viewState: .init( rowCoreViewState: .init(context: .hiddenAsset, title: resource.fungibleResourceName), - accessory: { unhideButton(asset: .fungible(resource.resourceAddress)) }, + accessory: { unhideButton(resource: .fungible(resource.resourceAddress)) }, icon: { Thumbnail(.token(.other), url: resource.metadata.iconURL) } )) } @@ -64,13 +64,13 @@ extension HiddenAssets { empty } else { VStack(spacing: .medium3) { - ForEachStatic(store.nonFungible) { _ in + ForEachStatic(store.nonFungible) { resource in Card { -// PlainListRow(viewState: .init( -// rowCoreViewState: details.rowCoreViewState, -// accessory: { unhideButton(asset: .nonFungible(details.token.id)) }, -// icon: { Thumbnail(.nft, url: details.resource.metadata.iconURL) } -// )) + PlainListRow(viewState: .init( + rowCoreViewState: .init(context: .hiddenAsset, title: resource.metadata.name), + accessory: { unhideButton(resource: .nonFungible(resource.resourceAddress)) }, + icon: { Thumbnail(.nft, url: resource.metadata.iconURL) } + )) } } } @@ -87,7 +87,7 @@ extension HiddenAssets { Card { PlainListRow(viewState: .init( rowCoreViewState: poolUnit.rowCoreViewState, - accessory: { unhideButton(asset: .poolUnit(poolUnit.details.address)) }, + accessory: { unhideButton(resource: .poolUnit(poolUnit.details.address)) }, icon: { Thumbnail(.poolUnit, url: poolUnit.resource.metadata.iconURL) } )) } @@ -96,9 +96,9 @@ extension HiddenAssets { } } - private func unhideButton(asset: ResourceIdentifier) -> some SwiftUI.View { + private func unhideButton(resource: ResourceIdentifier) -> some SwiftUI.View { Button(L10n.HiddenAssets.unhide) { - store.send(.view(.unhideTapped(asset))) + store.send(.view(.unhideTapped(resource))) } .buttonStyle(.secondaryRectangular(shouldExpand: false)) } @@ -107,7 +107,7 @@ extension HiddenAssets { ZStack { PlainListRow(viewState: .init( rowCoreViewState: .init(context: .hiddenAsset, title: "dummy"), - accessory: { unhideButton(asset: .fungible(.mainnetXRD)) }, + accessory: { unhideButton(resource: .fungible(.mainnetXRD)) }, icon: { Thumbnail(.token(.other), url: nil) } )) .hidden() @@ -122,16 +122,6 @@ extension HiddenAssets { } } -private extension HiddenAssets.State.NonFungibleDetails { - var rowCoreViewState: PlainListRowCore.ViewState { - .init( - context: .hiddenAsset, - title: resource.metadata.name ?? token.id.resourceAddress.formatted(), - subtitle: token.data?.name ?? token.id.localID.formatted() - ) - } -} - private extension HiddenAssets.State.PoolUnitDetails { var rowCoreViewState: PlainListRowCore.ViewState { .init(context: .hiddenAsset, title: "-", subtitle: details.dAppName ?? "-") diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index ae66a1092e..2bdcd06465 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -6,17 +6,12 @@ public struct HiddenAssets: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { var fungible: [OnLedgerEntity.Resource] = [] - var nonFungible: [NonFungibleDetails] = [] + var nonFungible: [OnLedgerEntity.Resource] = [] var poolUnit: [PoolUnitDetails] = [] @Presents var destination: Destination.State? = nil - public struct NonFungibleDetails: Sendable, Hashable { - let resource: OnLedgerEntity.Resource - let token: OnLedgerEntity.NonFungibleToken - } - public struct PoolUnitDetails: Sendable, Hashable { let resource: OnLedgerEntity.Resource let details: OnLedgerEntitiesClient.OwnedResourcePoolDetails @@ -32,11 +27,11 @@ public struct HiddenAssets: Sendable, FeatureReducer { } public enum InternalAction: Sendable, Equatable { - case loadAssets([ResourceIdentifier]) + case loadResources([ResourceIdentifier]) case setFungible([OnLedgerEntity.Resource]) - case setNonFungible([State.NonFungibleDetails]) + case setNonFungible([OnLedgerEntity.Resource]) case setPoolUnit([State.PoolUnitDetails]) - case didUnhideAsset(ResourceIdentifier) + case didUnhideResource(ResourceIdentifier) } public struct Destination: DestinationReducer { @@ -73,17 +68,17 @@ public struct HiddenAssets: Sendable, FeatureReducer { switch viewAction { case .task: return .run { send in - let hiddenAssets = await appPreferencesClient.getHiddenAssets() - await send(.internal(.loadAssets(hiddenAssets))) + let hiddenResources = await appPreferencesClient.getHiddenResources() + await send(.internal(.loadResources(hiddenResources))) } - case let .unhideTapped(asset): + case let .unhideTapped(resource): state.destination = .unhideAlert(.init( title: { TextState(L10n.HiddenAssets.unhideConfirmation) }, actions: { ButtonState(role: .cancel, action: .cancelTapped) { TextState(L10n.Common.cancel) } - ButtonState(action: .confirmTapped(asset)) { + ButtonState(action: .confirmTapped(resource)) { TextState(L10n.Common.confirm) } } @@ -94,10 +89,10 @@ public struct HiddenAssets: Sendable, FeatureReducer { public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { switch internalAction { - case let .loadAssets(hiddenAssets): - return fungibleEffect(hiddenAssets: hiddenAssets) - .merge(with: nonFungibleEffect(hiddenAssets: hiddenAssets)) - .merge(with: poolUnitEffect(hiddenAssets: hiddenAssets)) + case let .loadResources(hiddenResources): + return fungibleEffect(hiddenResources: hiddenResources) + .merge(with: nonFungibleEffect(hiddenResources: hiddenResources)) + .merge(with: poolUnitEffect(hiddenResources: hiddenResources)) case let .setFungible(values): state.fungible = values @@ -111,12 +106,12 @@ public struct HiddenAssets: Sendable, FeatureReducer { state.poolUnit = values return .none - case let .didUnhideAsset(asset): - switch asset { + case let .didUnhideResource(resource): + switch resource { case let .fungible(resourceAddress): state.fungible.removeAll(where: { $0.resourceAddress == resourceAddress }) case let .nonFungible(resourceAddress): - state.nonFungible.removeAll(where: { $0.token.id.resourceAddress == resourceAddress }) + state.nonFungible.removeAll(where: { $0.resourceAddress == resourceAddress }) case let .poolUnit(poolAddress): state.poolUnit.removeAll(where: { $0.details.address == poolAddress }) } @@ -129,12 +124,12 @@ public struct HiddenAssets: Sendable, FeatureReducer { switch presentedAction { case let .unhideAlert(action): switch action { - case let .confirmTapped(asset): + case let .confirmTapped(resource): return .run { send in try await appPreferencesClient.updating { preferences in - preferences.resources.unhideResource(resource: asset) + preferences.resources.unhideResource(resource: resource) } - await send(.internal(.didUnhideAsset(asset)), animation: .default) + await send(.internal(.didUnhideResource(resource)), animation: .default) } case .cancelTapped: state.destination = nil @@ -143,31 +138,23 @@ public struct HiddenAssets: Sendable, FeatureReducer { } } - private func fungibleEffect(hiddenAssets: [ResourceIdentifier]) -> Effect { + private func fungibleEffect(hiddenResources: [ResourceIdentifier]) -> Effect { .run { send in - let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.fungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) + let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenResources.fungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) await send(.internal(.setFungible(resources))) } } - private func nonFungibleEffect(hiddenAssets: [ResourceIdentifier]) -> Effect { - .run { _ in -// let nonFungibleDetails = try await hiddenAssets.nonFungibleDictionary.parallelMap { resourceAddress, nonFungibleIds in -// let resource = try await onLedgerEntitiesClient.getResource(resourceAddress) -// let tokens = try await onLedgerEntitiesClient.getNonFungibleTokenData(.init(resource: resourceAddress, nonFungibleIds: nonFungibleIds)) -// return tokens.map { -// State.NonFungibleDetails(resource: resource, token: $0) -// } -// } -// .flatMap { $0 } -// -// await send(.internal(.setNonFungible(nonFungibleDetails))) + private func nonFungibleEffect(hiddenResources: [ResourceIdentifier]) -> Effect { + .run { send in + let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenResources.nonFungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) + await send(.internal(.setNonFungible(resources))) } } - private func poolUnitEffect(hiddenAssets: [ResourceIdentifier]) -> Effect { + private func poolUnitEffect(hiddenResources: [ResourceIdentifier]) -> Effect { .run { send in - let resourcePools = try await onLedgerEntitiesClient.getEntities(addresses: hiddenAssets.poolUnitAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resourcePool) + let resourcePools = try await onLedgerEntitiesClient.getEntities(addresses: hiddenResources.poolUnitAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resourcePool) let resources = try await resourcePools.parallelMap { try await onLedgerEntitiesClient.getResource($0.poolUnitResourceAddress) } @@ -189,26 +176,21 @@ public struct HiddenAssets: Sendable, FeatureReducer { private extension [ResourceIdentifier] { var fungibleAddresses: [Address] { compactMap { item in - switch item { - case let .fungible(resourceAddress): - resourceAddress.asGeneral - case .nonFungible, .poolUnit: - nil + guard case let .fungible(resourceAddress) = item else { + return nil } + return resourceAddress.asGeneral } } -// var nonFungibleDictionary: [ResourceAddress: [NonFungibleGlobalId]] { -// let nonFungibleIds = self.compactMap { item -> ResourceAddress? in -// guard case let .nonFungible(id) = item else { -// return nil -// } -// return id -// } -// return Dictionary(grouping: nonFungibleIds) { -// $0.resourceAddress -// } -// } + var nonFungibleAddresses: [Address] { + compactMap { item in + guard case let .nonFungible(resourceAddress) = item else { + return nil + } + return resourceAddress.asGeneral + } + } var poolUnitAddresses: [Address] { compactMap { item in From d78aa100e9fc28a664b1d485b3c9d2d38b810972 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 15:59:01 +0200 Subject: [PATCH 26/34] update TX review message --- .../TransactionReview+Sections.swift | 4 ++-- .../TransactionReviewAccount+View.swift | 8 +++++--- .../TransactionReviewAccount.swift | 4 +++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift index 61f03954e9..a275967bef 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReview+Sections.swift @@ -418,7 +418,7 @@ extension TransactionReview { guard !withdrawals.isEmpty else { return nil } let withdrawalAccounts = withdrawals.map { - TransactionReviewAccount.State(account: $0.key, transfers: $0.value) + TransactionReviewAccount.State(account: $0.key, transfers: $0.value, isDeposit: false) } .asIdentified() @@ -463,7 +463,7 @@ extension TransactionReview { let depositAccounts = deposits .filter { !$0.value.isEmpty } - .map { TransactionReviewAccount.State(account: $0.key, transfers: $0.value) } + .map { TransactionReviewAccount.State(account: $0.key, transfers: $0.value, isDeposit: true) } .asIdentified() guard !depositAccounts.isEmpty else { return nil } diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift index 5e9c1d1d33..ba7e72ab9b 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount+View.swift @@ -47,7 +47,7 @@ extension TransactionReviewAccounts { extension TransactionReviewAccount.State { var viewState: TransactionReviewAccount.ViewState { - .init(account: account, transfers: transfers.elements, showApprovedMark: account.isApproved) + .init(account: account, transfers: transfers.elements, showApprovedMark: account.isApproved, isDeposit: isDeposit) } } @@ -57,6 +57,7 @@ extension TransactionReviewAccount { let account: TransactionReview.ReviewAccount let transfers: [TransactionReview.Transfer] // FIXME: GK use viewstate? let showApprovedMark: Bool + let isDeposit: Bool } @MainActor @@ -74,7 +75,7 @@ extension TransactionReviewAccount { VStack(spacing: .zero) { ForEach(viewStore.transfers) { transfer in - TransactionReviewResourceView(transfer: transfer.value) { token in + TransactionReviewResourceView(transfer: transfer.value, isDeposit: viewStore.isDeposit) { token in viewStore.send(.transferTapped(transfer.value, token)) } @@ -94,6 +95,7 @@ extension TransactionReviewAccount { // MARK: - TransactionReviewResourceView struct TransactionReviewResourceView: View { let transfer: ResourceBalance // FIXME: GK use viewstate + let isDeposit: Bool let onTap: (OnLedgerEntity.NonFungibleToken?) -> Void var body: some View { @@ -113,7 +115,7 @@ struct TransactionReviewResourceView: View { guard let isHidden = transfer.isHidden, isHidden else { return nil } - return L10n.TransactionReview.hiddenAsset + return isDeposit ? L10n.TransactionReview.HiddenAsset.deposit : L10n.TransactionReview.HiddenAsset.withdraw } } diff --git a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount.swift b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount.swift index 9d2cd44c69..7c2aafdf2e 100644 --- a/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount.swift +++ b/RadixWallet/Features/TransactionReviewFeature/TransactionReviewAccount/TransactionReviewAccount.swift @@ -59,10 +59,12 @@ public struct TransactionReviewAccount: Sendable, FeatureReducer { public var id: AccountAddress { account.address } public let account: TransactionReview.ReviewAccount public var transfers: IdentifiedArrayOf + public let isDeposit: Bool - public init(account: TransactionReview.ReviewAccount, transfers: IdentifiedArrayOf) { + public init(account: TransactionReview.ReviewAccount, transfers: IdentifiedArrayOf, isDeposit: Bool) { self.account = account self.transfers = transfers + self.isDeposit = isDeposit } } From 52928c28ed9fee42541cc631deee84e22ab487b0 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 17:26:30 +0200 Subject: [PATCH 27/34] wip --- RadixWallet.xcodeproj/project.pbxproj | 22 +++--- .../ConfirmationView+Configuration.swift | 8 +++ .../Components/ConfirmationView.swift | 2 +- .../FungibleTokenDetails+Reducer.swift | 12 ++-- .../Details/FungibleTokenDetails+View.swift | 6 +- .../Components/HideAsset/HideAsset+View.swift | 51 ------------- .../HIdeResource.swift} | 40 +++++++---- .../HideResource/HideResource+View.swift | 71 +++++++++++++++++++ .../NonFungibleTokenDetails+Reducer.swift | 31 ++++---- .../NonFungibleTokenDetails+View.swift | 10 +-- .../Components/PoolUnitDetails+View.swift | 6 +- .../Components/PoolUnitDetails.swift | 12 ++-- 12 files changed, 158 insertions(+), 113 deletions(-) delete mode 100644 RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift rename RadixWallet/Features/AssetsFeature/Components/{HideAsset/HideAsset.swift => HideResource/HIdeResource.swift} (74%) create mode 100644 RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index 4a08a4d85f..b2d659e2ee 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -917,8 +917,8 @@ 5BBC7DAA2C403F3400B04BD6 /* AccountCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBC7DA92C403F3400B04BD6 /* AccountCard.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 */; }; - 5BEB9CD22C6F6B24001FD9D4 /* HideAsset+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD02C6F6B24001FD9D4 /* HideAsset+View.swift */; }; - 5BEB9CD32C6F6B24001FD9D4 /* HideAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */; }; + 5BEB9CD22C6F6B24001FD9D4 /* HideResource+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD02C6F6B24001FD9D4 /* HideResource+View.swift */; }; + 5BEB9CD32C6F6B24001FD9D4 /* HIdeResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD12C6F6B24001FD9D4 /* HIdeResource.swift */; }; 5BEB9CD52C6F6CC9001FD9D4 /* ConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */; }; 5BEB9CD72C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */; }; 5BFA4FBA2C736B740030B517 /* HiddenAssets+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */; }; @@ -2071,8 +2071,8 @@ 5BBC7DA92C403F3400B04BD6 /* AccountCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCard.swift; sourceTree = ""; }; 5BC82B6A2BED18A1009AC162 /* FactoryReset+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactoryReset+View.swift"; sourceTree = ""; }; 5BC82B6B2BED18A1009AC162 /* FactoryReset+Reducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactoryReset+Reducer.swift"; sourceTree = ""; }; - 5BEB9CD02C6F6B24001FD9D4 /* HideAsset+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HideAsset+View.swift"; sourceTree = ""; }; - 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideAsset.swift; sourceTree = ""; }; + 5BEB9CD02C6F6B24001FD9D4 /* HideResource+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HideResource+View.swift"; sourceTree = ""; }; + 5BEB9CD12C6F6B24001FD9D4 /* HIdeResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIdeResource.swift; sourceTree = ""; }; 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationView.swift; sourceTree = ""; }; 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationView+Configuration.swift"; sourceTree = ""; }; 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HiddenAssets+View.swift"; sourceTree = ""; }; @@ -3849,7 +3849,7 @@ 48CFBE362ADC10D800E77A5C /* NonFungibleAssetList */, 48CFBE402ADC10D800E77A5C /* FungibleAssetList */, 48CFBE4C2ADC10D800E77A5C /* PoolUnitsList */, - 5BEB9CCF2C6F6AFD001FD9D4 /* HideAsset */, + 5BEB9CCF2C6F6AFD001FD9D4 /* HideResource */, 48CFBE5F2ADC10D800E77A5C /* HelperViews */, 48CFBE662ADC10D800E77A5C /* DetailsContainerWithHeaderView.swift */, ); @@ -5720,13 +5720,13 @@ path = BootstrapClient; sourceTree = ""; }; - 5BEB9CCF2C6F6AFD001FD9D4 /* HideAsset */ = { + 5BEB9CCF2C6F6AFD001FD9D4 /* HideResource */ = { isa = PBXGroup; children = ( - 5BEB9CD02C6F6B24001FD9D4 /* HideAsset+View.swift */, - 5BEB9CD12C6F6B24001FD9D4 /* HideAsset.swift */, + 5BEB9CD02C6F6B24001FD9D4 /* HideResource+View.swift */, + 5BEB9CD12C6F6B24001FD9D4 /* HIdeResource.swift */, ); - path = HideAsset; + path = HideResource; sourceTree = ""; }; 5BFA4FB72C736B500030B517 /* HiddenAssets */ = { @@ -6978,8 +6978,8 @@ 48D5F3982BD8DE0A000DE964 /* DebugInspectProfile.swift in Sources */, 48CFC2872ADC10D900E77A5C /* MinimumPercentageStepper+View.swift in Sources */, E76645A42C23138300065D9A /* Throwable.swift in Sources */, - 5BEB9CD22C6F6B24001FD9D4 /* HideAsset+View.swift in Sources */, - 5BEB9CD32C6F6B24001FD9D4 /* HideAsset.swift in Sources */, + 5BEB9CD22C6F6B24001FD9D4 /* HideResource+View.swift in Sources */, + 5BEB9CD32C6F6B24001FD9D4 /* HIdeResource.swift in Sources */, 5B272DD72C36E89600B74F1F /* AppEventsClient+Interface.swift in Sources */, 48CFC2A12ADC10D900E77A5C /* PersonaFeature.swift in Sources */, 5BFA4FBA2C736B740030B517 /* HiddenAssets+View.swift in Sources */, diff --git a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift index f9f9b79a59..0d0b4350bf 100644 --- a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift +++ b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift @@ -16,4 +16,12 @@ extension ConfirmationView.Configuration { primaryAction: L10n.AssetDetails.HideAsset.button ) } + + static func hideCollection(name: String) -> Self { + .init( + title: L10n.AssetDetails.HideCollection.title, + message: L10n.AssetDetails.HideCollection.message(name), + primaryAction: L10n.AssetDetails.HideCollection.button + ) + } } diff --git a/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift index 5f9192b8bc..e3d244aad1 100644 --- a/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift +++ b/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift @@ -31,7 +31,7 @@ public struct ConfirmationView: View { .textStyle(.sheetTitle) .foregroundColor(.app.gray1) - Text(configuration.message) + Text(markdown: configuration.message, emphasizedColor: .app.gray1, emphasizedFont: .app.body1Header) .textStyle(.body1Regular) .foregroundColor(.app.gray1) } diff --git a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift index 88a91e91fb..fe2f3ba112 100644 --- a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+Reducer.swift @@ -9,7 +9,7 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { let isXRD: Bool let ownedFungibleResource: OnLedgerEntity.OwnedFungibleResource? let ledgerState: AtLedgerState? - var hideAsset: HideAsset.State + var hideResource: HideResource.State public init( resourceAddress: ResourceAddress, @@ -23,7 +23,7 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { self.ownedFungibleResource = ownedFungibleResource self.isXRD = isXRD self.ledgerState = ledgerState - self.hideAsset = .init(asset: .fungible(resourceAddress)) + self.hideResource = .init(kind: .fungible(resourceAddress)) } } @@ -38,7 +38,7 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { @CasePathable public enum ChildAction: Sendable, Equatable { - case hideAsset(HideAsset.Action) + case hideResource(HideResource.Action) } @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient @@ -48,8 +48,8 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { public init() {} public var body: some ReducerOf { - Scope(state: \.hideAsset, action: \.child.hideAsset) { - HideAsset() + Scope(state: \.hideResource, action: \.child.hideResource) { + HideResource() } Reduce(core) @@ -85,7 +85,7 @@ public struct FungibleTokenDetails: Sendable, FeatureReducer { public func reduce(into state: inout State, childAction: ChildAction) -> Effect { switch childAction { - case .hideAsset(.delegate(.didHideAsset)): + case .hideResource(.delegate(.didHideResource)): .run { _ in await dismiss() } default: .none diff --git a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift index c435c45b9c..ff148832a8 100644 --- a/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/FungibleAssetList/Components/Details/FungibleTokenDetails+View.swift @@ -64,7 +64,7 @@ extension FungibleTokenDetails { VStack(spacing: .medium1) { AssetResourceDetailsSection(viewState: viewStore.details) - HideAsset.View(store: store.hideAsset) + HideResource.View(store: store.hideResource) } .padding(.bottom, .medium1) } @@ -77,7 +77,7 @@ extension FungibleTokenDetails { } private extension StoreOf { - var hideAsset: StoreOf { - scope(state: \.hideAsset, action: \.child.hideAsset) + var hideResource: StoreOf { + scope(state: \.hideResource, action: \.child.hideResource) } } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift deleted file mode 100644 index 1d8d33fc31..0000000000 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset+View.swift +++ /dev/null @@ -1,51 +0,0 @@ -// MARK: - HideAsset.View -public extension HideAsset { - struct View: SwiftUI.View { - private let store: StoreOf - - public init(store: StoreOf) { - self.store = store - } - - public var body: some SwiftUI.View { - WithPerceptionTracking { - if store.shouldShow { - Button(L10n.AssetDetails.HideAsset.button) { - store.send(.view(.buttonTapped)) - } - .buttonStyle(.secondaryRectangular(shouldExpand: true)) - .padding(.horizontal, .medium3) - } - } - .task { - await store.send(.view(.task)).finish() - } - .destination(store: store) - } - } -} - -private extension StoreOf { - var destination: PresentationStoreOf { - func scopeState(state: State) -> PresentationState { - state.$destination - } - return scope(state: scopeState, action: Action.destination) - } -} - -@MainActor -private extension View { - func destination(store: StoreOf) -> some View { - let destinationStore = store.destination - return confirmation(with: destinationStore, store: store) - } - - private func confirmation(with destinationStore: PresentationStoreOf, store: StoreOf) -> some View { - sheet(store: destinationStore.scope(state: \.confirmation, action: \.confirmation)) { _ in - ConfirmationView(configuration: .hideAsset) { action in - store.send(.destination(.presented(.confirmation(action)))) - } - } - } -} diff --git a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift b/RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift similarity index 74% rename from RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift rename to RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift index 506002e953..68e196c9ac 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideAsset/HideAsset.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift @@ -1,16 +1,30 @@ -// MARK: - HideAsset +// MARK: - HideResource @Reducer -public struct HideAsset: Sendable, FeatureReducer { +public struct HideResource: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { - let asset: ResourceIdentifier + let kind: Kind var shouldShow = true @Presents var destination: Destination.State? = nil - public init(asset: ResourceIdentifier) { - self.asset = asset + public init(kind: Kind) { + self.kind = kind + } + + public enum Kind: Hashable, Sendable { + case fungible(ResourceAddress) + case nonFungible(ResourceAddress, name: String?) + case poolUnit(PoolAddress) + } + + var resource: ResourceIdentifier { + switch kind { + case let .fungible(address): .fungible(address) + case let .nonFungible(address, _): .nonFungible(address) + case let .poolUnit(address): .poolUnit(address) + } } } @@ -26,7 +40,7 @@ public struct HideAsset: Sendable, FeatureReducer { } public enum DelegateAction: Sendable, Equatable { - case didHideAsset + case didHideResource } // MARK: - Destination @@ -81,7 +95,7 @@ public struct HideAsset: Sendable, FeatureReducer { switch presentedAction { case .confirmation(.confirm): state.destination = nil - return hideAssetEffect(state: state) + return hideResourceEffect(state: state) case .confirmation(.cancel): state.destination = nil return .none @@ -89,11 +103,11 @@ public struct HideAsset: Sendable, FeatureReducer { } } -private extension HideAsset { +private extension HideResource { func shouldShowEffect(state: State) -> Effect { .run { send in let isXrd: Bool - switch state.asset { + switch state.resource { case let .fungible(resource): let networkId = await gatewaysClient.getCurrentNetworkID() isXrd = resource.isXRD(on: networkId) @@ -103,18 +117,18 @@ private extension HideAsset { if isXrd { await send(.internal(.setShouldShow(false))) } else { - let isAlreadyHidden = await appPreferencesClient.isResourceHidden(resource: state.asset) + let isAlreadyHidden = await appPreferencesClient.isResourceHidden(resource: state.resource) await send(.internal(.setShouldShow(!isAlreadyHidden))) } } } - func hideAssetEffect(state: State) -> Effect { + func hideResourceEffect(state: State) -> Effect { .run { send in try await appPreferencesClient.updating { preferences in - preferences.resources.hideResource(resource: state.asset) + preferences.resources.hideResource(resource: state.resource) } - await send(.delegate(.didHideAsset)) + await send(.delegate(.didHideResource)) } } } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift new file mode 100644 index 0000000000..c7fb49f681 --- /dev/null +++ b/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift @@ -0,0 +1,71 @@ +// MARK: - HideResource.View +public extension HideResource { + struct View: SwiftUI.View { + private let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some SwiftUI.View { + WithPerceptionTracking { + if store.shouldShow { + Button(store.title) { + store.send(.view(.buttonTapped)) + } + .buttonStyle(.secondaryRectangular(shouldExpand: true)) + .padding(.horizontal, .medium3) + } + } + .task { + await store.send(.view(.task)).finish() + } + .destination(store: store) + } + } +} + +private extension StoreOf { + var destination: PresentationStoreOf { + func scopeState(state: State) -> PresentationState { + state.$destination + } + return scope(state: scopeState, action: Action.destination) + } +} + +@MainActor +private extension View { + func destination(store: StoreOf) -> some View { + let destinationStore = store.destination + return confirmation(with: destinationStore, store: store) + } + + private func confirmation(with destinationStore: PresentationStoreOf, store: StoreOf) -> some View { + sheet(store: destinationStore.scope(state: \.confirmation, action: \.confirmation)) { _ in + ConfirmationView(configuration: store.confirmationConfiguration) { action in + store.send(.destination(.presented(.confirmation(action)))) + } + } + } +} + +private extension HideResource.State { + var title: String { + switch kind { + case .fungible, .poolUnit: + L10n.AssetDetails.HideAsset.button + case .nonFungible: + L10n.AssetDetails.hideCollectionButton + } + } + + var confirmationConfiguration: ConfirmationView.Configuration { + switch kind { + case .fungible, .poolUnit: + .hideAsset + case let .nonFungible(_, name): + .hideCollection(name: name ?? "") + } + } +} diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift index 825dcf04a5..59df23260e 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+Reducer.swift @@ -4,14 +4,14 @@ import SwiftUI // MARK: - NonFungibleTokenDetails public struct NonFungibleTokenDetails: Sendable, FeatureReducer { public struct State: Sendable, Hashable { - public let resourceAddress: ResourceAddress - public var resourceDetails: Loadable - public let ownedResource: OnLedgerEntity.OwnedNonFungibleResource? - public let token: OnLedgerEntity.NonFungibleToken? - public let ledgerState: AtLedgerState - public let stakeClaim: OnLedgerEntitiesClient.StakeClaim? - public let isClaimStakeEnabled: Bool - var hideAsset: HideAsset.State? + let resourceAddress: ResourceAddress + var resourceDetails: Loadable + let ownedResource: OnLedgerEntity.OwnedNonFungibleResource? + let token: OnLedgerEntity.NonFungibleToken? + let ledgerState: AtLedgerState + let stakeClaim: OnLedgerEntitiesClient.StakeClaim? + let isClaimStakeEnabled: Bool + var hideResource: HideResource.State? public init( resourceAddress: ResourceAddress, @@ -29,8 +29,8 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { self.ledgerState = ledgerState self.stakeClaim = stakeClaim self.isClaimStakeEnabled = isClaimStakeEnabled - if stakeClaim == nil { - hideAsset = .init(asset: .nonFungible(resourceAddress)) + if stakeClaim == nil, case let .success(resource) = resourceDetails { + hideResource = .init(kind: .nonFungible(resourceAddress, name: resource.metadata.name)) } } } @@ -51,7 +51,7 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { @CasePathable public enum ChildAction: Sendable, Equatable { - case hideAsset(HideAsset.Action) + case hideResource(HideResource.Action) } @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient @@ -61,8 +61,8 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { public var body: some ReducerOf { Reduce(core) - .ifLet(\.hideAsset, action: \.child.hideAsset) { - HideAsset() + .ifLet(\.hideResource, action: \.child.hideResource) { + HideResource() } } @@ -93,6 +93,9 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { switch internalAction { case let .resourceLoadResult(.success(resource)): state.resourceDetails = .success(resource) + if state.stakeClaim == nil { + state.hideResource = .init(kind: .nonFungible(resource.resourceAddress, name: resource.metadata.name)) + } return .none case let .resourceLoadResult(.failure(err)): state.resourceDetails = .failure(err) @@ -102,7 +105,7 @@ public struct NonFungibleTokenDetails: Sendable, FeatureReducer { public func reduce(into state: inout State, childAction: ChildAction) -> Effect { switch childAction { - case .hideAsset(.delegate(.didHideAsset)): + case .hideResource(.delegate(.didHideResource)): .run { _ in await dismiss() } default: .none diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift index a4860cee04..db76fb1264 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Details/NonFungibleTokenDetails+View.swift @@ -119,14 +119,14 @@ extension NonFungibleTokenDetails { } AssetResourceDetailsSection(viewState: viewStore.resourceDetails) + + IfLetStore(store.scope(state: \.hideResource, action: \.child.hideResource)) { store in + HideResource.View(store: store) + .padding(.vertical, .medium1) + } } .padding(.vertical, .medium1) .background(.app.gray5, ignoresSafeAreaEdges: .bottom) - - IfLetStore(store.scope(state: \.hideAsset, action: \.child.hideAsset)) { store in - HideAsset.View(store: store) - .padding(.vertical, .medium1) - } } } .foregroundColor(.app.gray1) diff --git a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift index f36b2ecac7..acf686efc3 100644 --- a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails+View.swift @@ -62,7 +62,7 @@ extension PoolUnitDetails { AssetResourceDetailsSection(viewState: viewStore.resourceDetails) - HideAsset.View(store: store.hideAsset) + HideResource.View(store: store.hideResource) } .padding(.bottom, .medium1) } @@ -72,7 +72,7 @@ extension PoolUnitDetails { } private extension StoreOf { - var hideAsset: StoreOf { - scope(state: \.hideAsset, action: \.child.hideAsset) + var hideResource: StoreOf { + scope(state: \.hideResource, action: \.child.hideResource) } } diff --git a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift index 78e9c7a0d5..3fe69a2ed4 100644 --- a/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift +++ b/RadixWallet/Features/AssetsFeature/Components/PoolUnitsList/Components/PoolUnitDetails.swift @@ -5,11 +5,11 @@ import SwiftUI public struct PoolUnitDetails: Sendable, FeatureReducer { public struct State: Sendable, Hashable { let resourcesDetails: OnLedgerEntitiesClient.OwnedResourcePoolDetails - var hideAsset: HideAsset.State + var hideResource: HideResource.State public init(resourcesDetails: OnLedgerEntitiesClient.OwnedResourcePoolDetails) { self.resourcesDetails = resourcesDetails - self.hideAsset = .init(asset: .poolUnit(resourcesDetails.address)) + self.hideResource = .init(kind: .poolUnit(resourcesDetails.address)) } } @@ -21,12 +21,12 @@ public struct PoolUnitDetails: Sendable, FeatureReducer { @CasePathable public enum ChildAction: Sendable, Equatable { - case hideAsset(HideAsset.Action) + case hideResource(HideResource.Action) } public var body: some ReducerOf { - Scope(state: \.hideAsset, action: \.child.hideAsset) { - HideAsset() + Scope(state: \.hideResource, action: \.child.hideResource) { + HideResource() } Reduce(core) @@ -43,7 +43,7 @@ public struct PoolUnitDetails: Sendable, FeatureReducer { public func reduce(into state: inout State, childAction: ChildAction) -> Effect { switch childAction { - case .hideAsset(.delegate(.didHideAsset)): + case .hideResource(.delegate(.didHideResource)): .run { _ in await dismiss() } default: .none From 9608b67eb648a28d025a73203f396ca42a63cc98 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 17:50:58 +0200 Subject: [PATCH 28/34] changes --- RadixWallet.xcodeproj/project.pbxproj | 4 -- .../ConfirmationView+Configuration.swift | 27 ------------ .../Components/ConfirmationView.swift | 42 +++++++++++++++---- .../AccountPreferences+View.swift | 2 +- .../HideResource/HideResource+View.swift | 8 ++-- 5 files changed, 39 insertions(+), 44 deletions(-) delete mode 100644 RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index b2d659e2ee..bcb9017654 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -920,7 +920,6 @@ 5BEB9CD22C6F6B24001FD9D4 /* HideResource+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD02C6F6B24001FD9D4 /* HideResource+View.swift */; }; 5BEB9CD32C6F6B24001FD9D4 /* HIdeResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD12C6F6B24001FD9D4 /* HIdeResource.swift */; }; 5BEB9CD52C6F6CC9001FD9D4 /* ConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */; }; - 5BEB9CD72C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */; }; 5BFA4FBA2C736B740030B517 /* HiddenAssets+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */; }; 5BFA4FBB2C736B740030B517 /* HiddenAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFA4FB92C736B740030B517 /* HiddenAssets.swift */; }; 830818482B9F1621002D8351 /* HTTPClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830818472B9F1621002D8351 /* HTTPClient+Live.swift */; }; @@ -2074,7 +2073,6 @@ 5BEB9CD02C6F6B24001FD9D4 /* HideResource+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HideResource+View.swift"; sourceTree = ""; }; 5BEB9CD12C6F6B24001FD9D4 /* HIdeResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIdeResource.swift; sourceTree = ""; }; 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationView.swift; sourceTree = ""; }; - 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationView+Configuration.swift"; sourceTree = ""; }; 5BFA4FB82C736B740030B517 /* HiddenAssets+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HiddenAssets+View.swift"; sourceTree = ""; }; 5BFA4FB92C736B740030B517 /* HiddenAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenAssets.swift; sourceTree = ""; }; 830818472B9F1621002D8351 /* HTTPClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPClient+Live.swift"; sourceTree = ""; }; @@ -5128,7 +5126,6 @@ 5B6499B92BCFDB1E000F2176 /* ShareView.swift */, A4DCCC4A2C2DA08000438A7B /* GeometryExtensions.swift */, 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */, - 5BEB9CD62C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift */, ); path = Components; sourceTree = ""; @@ -7082,7 +7079,6 @@ A4EBB5D62BD0777C00D05FDE /* ConfigurationBackup+Reducer.swift in Sources */, A41557532B7645E70040AD4E /* TransactionHistory+Reducer.swift in Sources */, E6A2D9E72AFA7132001857EC /* Mnemonics+Utils.swift in Sources */, - 5BEB9CD72C6F6DAB001FD9D4 /* ConfirmationView+Configuration.swift in Sources */, 83EE5EF32BE3C16F00B1531D /* StateAccountResourcePreferencesPageResponse.swift in Sources */, 48CFC28A2ADC10D900E77A5C /* TransactionReviewGuarantees.swift in Sources */, 48CFC5772ADC10DA00E77A5C /* MetadataTypedValue.swift in Sources */, diff --git a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift deleted file mode 100644 index 0d0b4350bf..0000000000 --- a/RadixWallet/Core/DesignSystem/Components/ConfirmationView+Configuration.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -extension ConfirmationView.Configuration { - static var hideAccount: Self { - .init( - title: L10n.AccountSettings.HideAccount.title, - message: L10n.AccountSettings.HideAccount.message, - primaryAction: L10n.AccountSettings.HideAccount.button - ) - } - - static var hideAsset: Self { - .init( - title: L10n.AssetDetails.HideAsset.title, - message: L10n.AssetDetails.HideAsset.message, - primaryAction: L10n.AssetDetails.HideAsset.button - ) - } - - static func hideCollection(name: String) -> Self { - .init( - title: L10n.AssetDetails.HideCollection.title, - message: L10n.AssetDetails.HideCollection.message(name), - primaryAction: L10n.AssetDetails.HideCollection.button - ) - } -} diff --git a/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift b/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift index e3d244aad1..ebafec8bc0 100644 --- a/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift +++ b/RadixWallet/Core/DesignSystem/Components/ConfirmationView.swift @@ -4,7 +4,7 @@ public typealias ConfirmationAction = ConfirmationView.Action // MARK: - ConfirmationView public struct ConfirmationView: View { - let configuration: Configuration + let kind: Kind let onAction: (Action) -> Void public var body: some View { @@ -27,11 +27,11 @@ public struct ConfirmationView: View { .frame(.small) .foregroundColor(.app.gray3) - Text(configuration.title) + Text(title) .textStyle(.sheetTitle) .foregroundColor(.app.gray1) - Text(markdown: configuration.message, emphasizedColor: .app.gray1, emphasizedFont: .app.body1Header) + Text(markdown: message, emphasizedColor: .app.gray1, emphasizedFont: .app.body1Header) .textStyle(.body1Regular) .foregroundColor(.app.gray1) } @@ -45,7 +45,7 @@ public struct ConfirmationView: View { } .buttonStyle(.secondaryRectangular) - Button(configuration.primaryAction) { + Button(primaryAction) { onAction(.confirm) } .buttonStyle(.primaryRectangular) @@ -58,10 +58,10 @@ public struct ConfirmationView: View { } extension ConfirmationView { - public struct Configuration { - let title: String - let message: String - let primaryAction: String + public enum Kind: Hashable, Sendable { + case hideAccount + case hideAsset + case hideCollection(name: String) } public enum Action: Sendable { @@ -69,3 +69,29 @@ extension ConfirmationView { case confirm } } + +extension ConfirmationView { + var title: String { + switch kind { + case .hideAccount: L10n.Confirmation.HideAccount.title + case .hideAsset: L10n.Confirmation.HideAsset.title + case .hideCollection: L10n.Confirmation.HideCollection.title + } + } + + var message: String { + switch kind { + case .hideAccount: L10n.Confirmation.HideAccount.message + case .hideAsset: L10n.Confirmation.HideAsset.message + case let .hideCollection(name): L10n.Confirmation.HideCollection.message(name) + } + } + + var primaryAction: String { + switch kind { + case .hideAccount: L10n.Confirmation.HideAccount.button + case .hideAsset: L10n.Confirmation.HideAsset.button + case .hideCollection: L10n.Confirmation.HideCollection.button + } + } +} diff --git a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift index f0d2bf892b..95cb9cdbab 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+View.swift @@ -153,7 +153,7 @@ private extension View { private func hideAccount(with destinationStore: PresentationStoreOf, store: StoreOf) -> some View { sheet(store: destinationStore.scope(state: \.hideAccount, action: \.hideAccount)) { _ in - ConfirmationView(configuration: .hideAccount) { action in + ConfirmationView(kind: .hideAccount) { action in store.send(.destination(.presented(.hideAccount(action)))) } } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift index c7fb49f681..a3e217fd55 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift @@ -43,7 +43,7 @@ private extension View { private func confirmation(with destinationStore: PresentationStoreOf, store: StoreOf) -> some View { sheet(store: destinationStore.scope(state: \.confirmation, action: \.confirmation)) { _ in - ConfirmationView(configuration: store.confirmationConfiguration) { action in + ConfirmationView(kind: store.confirmationKind) { action in store.send(.destination(.presented(.confirmation(action)))) } } @@ -54,13 +54,13 @@ private extension HideResource.State { var title: String { switch kind { case .fungible, .poolUnit: - L10n.AssetDetails.HideAsset.button + L10n.AssetDetails.hideAsset case .nonFungible: - L10n.AssetDetails.hideCollectionButton + L10n.AssetDetails.hideCollection } } - var confirmationConfiguration: ConfirmationView.Configuration { + var confirmationKind: ConfirmationView.Kind { switch kind { case .fungible, .poolUnit: .hideAsset From 8b614426366c855ea69193ee536adcd0bcaed753 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 18:00:03 +0200 Subject: [PATCH 29/34] Update HideResource UI --- .../HideResource/HideResource+View.swift | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift b/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift index a3e217fd55..253f0b28ab 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideResource/HideResource+View.swift @@ -10,11 +10,17 @@ public extension HideResource { public var body: some SwiftUI.View { WithPerceptionTracking { if store.shouldShow { - Button(store.title) { - store.send(.view(.buttonTapped)) + VStack(spacing: .medium2) { + if store.showSeparator { + AssetDetailsSeparator() + } + + Button(store.title) { + store.send(.view(.buttonTapped)) + } + .buttonStyle(.secondaryRectangular(shouldExpand: true)) + .padding(.horizontal, .medium3) } - .buttonStyle(.secondaryRectangular(shouldExpand: true)) - .padding(.horizontal, .medium3) } } .task { @@ -60,6 +66,15 @@ private extension HideResource.State { } } + var showSeparator: Bool { + switch kind { + case .fungible, .poolUnit: + false + case .nonFungible: + true + } + } + var confirmationKind: ConfirmationView.Kind { switch kind { case .fungible, .poolUnit: From b95bc07d22671b924f7e09ea3c33ac3520c0a09c Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 26 Aug 2024 18:14:42 +0200 Subject: [PATCH 30/34] feedback from review --- .../AccountPortfoliosClient+Live.swift | 4 ++-- .../AppPreferencesClient+Interface.swift | 2 +- .../EntitiesVisibilityClient+Interface.swift | 10 ---------- .../AccountPreferences+Reducer.swift | 2 +- .../ResourceBalance/ResourceBalanceButton.swift | 2 +- .../Components/HideResource/HIdeResource.swift | 2 +- .../PersonaDetails/PersonaDetails.swift | 2 +- 7 files changed, 7 insertions(+), 17 deletions(-) diff --git a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift index d0ca12f7ca..2f4cb39b3e 100644 --- a/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift +++ b/RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift @@ -25,9 +25,9 @@ extension AccountPortfoliosClient: DependencyKey { } } - /// Update when hidden assets change + /// Update when hidden resources change Task { - for try await _ in await appPreferencesClient.appPreferenceUpdates().map(\.resources.hiddenResources) { + for try await _ in await appPreferencesClient.appPreferenceUpdates().removeDuplicates(by: { $0.resources.hiddenResources == $1.resources.hiddenResources }) { guard !Task.isCancelled else { return } let accountAddresses = state.portfoliosSubject.value.wrappedValue.map { $0.map(\.key) } ?? [] _ = try await fetchAccountPortfolios(accountAddresses, forceRefreshEntities: false, forceRefreshPrices: true) diff --git a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift index 8be2817bc9..45a1f349f8 100644 --- a/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift +++ b/RadixWallet/Clients/AppPreferencesClient/AppPreferencesClient+Interface.swift @@ -75,7 +75,7 @@ extension AppPreferencesClient { await getPreferences().resources.hiddenResources } - public func isResourceHidden(resource: ResourceIdentifier) async -> Bool { + public func isResourceHidden(_ resource: ResourceIdentifier) async -> Bool { await getHiddenResources().contains(resource) } } diff --git a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift index 608d4098a8..0c05ba22d5 100644 --- a/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift +++ b/RadixWallet/Clients/EntitiesVisibilityClient/EntitiesVisibilityClient+Interface.swift @@ -20,13 +20,3 @@ extension EntitiesVisibilityClient { public typealias UnhidePersona = @Sendable (Persona.ID) async throws -> Void public typealias GetHiddenEntities = @Sendable () async throws -> HiddenEntities } - -extension EntitiesVisibilityClient { - func hide(account: Account) async throws { - try await hideAccount(account.id) - } - - func hide(persona: Persona) async throws { - try await hidePersona(persona.id) - } -} diff --git a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift index 466069882f..180beb0d73 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/AccountPreferences+Reducer.swift @@ -174,7 +174,7 @@ public struct AccountPreferences: Sendable, FeatureReducer { private func hideAccountEffect(state: State) -> Effect { .run { [account = state.account] send in - try await entitiesVisibilityClient.hide(account: account) + try await entitiesVisibilityClient.hideAccount(account.id) overlayWindowClient.scheduleHUD(.accountHidden) await send(.delegate(.accountHidden)) } catch: { error, _ in diff --git a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift index 67c9a400f7..9254e4fece 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HelperViews/ResourceBalance/ResourceBalanceButton.swift @@ -37,8 +37,8 @@ public struct ResourceBalanceButton: View { } } .padding(.top, topPadding) - .padding(.bottom, bottomPadding) .padding(.horizontal, horizontalSpacing) + .padding(.bottom, bottomPadding) .contentShape(Rectangle()) .background(background) } diff --git a/RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift b/RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift index 68e196c9ac..6a5faf7e11 100644 --- a/RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift +++ b/RadixWallet/Features/AssetsFeature/Components/HideResource/HIdeResource.swift @@ -117,7 +117,7 @@ private extension HideResource { if isXrd { await send(.internal(.setShouldShow(false))) } else { - let isAlreadyHidden = await appPreferencesClient.isResourceHidden(resource: state.resource) + let isAlreadyHidden = await appPreferencesClient.isResourceHidden(state.resource) await send(.internal(.setShouldShow(!isAlreadyHidden))) } } diff --git a/RadixWallet/Features/DappsAndPersonas/PersonaDetails/PersonaDetails.swift b/RadixWallet/Features/DappsAndPersonas/PersonaDetails/PersonaDetails.swift index 13691470be..e689c2aff4 100644 --- a/RadixWallet/Features/DappsAndPersonas/PersonaDetails/PersonaDetails.swift +++ b/RadixWallet/Features/DappsAndPersonas/PersonaDetails/PersonaDetails.swift @@ -249,7 +249,7 @@ public struct PersonaDetails: Sendable, FeatureReducer { return .none } return .run { send in - try await entitiesVisibilityClient.hide(persona: persona) + try await entitiesVisibilityClient.hidePersona(persona.id) overlayWindowClient.scheduleHUD(.personaHidden) await send(.delegate(.personaHidden)) } catch: { error, _ in From 2a0c9aa5bba82d76d6af850d2903f25e52c93eb6 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 27 Aug 2024 11:33:43 +0200 Subject: [PATCH 31/34] logic for nftIds count --- .../HiddenAssets/HiddenAssets+View.swift | 16 +++++++---- .../HiddenAssets/HiddenAssets.swift | 27 ++++++++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index e48a5ce151..1269df0394 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -64,12 +64,12 @@ extension HiddenAssets { empty } else { VStack(spacing: .medium3) { - ForEachStatic(store.nonFungible) { resource in + ForEachStatic(store.nonFungible) { nonFungible in Card { PlainListRow(viewState: .init( - rowCoreViewState: .init(context: .hiddenAsset, title: resource.metadata.name), - accessory: { unhideButton(resource: .nonFungible(resource.resourceAddress)) }, - icon: { Thumbnail(.nft, url: resource.metadata.iconURL) } + rowCoreViewState: nonFungible.rowCoreViewState, + accessory: { unhideButton(resource: .nonFungible(nonFungible.resource.resourceAddress)) }, + icon: { Thumbnail(.nft, url: nonFungible.resource.metadata.iconURL) } )) } } @@ -122,8 +122,14 @@ extension HiddenAssets { } } +private extension HiddenAssets.State.NonFungibleDetails { + var rowCoreViewState: PlainListRowCore.ViewState { + .init(context: .hiddenAsset, title: resource.metadata.name ?? "-", subtitle: "\(count)") + } +} + private extension HiddenAssets.State.PoolUnitDetails { var rowCoreViewState: PlainListRowCore.ViewState { - .init(context: .hiddenAsset, title: "-", subtitle: details.dAppName ?? "-") + .init(context: .hiddenAsset, title: resource.fungibleResourceName ?? "-", subtitle: details.dAppName ?? "-") } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 2bdcd06465..93666ca8db 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -6,12 +6,17 @@ public struct HiddenAssets: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { var fungible: [OnLedgerEntity.Resource] = [] - var nonFungible: [OnLedgerEntity.Resource] = [] + var nonFungible: [NonFungibleDetails] = [] var poolUnit: [PoolUnitDetails] = [] @Presents var destination: Destination.State? = nil + public struct NonFungibleDetails: Sendable, Hashable { + let resource: OnLedgerEntity.Resource + let count: Int + } + public struct PoolUnitDetails: Sendable, Hashable { let resource: OnLedgerEntity.Resource let details: OnLedgerEntitiesClient.OwnedResourcePoolDetails @@ -29,7 +34,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { public enum InternalAction: Sendable, Equatable { case loadResources([ResourceIdentifier]) case setFungible([OnLedgerEntity.Resource]) - case setNonFungible([OnLedgerEntity.Resource]) + case setNonFungible([State.NonFungibleDetails]) case setPoolUnit([State.PoolUnitDetails]) case didUnhideResource(ResourceIdentifier) } @@ -57,6 +62,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { @Dependency(\.appPreferencesClient) var appPreferencesClient @Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient + @Dependency(\.accountsClient) var accountsClient public init() {} @@ -111,7 +117,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case let .fungible(resourceAddress): state.fungible.removeAll(where: { $0.resourceAddress == resourceAddress }) case let .nonFungible(resourceAddress): - state.nonFungible.removeAll(where: { $0.resourceAddress == resourceAddress }) + state.nonFungible.removeAll(where: { $0.resource.resourceAddress == resourceAddress }) case let .poolUnit(poolAddress): state.poolUnit.removeAll(where: { $0.details.address == poolAddress }) } @@ -148,7 +154,14 @@ public struct HiddenAssets: Sendable, FeatureReducer { private func nonFungibleEffect(hiddenResources: [ResourceIdentifier]) -> Effect { .run { send in let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenResources.nonFungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) - await send(.internal(.setNonFungible(resources))) + let accountAddresses = try await accountsClient.getAccountsOnCurrentNetwork().map(\.address) + let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses) + let nonFungibleDetails = resources.map { resource in + let count = countOfNonFungibleIds(accounts: accounts, resourceAddress: resource.resourceAddress) + return State.NonFungibleDetails(resource: resource, count: count) + } + + await send(.internal(.setNonFungible(nonFungibleDetails))) } } @@ -169,6 +182,12 @@ public struct HiddenAssets: Sendable, FeatureReducer { await send(.internal(.setPoolUnit(poolUnitDetails))) } } + + private func countOfNonFungibleIds(accounts: [OnLedgerEntity.OnLedgerAccount], resourceAddress: ResourceAddress) -> Int { + accounts + .compactMap { $0.nonFungibleResources.first(where: { $0.resourceAddress == resourceAddress })?.nonFungibleIdsCount } + .reduce(0, +) + } } // MARK: - Helpers From d68f401de690d26ae2e56afa626d5476e9e5061a Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 27 Aug 2024 12:44:11 +0200 Subject: [PATCH 32/34] show X in collection --- .../Components/Row/NonFungibleAssetRow+View.swift | 2 +- .../HiddenAssets/HiddenAssets+View.swift | 2 +- .../Preferences/HiddenAssets/HiddenAssets.swift | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungibleAssetRow+View.swift b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungibleAssetRow+View.swift index 264e145d82..c79309a3c4 100644 --- a/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungibleAssetRow+View.swift +++ b/RadixWallet/Features/AssetsFeature/Components/NonFungibleAssetList/Components/Row/NonFungibleAssetRow+View.swift @@ -60,7 +60,7 @@ extension NonFungibleAssetList.Row.View { .textStyle(.secondaryHeader) } - Text("\(viewStore.resource.nonFungibleIdsCount)") + Text(L10n.Account.Nfts.itemsCount(viewStore.resource.nonFungibleIdsCount)) .font(.app.body2HighImportance) .foregroundColor(.app.gray2) } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 1269df0394..3f54b69da8 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -124,7 +124,7 @@ extension HiddenAssets { private extension HiddenAssets.State.NonFungibleDetails { var rowCoreViewState: PlainListRowCore.ViewState { - .init(context: .hiddenAsset, title: resource.metadata.name ?? "-", subtitle: "\(count)") + .init(context: .hiddenAsset, title: resource.metadata.name ?? "-", subtitle: L10n.HiddenAssets.NonFungibles.count(count)) } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 93666ca8db..199e921b74 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -79,7 +79,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { } case let .unhideTapped(resource): state.destination = .unhideAlert(.init( - title: { TextState(L10n.HiddenAssets.unhideConfirmation) }, + title: { TextState(resource.unhideAlertTitle) }, actions: { ButtonState(role: .cancel, action: .cancelTapped) { TextState(L10n.Common.cancel) @@ -220,3 +220,14 @@ private extension [ResourceIdentifier] { } } } + +private extension ResourceIdentifier { + var unhideAlertTitle: String { + switch self { + case .fungible, .poolUnit: + L10n.HiddenAssets.UnhideConfirmation.asset + case .nonFungible: + L10n.HiddenAssets.UnhideConfirmation.collection + } + } +} From 1baa3d0dfa2f674866b1c00f7acd60b556dc6752 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 27 Aug 2024 16:46:01 +0200 Subject: [PATCH 33/34] Update UI and include AssetRow --- RadixWallet.xcodeproj/project.pbxproj | 4 ++ .../DesignSystem/Components/AssetRow.swift | 48 ++++++++++++++ .../Components/PlainListRow.swift | 16 ++--- .../HiddenAssets/HiddenAssets+View.swift | 66 ++++++++----------- .../HiddenAssets/HiddenAssets.swift | 28 ++------ .../HiddenEntities/HiddenEntities+View.swift | 10 +-- 6 files changed, 98 insertions(+), 74 deletions(-) create mode 100644 RadixWallet/Core/DesignSystem/Components/AssetRow.swift diff --git a/RadixWallet.xcodeproj/project.pbxproj b/RadixWallet.xcodeproj/project.pbxproj index bcb9017654..96814dc1c6 100644 --- a/RadixWallet.xcodeproj/project.pbxproj +++ b/RadixWallet.xcodeproj/project.pbxproj @@ -881,6 +881,7 @@ 5B272DDB2C36E9D300B74F1F /* AppEventsClient+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B272DDA2C36E9D300B74F1F /* AppEventsClient+Test.swift */; }; 5B2A45022BD6680400AEC8AD /* ContactSupportClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2A45012BD6680400AEC8AD /* ContactSupportClient.swift */; }; 5B2A45042BD6689100AEC8AD /* ContactSupportClient+Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2A45032BD6689100AEC8AD /* ContactSupportClient+Live.swift */; }; + 5B3C48A92C7E190C00DB160D /* AssetRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3C48A82C7E190C00DB160D /* AssetRow.swift */; }; 5B43B08B2BDAAD4B00AA1E92 /* AddressDetails+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B43B0892BDAAD4B00AA1E92 /* AddressDetails+View.swift */; }; 5B43B08C2BDAAD4B00AA1E92 /* AddressDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B43B08A2BDAAD4B00AA1E92 /* AddressDetails.swift */; }; 5B45E2FA2BC45770007C4C84 /* FactorSourceAccess+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B45E2F82BC45770007C4C84 /* FactorSourceAccess+View.swift */; }; @@ -2034,6 +2035,7 @@ 5B272DDA2C36E9D300B74F1F /* AppEventsClient+Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppEventsClient+Test.swift"; sourceTree = ""; }; 5B2A45012BD6680400AEC8AD /* ContactSupportClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSupportClient.swift; sourceTree = ""; }; 5B2A45032BD6689100AEC8AD /* ContactSupportClient+Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContactSupportClient+Live.swift"; sourceTree = ""; }; + 5B3C48A82C7E190C00DB160D /* AssetRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetRow.swift; sourceTree = ""; }; 5B43B0892BDAAD4B00AA1E92 /* AddressDetails+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddressDetails+View.swift"; sourceTree = ""; }; 5B43B08A2BDAAD4B00AA1E92 /* AddressDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDetails.swift; sourceTree = ""; }; 5B45E2F82BC45770007C4C84 /* FactorSourceAccess+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FactorSourceAccess+View.swift"; sourceTree = ""; }; @@ -5126,6 +5128,7 @@ 5B6499B92BCFDB1E000F2176 /* ShareView.swift */, A4DCCC4A2C2DA08000438A7B /* GeometryExtensions.swift */, 5BEB9CD42C6F6CC9001FD9D4 /* ConfirmationView.swift */, + 5B3C48A82C7E190C00DB160D /* AssetRow.swift */, ); path = Components; sourceTree = ""; @@ -7179,6 +7182,7 @@ 48CFC4962ADC10DA00E77A5C /* StateKeyValueStoreDataRequest.swift in Sources */, 48CFC3292ADC10D900E77A5C /* CameraPermission+Reducer.swift in Sources */, 48CFC53A2ADC10DA00E77A5C /* NotSyncedUpError.swift in Sources */, + 5B3C48A92C7E190C00DB160D /* AssetRow.swift in Sources */, 48CFC54E2ADC10DA00E77A5C /* StateEntityNonFungibleResourceVaultsPageResponse.swift in Sources */, E66DCC402BD93EC000547CF4 /* Stage1MigrateToSargon+Header.swift in Sources */, 835F196D2B3581C300E0B71D /* UnknownDappComponents+View.swift in Sources */, diff --git a/RadixWallet/Core/DesignSystem/Components/AssetRow.swift b/RadixWallet/Core/DesignSystem/Components/AssetRow.swift new file mode 100644 index 0000000000..b17b26d995 --- /dev/null +++ b/RadixWallet/Core/DesignSystem/Components/AssetRow.swift @@ -0,0 +1,48 @@ +import SwiftUI + +struct AssetRow: View { + let name: String? + let address: LedgerIdentifiable.Address + let type: Thumbnail.ContentType + let url: URL? + let accessory: Accessory? + + init( + name: String?, + address: LedgerIdentifiable.Address, + type: Thumbnail.ContentType, + url: URL?, + @ViewBuilder accessory: () -> Accessory + ) { + self.name = name + self.address = address + self.type = type + self.url = url + self.accessory = accessory() + } + + var body: some SwiftUI.View { + HStack(spacing: .zero) { + Thumbnail(type, url: url) + + VStack(alignment: .leading, spacing: .zero) { + Text(name ?? "-") + .textStyle(.body1HighImportance) + .foregroundColor(.app.gray1) + .lineLimit(1) + + AddressView(.address(address)) + .textStyle(.body2Regular) + .foregroundColor(.app.gray2) + } + .padding(.horizontal, .small2) + + Spacer() + + if let accessory { + accessory + } + } + .padding(.medium3) + } +} diff --git a/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift b/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift index c287c32724..d7b461a766 100644 --- a/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift +++ b/RadixWallet/Core/DesignSystem/Components/PlainListRow.swift @@ -196,14 +196,14 @@ private extension PlainListRowCore.ViewState { .secondaryHeader case .settings, .dappAndPersona: .body1Header - case .hiddenAsset: + case .hiddenPersona: .body1HighImportance } } var subtitleTextStyle: TextStyle { switch context { - case .toggle, .hiddenAsset: + case .toggle, .hiddenPersona: .body2Regular case .settings, .dappAndPersona: detail == nil ? .body1Regular : .body2Regular @@ -212,7 +212,7 @@ private extension PlainListRowCore.ViewState { var subtitleForegroundColor: Color { switch context { - case .toggle, .hiddenAsset: + case .toggle, .hiddenPersona: .app.gray2 case .settings, .dappAndPersona: .app.gray1 @@ -221,7 +221,7 @@ private extension PlainListRowCore.ViewState { var titleLineLimit: Int? { switch context { - case .settings, .dappAndPersona, .hiddenAsset: + case .settings, .dappAndPersona, .hiddenPersona: 1 case .toggle: nil @@ -230,7 +230,7 @@ private extension PlainListRowCore.ViewState { var subtitleLineLimit: Int { switch context { - case .toggle, .hiddenAsset: + case .toggle, .hiddenPersona: 2 case .settings, .dappAndPersona: 3 @@ -245,14 +245,14 @@ private extension PlainListRowCore.ViewState { .medium1 case .dappAndPersona: .medium3 - case .hiddenAsset: + case .hiddenPersona: .medium3 } } var horizontalPadding: CGFloat { switch context { - case .toggle, .settings, .hiddenAsset: + case .toggle, .settings, .hiddenPersona: .medium3 case .dappAndPersona: .medium1 @@ -266,7 +266,7 @@ extension PlainListRowCore.ViewState { case settings case toggle case dappAndPersona - case hiddenAsset + case hiddenPersona } } diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift index 3f54b69da8..73fad87711 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets+View.swift @@ -42,16 +42,18 @@ extension HiddenAssets { @ViewBuilder private var fungibles: some SwiftUI.View { if store.fungible.isEmpty { - empty + emptyState } else { VStack(spacing: .medium3) { ForEachStatic(store.fungible) { resource in Card { - PlainListRow(viewState: .init( - rowCoreViewState: .init(context: .hiddenAsset, title: resource.fungibleResourceName), - accessory: { unhideButton(resource: .fungible(resource.resourceAddress)) }, - icon: { Thumbnail(.token(.other), url: resource.metadata.iconURL) } - )) + AssetRow( + name: resource.fungibleResourceName, + address: .resource(resource.resourceAddress), + type: .token(.other), + url: resource.metadata.iconURL, + accessory: { unhideButton(resource: .fungible(resource.resourceAddress)) } + ) } } } @@ -61,16 +63,18 @@ extension HiddenAssets { @ViewBuilder private var nonFungibles: some SwiftUI.View { if store.nonFungible.isEmpty { - empty + emptyState } else { VStack(spacing: .medium3) { - ForEachStatic(store.nonFungible) { nonFungible in + ForEachStatic(store.nonFungible) { resource in Card { - PlainListRow(viewState: .init( - rowCoreViewState: nonFungible.rowCoreViewState, - accessory: { unhideButton(resource: .nonFungible(nonFungible.resource.resourceAddress)) }, - icon: { Thumbnail(.nft, url: nonFungible.resource.metadata.iconURL) } - )) + AssetRow( + name: resource.metadata.name, + address: .resource(resource.resourceAddress), + type: .nft, + url: resource.metadata.iconURL, + accessory: { unhideButton(resource: .nonFungible(resource.resourceAddress)) } + ) } } } @@ -80,16 +84,18 @@ extension HiddenAssets { @ViewBuilder private var poolUnits: some SwiftUI.View { if store.poolUnit.isEmpty { - empty + emptyState } else { VStack(spacing: .medium3) { ForEachStatic(store.poolUnit) { poolUnit in Card { - PlainListRow(viewState: .init( - rowCoreViewState: poolUnit.rowCoreViewState, - accessory: { unhideButton(resource: .poolUnit(poolUnit.details.address)) }, - icon: { Thumbnail(.poolUnit, url: poolUnit.resource.metadata.iconURL) } - )) + AssetRow( + name: poolUnit.resource.fungibleResourceName, + address: .resourcePool(poolUnit.details.address), + type: .poolUnit, + url: poolUnit.resource.metadata.iconURL, + accessory: { unhideButton(resource: .poolUnit(poolUnit.details.address)) } + ) } } } @@ -103,14 +109,10 @@ extension HiddenAssets { .buttonStyle(.secondaryRectangular(shouldExpand: false)) } - private var empty: some SwiftUI.View { + private var emptyState: some SwiftUI.View { ZStack { - PlainListRow(viewState: .init( - rowCoreViewState: .init(context: .hiddenAsset, title: "dummy"), - accessory: { unhideButton(resource: .fungible(.mainnetXRD)) }, - icon: { Thumbnail(.token(.other), url: nil) } - )) - .hidden() + AssetRow(name: "dummy", address: .resource(.mainnetXRD), type: .token(.other), url: nil, accessory: { unhideButton(resource: .fungible(.mainnetXRD)) }) + .hidden() Text(L10n.Common.none) .textStyle(.secondaryHeader) @@ -121,15 +123,3 @@ extension HiddenAssets { } } } - -private extension HiddenAssets.State.NonFungibleDetails { - var rowCoreViewState: PlainListRowCore.ViewState { - .init(context: .hiddenAsset, title: resource.metadata.name ?? "-", subtitle: L10n.HiddenAssets.NonFungibles.count(count)) - } -} - -private extension HiddenAssets.State.PoolUnitDetails { - var rowCoreViewState: PlainListRowCore.ViewState { - .init(context: .hiddenAsset, title: resource.fungibleResourceName ?? "-", subtitle: details.dAppName ?? "-") - } -} diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift index 199e921b74..d255fe326c 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenAssets/HiddenAssets.swift @@ -6,17 +6,12 @@ public struct HiddenAssets: Sendable, FeatureReducer { @ObservableState public struct State: Sendable, Hashable { var fungible: [OnLedgerEntity.Resource] = [] - var nonFungible: [NonFungibleDetails] = [] - var poolUnit: [PoolUnitDetails] = [] + var nonFungible: [OnLedgerEntity.Resource] = [] + var poolUnit: [State.PoolUnitDetails] = [] @Presents var destination: Destination.State? = nil - public struct NonFungibleDetails: Sendable, Hashable { - let resource: OnLedgerEntity.Resource - let count: Int - } - public struct PoolUnitDetails: Sendable, Hashable { let resource: OnLedgerEntity.Resource let details: OnLedgerEntitiesClient.OwnedResourcePoolDetails @@ -34,7 +29,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { public enum InternalAction: Sendable, Equatable { case loadResources([ResourceIdentifier]) case setFungible([OnLedgerEntity.Resource]) - case setNonFungible([State.NonFungibleDetails]) + case setNonFungible([OnLedgerEntity.Resource]) case setPoolUnit([State.PoolUnitDetails]) case didUnhideResource(ResourceIdentifier) } @@ -117,7 +112,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { case let .fungible(resourceAddress): state.fungible.removeAll(where: { $0.resourceAddress == resourceAddress }) case let .nonFungible(resourceAddress): - state.nonFungible.removeAll(where: { $0.resource.resourceAddress == resourceAddress }) + state.nonFungible.removeAll(where: { $0.resourceAddress == resourceAddress }) case let .poolUnit(poolAddress): state.poolUnit.removeAll(where: { $0.details.address == poolAddress }) } @@ -154,14 +149,7 @@ public struct HiddenAssets: Sendable, FeatureReducer { private func nonFungibleEffect(hiddenResources: [ResourceIdentifier]) -> Effect { .run { send in let resources = try await onLedgerEntitiesClient.getEntities(addresses: hiddenResources.nonFungibleAddresses, metadataKeys: .resourceMetadataKeys).compactMap(\.resource) - let accountAddresses = try await accountsClient.getAccountsOnCurrentNetwork().map(\.address) - let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses) - let nonFungibleDetails = resources.map { resource in - let count = countOfNonFungibleIds(accounts: accounts, resourceAddress: resource.resourceAddress) - return State.NonFungibleDetails(resource: resource, count: count) - } - - await send(.internal(.setNonFungible(nonFungibleDetails))) + await send(.internal(.setNonFungible(resources))) } } @@ -182,12 +170,6 @@ public struct HiddenAssets: Sendable, FeatureReducer { await send(.internal(.setPoolUnit(poolUnitDetails))) } } - - private func countOfNonFungibleIds(accounts: [OnLedgerEntity.OnLedgerAccount], resourceAddress: ResourceAddress) -> Int { - accounts - .compactMap { $0.nonFungibleResources.first(where: { $0.resourceAddress == resourceAddress })?.nonFungibleIdsCount } - .reduce(0, +) - } } // MARK: - Helpers diff --git a/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift b/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift index fcbadc22e7..f43a3fe62e 100644 --- a/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift +++ b/RadixWallet/Features/SettingsFeature/Preferences/HiddenEntities/HiddenEntities+View.swift @@ -39,13 +39,13 @@ extension HiddenEntities { @ViewBuilder private var personas: some SwiftUI.View { if store.personas.isEmpty { - empty + emptyState } else { VStack(spacing: .medium3) { ForEachStatic(store.personas) { persona in Card { PlainListRow(viewState: .init( - rowCoreViewState: .init(context: .hiddenAsset, title: persona.displayName.value), + rowCoreViewState: .init(context: .hiddenPersona, title: persona.displayName.value), accessory: { unhideButton(action: .unhidePersonaTapped(persona.id)) }, icon: { Thumbnail(.persona, url: nil) } )) @@ -58,7 +58,7 @@ extension HiddenEntities { @ViewBuilder private var accounts: some SwiftUI.View { if store.accounts.isEmpty { - empty + emptyState } else { VStack(spacing: .medium3) { ForEachStatic(store.accounts) { account in @@ -79,10 +79,10 @@ extension HiddenEntities { .buttonStyle(.secondaryRectangular(shouldExpand: false)) } - private var empty: some SwiftUI.View { + private var emptyState: some SwiftUI.View { ZStack { PlainListRow(viewState: .init( - rowCoreViewState: .init(context: .hiddenAsset, title: "dummy"), + rowCoreViewState: .init(context: .hiddenPersona, title: "dummy"), accessory: { unhideButton(action: .task) }, icon: { Thumbnail(.persona, url: nil) } )) From a44bd348e16738ed83a92f33247198949f69d20c Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Tue, 27 Aug 2024 17:24:17 +0200 Subject: [PATCH 34/34] Update ResourceListView --- .../ResourcesList+View.swift | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift index 398e749115..2e28d42105 100644 --- a/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift +++ b/RadixWallet/Features/AccountPreferencesFeature/Children/ThirdPartyDeposits/ResourcesList+View.swift @@ -109,15 +109,18 @@ extension ResourcesList.View { ScrollView { VStack(spacing: .zero) { ForEach(resources) { resource in - PlainListRow(viewState: .init( - rowCoreViewState: resource.rowCoreViewState, - accessory: { accesoryView(resource: resource) }, - icon: { iconView(resource: resource) } - )) - .background(Color.app.white) - .withSeparator + Card { + AssetRow( + name: resource.name, + address: resource.address.ledgerIdentifiable, + type: resource.thumbnailType, + url: resource.iconURL, + accessory: { accesoryView(resource: resource) } + ) + } } } + .padding(.horizontal, .medium3) } } @@ -217,17 +220,17 @@ extension ResourcesListMode { } } -extension ResourceViewState.Address { - var ledgerIdentifiable: LedgerIdentifiable { +private extension ResourceViewState.Address { + var ledgerIdentifiable: LedgerIdentifiable.Address { switch self { case let .assetException(exception): - .address(.resource(exception.address)) + .resource(exception.address) case let .allowedDepositor(.resource(resourceAddress)): - .address(.resource(resourceAddress)) + .resource(resourceAddress) case let .allowedDepositor(.nonFungible(nonFungibleGlobalID)): - .address(.nonFungibleGlobalID(nonFungibleGlobalID)) + .nonFungibleGlobalID(nonFungibleGlobalID) } } }