From 0f9d6fe1d05dcb45138499e51a652d49e339ef9a Mon Sep 17 00:00:00 2001 From: Stephen Heaps Date: Fri, 19 Jan 2024 15:58:16 -0500 Subject: [PATCH] Split `AssetIconView` into `AssetIcon` (no sizing) and `AssetIconView` (applies dynamic sizing). Split `NetworkIcon` into `NetworkIcon` (no sizing) and `NetworkIconView` (applies dynamic sizing). Update `MultipleCircleIconView` to apply a background instead of just a stroke for when transparent icons are used. --- .../Crypto/Accounts/AccountsView.swift | 15 +--- .../BraveWallet/Crypto/AssetIconView.swift | 82 +++++++++++-------- .../Crypto/MultipleNetworkIconsView.swift | 25 +++++- .../Crypto/NetworkSelectionRootView.swift | 2 +- .../Crypto/Portfolio/PortfolioView.swift | 2 +- .../Extensions/BraveWalletExtensions.swift | 5 +- .../BraveWallet/MultipleCircleIconView.swift | 10 ++- Sources/BraveWallet/NetworkIcon.swift | 26 ++++-- 8 files changed, 107 insertions(+), 60 deletions(-) diff --git a/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift b/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift index 965aac1b50a..a72540d5f59 100644 --- a/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift +++ b/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift @@ -289,20 +289,11 @@ private struct AccountCardView: View { .redacted(reason: .placeholder) .shimmer(true) } else { - MultipleCircleIconView( - models: tokensWithBalances, - shape: .circle, + MultipleAssetIconsView( + tokens: tokensWithBalances, iconSize: 24, maxIconSize: 32 - ) { token in - AssetIconView( - token: token, - network: .init(), // not shown - shouldShowNetworkIcon: false, - length: 24, - maxLength: 32 - ) - } + ) Spacer() Text(balance) .font(.title3.weight(.medium)) diff --git a/Sources/BraveWallet/Crypto/AssetIconView.swift b/Sources/BraveWallet/Crypto/AssetIconView.swift index d91c493e488..50b8c66f8c1 100644 --- a/Sources/BraveWallet/Crypto/AssetIconView.swift +++ b/Sources/BraveWallet/Crypto/AssetIconView.swift @@ -8,37 +8,11 @@ import BraveCore import BraveUI import DesignSystem -/// Displays an asset's icon from the token registry -/// -/// By default, creating an `AssetIconView` will result in a dynamically sized icon based -/// on the users size category. If you for some reason need to obtain a fixed size asset icon, -/// wrap this view in another frame of your desired size, for example: -/// -/// AssetIconView(token: .eth) -/// .frame(width: 20, height: 20) -/// -struct AssetIconView: View { - var token: BraveWallet.BlockchainToken - var network: BraveWallet.NetworkInfo - /// If we should show the network logo on non-native assets - var shouldShowNetworkIcon: Bool = false - @ScaledMetric var length: CGFloat = 40 - var maxLength: CGFloat? - @ScaledMetric var networkSymbolLength: CGFloat = 15 - var maxNetworkSymbolLength: CGFloat? - - private var fallbackMonogram: some View { - BlockieMaterial(address: token.contractAddress) - .blur(radius: 8, opaque: true) - .clipShape(Circle()) - .overlay( - Text(token.symbol.first?.uppercased() ?? "") - .font(.system(size: length / 2, weight: .bold, design: .rounded)) - .foregroundColor(.white) - .shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1) - ) - } - +/// Displays an asset's icon from the token registry or logo. +struct AssetIcon: View { + let token: BraveWallet.BlockchainToken + let network: BraveWallet.NetworkInfo? + var body: some View { Group { if let uiImage = token.localImage(network: network) { @@ -59,13 +33,49 @@ struct AssetIconView: View { fallbackMonogram } } - .frame(width: min(length, maxLength ?? length), height: min(length, maxLength ?? length)) - .overlay(tokenLogo, alignment: .bottomTrailing) - .accessibilityHidden(true) } - @ViewBuilder private var tokenLogo: some View { - if shouldShowNetworkIcon, // explicitly show/not show network logo + @State private var monogramSize: CGSize = .zero + private var fallbackMonogram: some View { + BlockieMaterial(address: token.contractAddress) + .blur(radius: 8, opaque: true) + .clipShape(Circle()) + .readSize(onChange: { newSize in + monogramSize = newSize + }) + .overlay( + Text(token.symbol.first?.uppercased() ?? "") + .font(.system(size: max(monogramSize.width, monogramSize.height) / 2, weight: .bold, design: .rounded)) + .foregroundColor(.white) + .shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1) + ) + } +} + +/// Displays an asset's icon from the token registry or logo. +/// +/// By default, creating an `AssetIconView` will result in a dynamically sized icon based +/// on the users size category. +struct AssetIconView: View { + var token: BraveWallet.BlockchainToken + var network: BraveWallet.NetworkInfo? + /// If we should show the network logo on non-native assets. NetworkInfo is required. + var shouldShowNetworkIcon: Bool = false + @ScaledMetric var length: CGFloat = 40 + var maxLength: CGFloat? + @ScaledMetric var networkSymbolLength: CGFloat = 15 + var maxNetworkSymbolLength: CGFloat? + + var body: some View { + AssetIcon(token: token, network: network) + .frame(width: min(length, maxLength ?? length), height: min(length, maxLength ?? length)) + .overlay(tokenNetworkLogo, alignment: .bottomTrailing) + .accessibilityHidden(true) + } + + @ViewBuilder private var tokenNetworkLogo: some View { + if let network, + shouldShowNetworkIcon, // explicitly show/not show network logo (!network.isNativeAsset(token) || network.nativeTokenLogoName != network.networkLogoName), // non-native asset OR if the network is not the official Ethereum network, but uses ETH as gas let image = network.networkLogoImage { Image(uiImage: image) diff --git a/Sources/BraveWallet/Crypto/MultipleNetworkIconsView.swift b/Sources/BraveWallet/Crypto/MultipleNetworkIconsView.swift index b5f7734ae96..16eef18d721 100644 --- a/Sources/BraveWallet/Crypto/MultipleNetworkIconsView.swift +++ b/Sources/BraveWallet/Crypto/MultipleNetworkIconsView.swift @@ -20,8 +20,31 @@ struct MultipleNetworkIconsView: View { maxIconSize: maxIconSize, iconDotSize: iconDotSize, iconView: { network in - NetworkIcon(network: network, length: iconSize) + NetworkIcon(network: network) } ) } } + +struct MultipleAssetIconsView: View { + + let tokens: [BraveWallet.BlockchainToken] + let maxBlockies = 3 + @ScaledMetric var iconSize = 24 + var maxIconSize: CGFloat = 32 + @ScaledMetric var blockieDotSize = 2.0 + + var body: some View { + MultipleCircleIconView( + models: tokens, + shape: .circle, + iconSize: iconSize, + maxIconSize: maxIconSize + ) { token in + AssetIcon( + token: token, + network: nil // not shown + ) + } + } +} diff --git a/Sources/BraveWallet/Crypto/NetworkSelectionRootView.swift b/Sources/BraveWallet/Crypto/NetworkSelectionRootView.swift index 638d2b100e2..627769fc818 100644 --- a/Sources/BraveWallet/Crypto/NetworkSelectionRootView.swift +++ b/Sources/BraveWallet/Crypto/NetworkSelectionRootView.swift @@ -161,7 +161,7 @@ private struct NetworkRowView: View { var body: some View { HStack { - NetworkIcon(network: network) + NetworkIconView(network: network) VStack(alignment: .leading, spacing: 0) { Text(network.chainName) .font(.body) diff --git a/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift b/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift index d295acd4dda..9991c844250 100644 --- a/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift +++ b/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift @@ -99,7 +99,7 @@ struct PortfolioAssetGroupHeaderView: View { VStack(spacing: 0) { HStack { if case let .network(networkInfo) = group.groupType { - NetworkIcon(network: networkInfo, length: 32) + NetworkIconView(network: networkInfo, length: 32) } else if case let .account(accountInfo) = group.groupType { Blockie(address: accountInfo.address) .frame(width: 32, height: 32) diff --git a/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift b/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift index f89ff3ba32b..d644bd2db26 100644 --- a/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift +++ b/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift @@ -392,8 +392,9 @@ extension BraveWallet.BlockchainToken { } /// Returns the local image asset for the `BlockchainToken`. - func localImage(network: BraveWallet.NetworkInfo) -> UIImage? { - if network.isNativeAsset(self), let uiImage = network.nativeTokenLogoImage { + func localImage(network: BraveWallet.NetworkInfo?) -> UIImage? { + if let network, + network.isNativeAsset(self), let uiImage = network.nativeTokenLogoImage { return uiImage } diff --git a/Sources/BraveWallet/MultipleCircleIconView.swift b/Sources/BraveWallet/MultipleCircleIconView.swift index 6e9b6ff30c1..3a4a1e6ff78 100644 --- a/Sources/BraveWallet/MultipleCircleIconView.swift +++ b/Sources/BraveWallet/MultipleCircleIconView.swift @@ -42,10 +42,15 @@ struct MultipleCircleIconView: View { HStack(spacing: -(min(iconSize, maxIconSize) / 2)) { let numberOfIcons = min(maxIcons, models.count) ForEach(0..: View { .frame(width: iconDotSize, height: iconDotSize) } .foregroundColor(.white) + .containerShape(ContainerShape(shape: shape)) ) } } diff --git a/Sources/BraveWallet/NetworkIcon.swift b/Sources/BraveWallet/NetworkIcon.swift index 4492b31f560..0a7d4757ee2 100644 --- a/Sources/BraveWallet/NetworkIcon.swift +++ b/Sources/BraveWallet/NetworkIcon.swift @@ -9,9 +9,7 @@ import BraveUI struct NetworkIcon: View { - var network: BraveWallet.NetworkInfo - - @ScaledMetric var length: CGFloat = 30 + let network: BraveWallet.NetworkInfo var body: some View { Group { @@ -36,14 +34,17 @@ struct NetworkIcon: View { } } .aspectRatio(1, contentMode: .fit) - .frame(width: length, height: length) } + @State private var monogramSize: CGSize = .zero private var networkIconMonogram: some View { Blockie(address: network.chainName, shape: .circle) + .readSize(onChange: { newSize in + monogramSize = newSize + }) .overlay( Text(network.chainName.first?.uppercased() ?? "") - .font(.system(size: length / 2, weight: .bold, design: .rounded)) + .font(.system(size: max(monogramSize.width, monogramSize.height) / 2, weight: .bold, design: .rounded)) .foregroundColor(.white) .shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1) ) @@ -58,3 +59,18 @@ struct NetworkIcon: View { return nil } } + +struct NetworkIconView: View { + + let network: BraveWallet.NetworkInfo + @ScaledMetric var length: CGFloat = 30 + var maxLength: CGFloat? + + var body: some View { + NetworkIcon(network: network) + .frame( + width: min(length, maxLength ?? length), + height: min(length, maxLength ?? length) + ) + } +}