From 4db7a39cc93728a2f9eb977b1eefab98137fdb63 Mon Sep 17 00:00:00 2001 From: Nuo Xu Date: Thu, 7 Dec 2023 11:47:23 -0800 Subject: [PATCH] Fix #8375: Wallet account blockie v2 (#8510) --- Sources/BraveWallet/Blockies/Blockies.swift | 60 ++++++++++------- .../Crypto/Accounts/AccountView.swift | 15 +---- .../Activity/AccountActivityView.swift | 1 + .../MultipleAccountBlockiesView.swift | 1 + .../BraveWallet/Crypto/AssetIconView.swift | 4 +- .../Crypto/Market/MarketView.swift | 4 +- .../Crypto/NFT/NFTDetailView.swift | 2 +- .../Crypto/Portfolio/PortfolioView.swift | 2 +- .../BraveWallet/MultipleCircleIconView.swift | 65 ++++++++++++++----- Sources/BraveWallet/NetworkIcon.swift | 2 +- .../SignInWithEthereumView.swift | 3 +- .../BraveWallet/Panels/WalletPanelView.swift | 34 ++++++---- 12 files changed, 122 insertions(+), 71 deletions(-) diff --git a/Sources/BraveWallet/Blockies/Blockies.swift b/Sources/BraveWallet/Blockies/Blockies.swift index 44d2dc5f90a..93f777eea04 100644 --- a/Sources/BraveWallet/Blockies/Blockies.swift +++ b/Sources/BraveWallet/Blockies/Blockies.swift @@ -26,9 +26,8 @@ private struct XorshiftRandomNumberGenerator: RandomNumberGenerator { class Blockies { private var generator: XorshiftRandomNumberGenerator private let colors: [Int] = [ - 0x5B5C63, 0x151E9A, 0x2197F9, 0x1FC3DC, 0x086582, - 0x67D4B4, 0xAFCE57, 0xF0CB44, 0xF28A29, 0xFC798F, - 0xC1226E, 0xFAB5EE, 0x9677EE, 0x5433B0, + 0x423EEE, 0xE2E2FC, 0xFE5907, 0xFEDED6, 0x5F5CF1, + 0x171553, 0x1C1E26, 0xE1E2E8, ] init(seed: String) { @@ -53,13 +52,17 @@ class Blockies { let normalized = Double(generator.next()) / Double(Int32.max) return UIColor(rgb: colors[Int(floor(normalized * 100)) % colors.count]) } + + func rand() -> Double { + Double(generator.next()) / Double(Int32.max) + } func image(length: Int, scale: CGFloat) -> UIImage { let color = makeColor() let backgroundColor = makeColor() let spotColor = makeColor() - - func data() -> [[Double]] { + + func data() -> [Double] { let dataLength = Int(ceil(Double(length) / 2.0)) var data: [[Double]] = [] for _ in 0.. 0 { - if value == 1 { - color.setFill() - } else { - spotColor.setFill() - } - context.fill(rect) - } + for (i, value) in data.enumerated() where value > 0 { + let row = floor(Double(i) / width) + let col = i % Int(width) + let fillColor = value == 1 ? color : spotColor + let shapeType = floor(rand() * 3) + + switch shapeType { + case 0: + let rectSizeMultiplier = rand() * 2 + let rect = CGRect(x: Int(col) * Int(scale), y: Int(row) * Int(scale), width: Int(scale * rectSizeMultiplier), height: Int(scale * rectSizeMultiplier)) + fillColor.setFill() + context.fill(rect) + case 1: + let rectSizeMultiplier = rand() + let x = Int(col) * Int(scale) + Int(scale) / 2 - Int(scale * rectSizeMultiplier / 2) + let y = Int(row) * Int(scale) + Int(scale) / 2 - Int(scale * rectSizeMultiplier / 2) + let rect = CGRect(x: x, y: y, width: Int(scale * rectSizeMultiplier), height: Int(scale * rectSizeMultiplier)) + fillColor.setFill() + context.cgContext.fillEllipse(in: rect) + default: + break } } } + return image } } @@ -108,12 +123,11 @@ struct Blockie: View { } var address: String - var shape: Shape = .circle + var shape: Shape = .rectangle private var base: some View { - Image(uiImage: Blockies(seed: address.lowercased()).image(length: 8, scale: 16)) + Image(uiImage: Blockies(seed: address.lowercased()).image(length: 4, scale: 25)) .resizable() - .blur(radius: 8, opaque: true) } var body: some View { @@ -122,7 +136,7 @@ struct Blockie: View { .clipShape(Circle()) } else { base - .clipShape(Rectangle()) + .clipShape(RoundedRectangle(cornerRadius: 4)) } } } diff --git a/Sources/BraveWallet/Crypto/Accounts/AccountView.swift b/Sources/BraveWallet/Crypto/Accounts/AccountView.swift index aeece6962e4..db6fe9613d9 100644 --- a/Sources/BraveWallet/Crypto/Accounts/AccountView.swift +++ b/Sources/BraveWallet/Crypto/Accounts/AccountView.swift @@ -11,26 +11,15 @@ struct AccountView: View { var address: String /// The account name describing what the account is for var name: String - /// The shape of the blockie used - var blockieShape: Blockie.Shape = .circle @ScaledMetric private var avatarSize = 40.0 private let maxAvatarSize: CGFloat = 80.0 - /// Corner radius only applied when `blockShape` is `rectangle`. - @ScaledMetric var cornerRadius = 4 var body: some View { HStack { Group { - if blockieShape == .rectangle { - Blockie(address: address, shape: blockieShape) - .frame(width: min(avatarSize, maxAvatarSize), height: min(avatarSize, maxAvatarSize)) - .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) - } else { - Blockie(address: address, shape: blockieShape) - .frame(width: min(avatarSize, maxAvatarSize), height: min(avatarSize, maxAvatarSize)) - .clipShape(Circle()) - } + Blockie(address: address) + .frame(width: min(avatarSize, maxAvatarSize), height: min(avatarSize, maxAvatarSize)) } VStack(alignment: .leading, spacing: 2) { Text(name) diff --git a/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift b/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift index 851313250bd..e05297ff967 100644 --- a/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift +++ b/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift @@ -190,6 +190,7 @@ private struct AccountActivityHeaderView: View { VStack { Blockie(address: account.address) .frame(width: 64, height: 64) + .clipShape(RoundedRectangle(cornerRadius: 4)) .accessibilityHidden(true) VStack(spacing: 4) { Text(account.name) diff --git a/Sources/BraveWallet/Crypto/Accounts/MultipleAccountBlockiesView.swift b/Sources/BraveWallet/Crypto/Accounts/MultipleAccountBlockiesView.swift index f59f9de2432..10bc592aa56 100644 --- a/Sources/BraveWallet/Crypto/Accounts/MultipleAccountBlockiesView.swift +++ b/Sources/BraveWallet/Crypto/Accounts/MultipleAccountBlockiesView.swift @@ -16,6 +16,7 @@ struct MultipleAccountBlockiesView: View { var body: some View { MultipleCircleIconView( models: accountAddresses, + shape: .rectangle, iconSize: blockieSize, maxIconSize: maxBlockieSize, iconDotSize: blockieDotSize, diff --git a/Sources/BraveWallet/Crypto/AssetIconView.swift b/Sources/BraveWallet/Crypto/AssetIconView.swift index 7eef4a84385..d91c493e488 100644 --- a/Sources/BraveWallet/Crypto/AssetIconView.swift +++ b/Sources/BraveWallet/Crypto/AssetIconView.swift @@ -28,7 +28,9 @@ struct AssetIconView: View { var maxNetworkSymbolLength: CGFloat? private var fallbackMonogram: some View { - Blockie(address: token.contractAddress) + 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)) diff --git a/Sources/BraveWallet/Crypto/Market/MarketView.swift b/Sources/BraveWallet/Crypto/Market/MarketView.swift index 92d0e05343f..4799d30c0f5 100644 --- a/Sources/BraveWallet/Crypto/Market/MarketView.swift +++ b/Sources/BraveWallet/Crypto/Market/MarketView.swift @@ -65,7 +65,9 @@ struct MarketView: View { WebImage(url: URL(string: coinMarket.image)) .resizable() .placeholder { - Blockie(address: coinMarket.id) + BlockieMaterial(address: coinMarket.id) + .blur(radius: 8, opaque: true) + .clipShape(Circle()) .overlay( Text(coinMarket.symbol.first?.uppercased() ?? "") .font(.system(size: coinLength / 2, weight: .bold, design: .rounded)) diff --git a/Sources/BraveWallet/Crypto/NFT/NFTDetailView.swift b/Sources/BraveWallet/Crypto/NFT/NFTDetailView.swift index ce42df78091..a5d08d836e9 100644 --- a/Sources/BraveWallet/Crypto/NFT/NFTDetailView.swift +++ b/Sources/BraveWallet/Crypto/NFT/NFTDetailView.swift @@ -82,7 +82,7 @@ struct NFTDetailView: View { .overlay(alignment: .bottomTrailing) { ZStack { if let owner = nftDetailStore.owner { - Blockie(address: owner.address, shape: .rectangle) + Blockie(address: owner.address) .overlay( RoundedRectangle(cornerRadius: 4) .stroke(lineWidth: 2) diff --git a/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift b/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift index 55a803fc155..50efb319c1f 100644 --- a/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift +++ b/Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift @@ -101,7 +101,7 @@ struct PortfolioAssetGroupHeaderView: View { if case let .network(networkInfo) = group.groupType { NetworkIcon(network: networkInfo, length: 32) } else if case let .account(accountInfo) = group.groupType { - Blockie(address: accountInfo.address, shape: .rectangle) + Blockie(address: accountInfo.address) .frame(width: 32, height: 32) .clipShape(RoundedRectangle(cornerRadius: 4)) } diff --git a/Sources/BraveWallet/MultipleCircleIconView.swift b/Sources/BraveWallet/MultipleCircleIconView.swift index 6f9b47b7438..6e9b6ff30c1 100644 --- a/Sources/BraveWallet/MultipleCircleIconView.swift +++ b/Sources/BraveWallet/MultipleCircleIconView.swift @@ -6,39 +6,70 @@ import SwiftUI import BraveCore +struct ContainerShape: InsettableShape { + enum Shape { + case circle, rectangle + } + let shape: Shape + var inset: CGFloat = 0 + + func inset(by amount: CGFloat) -> some InsettableShape { + ContainerShape(shape: shape, inset: amount) + } + + func path(in rect: CGRect) -> Path { + let rect = rect.insetBy(dx: inset, dy: inset) + switch shape { + case .circle: + return Circle().path(in: rect) + case .rectangle: + return RoundedRectangle(cornerRadius: 4).path(in: rect) + } + } +} + struct MultipleCircleIconView: View { let models: [Model] + var shape: ContainerShape.Shape = .circle let maxIcons = 3 @ScaledMetric var iconSize = 16.0 var maxIconSize: CGFloat = 32 @ScaledMetric var iconDotSize = 2.0 - + @ViewBuilder var iconView: (Model) -> IconView - + var body: some View { HStack(spacing: -(min(iconSize, maxIconSize) / 2)) { let numberOfIcons = min(maxIcons, models.count) ForEach(0.. maxIcons { - Circle() - .foregroundColor(Color(.braveBlurpleTint)) - .frame(width: min(iconSize, maxIconSize), height: min(iconSize, maxIconSize)) - .overlay( - HStack(spacing: 1) { - Circle() - .frame(width: iconDotSize, height: iconDotSize) - Circle() - .frame(width: iconDotSize, height: iconDotSize) - Circle() - .frame(width: iconDotSize, height: iconDotSize) - } - .foregroundColor(.white) - ) + Group { + if shape == .circle { + Circle() + } else { + RoundedRectangle(cornerRadius: 4) + } + } + .foregroundColor(Color(.braveBlurpleTint)) + .frame(width: min(iconSize, maxIconSize), height: min(iconSize, maxIconSize)) + .overlay( + HStack(spacing: 1) { + ContainerRelativeShape() + .frame(width: iconDotSize, height: iconDotSize) + ContainerRelativeShape() + .frame(width: iconDotSize, height: iconDotSize) + ContainerRelativeShape() + .frame(width: iconDotSize, height: iconDotSize) + } + .foregroundColor(.white) + ) } } } diff --git a/Sources/BraveWallet/NetworkIcon.swift b/Sources/BraveWallet/NetworkIcon.swift index 55b5166a3df..4492b31f560 100644 --- a/Sources/BraveWallet/NetworkIcon.swift +++ b/Sources/BraveWallet/NetworkIcon.swift @@ -40,7 +40,7 @@ struct NetworkIcon: View { } private var networkIconMonogram: some View { - Blockie(address: network.chainName) + Blockie(address: network.chainName, shape: .circle) .overlay( Text(network.chainName.first?.uppercased() ?? "") .font(.system(size: length / 2, weight: .bold, design: .rounded)) diff --git a/Sources/BraveWallet/Panels/Signature Request/SignInWithEthereumView.swift b/Sources/BraveWallet/Panels/Signature Request/SignInWithEthereumView.swift index d161c8fb408..a971717e0c3 100644 --- a/Sources/BraveWallet/Panels/Signature Request/SignInWithEthereumView.swift +++ b/Sources/BraveWallet/Panels/Signature Request/SignInWithEthereumView.swift @@ -73,8 +73,7 @@ struct SignInWithEthereumView: View { AddressView(address: account.address) { AccountView( address: account.address, - name: account.name, - blockieShape: .rectangle + name: account.name ) } diff --git a/Sources/BraveWallet/Panels/WalletPanelView.swift b/Sources/BraveWallet/Panels/WalletPanelView.swift index bae63292bba..c1ddc460da5 100644 --- a/Sources/BraveWallet/Panels/WalletPanelView.swift +++ b/Sources/BraveWallet/Panels/WalletPanelView.swift @@ -307,11 +307,11 @@ struct WalletPanelView: View { .lineLimit(1) } } - .foregroundColor(.white) + .foregroundColor(Color(.braveLabel)) .font(.caption.weight(.semibold)) .padding(.init(top: 6, leading: 12, bottom: 6, trailing: 12)) .background( - Color.white.opacity(0.5) + Color(.secondaryButtonTint) .clipShape(Capsule().inset(by: 0.5).stroke()) ) .clipShape(Capsule()) @@ -321,7 +321,7 @@ struct WalletPanelView: View { private var networkPickerButton: some View { NetworkPicker( - style: .init(textColor: .white, borderColor: .white), + style: .init(textColor: .braveLabel, borderColor: .secondaryButtonTint), isForOrigin: true, keyringStore: keyringStore, networkStore: networkStore @@ -331,7 +331,7 @@ struct WalletPanelView: View { private var pendingRequestsButton: some View { Button(action: { presentWalletWithContext(.pendingRequests) }) { Image(braveSystemName: "leo.notification.dot") - .foregroundColor(.white) + .foregroundColor(Color(.braveLabel)) .frame(minWidth: 30, minHeight: 44) .contentShape(Rectangle()) } @@ -342,6 +342,7 @@ struct WalletPanelView: View { presentWalletWithContext(.default) } label: { Image(systemName: "arrow.up.left.and.arrow.down.right") + .foregroundColor(Color(.braveLabel)) .rotationEffect(.init(degrees: 90)) .frame(minWidth: 30, minHeight: 44) .contentShape(Rectangle()) @@ -363,6 +364,7 @@ struct WalletPanelView: View { } } label: { Image(braveSystemName: "leo.more.horizontal") + .foregroundColor(Color(.braveLabel)) .frame(minWidth: 30, minHeight: 44) .contentShape(Rectangle()) } @@ -392,6 +394,7 @@ struct WalletPanelView: View { if sizeCategory.isAccessibilityCategory { VStack { Text(Strings.Wallet.braveWallet) + .foregroundColor(Color(.braveLabel)) .font(.headline) .background( Color.clear @@ -409,7 +412,7 @@ struct WalletPanelView: View { .padding(.horizontal, 16) .padding(.vertical, 4) .overlay( - Color.white.opacity(0.3) // Divider + Color(.braveLabel).opacity(0.3) // Divider .frame(height: pixelLength), alignment: .bottom ) @@ -423,6 +426,7 @@ struct WalletPanelView: View { } Spacer() Text(Strings.Wallet.braveWallet) + .foregroundColor(Color(.braveLabel)) .font(.headline) .background( Color.clear @@ -436,7 +440,7 @@ struct WalletPanelView: View { .padding(.horizontal, 16) .padding(.vertical, 4) .overlay( - Color.white.opacity(0.3) // Divider + Color(.braveLabel).opacity(0.3) // Divider .frame(height: pixelLength), alignment: .bottom ) @@ -451,6 +455,7 @@ struct WalletPanelView: View { } Spacer() } + .padding(.bottom, 12) VStack(spacing: 12) { Button { presentWalletWithContext(.accountSelection) @@ -458,20 +463,24 @@ struct WalletPanelView: View { Blockie(address: keyringStore.selectedAccount.address) .frame(width: blockieSize, height: blockieSize) .overlay( - Circle().strokeBorder(lineWidth: 2, antialiased: true) + RoundedRectangle(cornerRadius: 4) + .strokeBorder(Color(.braveLabel).opacity(0.6), style: .init(lineWidth: 1)) ) .overlay( Image(systemName: "chevron.down.circle.fill") .font(.footnote) - .background(Color(.braveLabel).clipShape(Circle())), + .background(Color(.braveLabel).clipShape(Circle())) + .offset(x: -4, y: 4), alignment: .bottomLeading ) } VStack(spacing: 4) { Text(keyringStore.selectedAccount.name) + .foregroundColor(Color(.braveLabel)) .font(.headline) AddressView(address: keyringStore.selectedAccount.address) { Text(keyringStore.selectedAccount.address.truncatedAddress) + .foregroundColor(Color(.braveLabel)) .font(.callout) .multilineTextAlignment(.center) } @@ -487,36 +496,39 @@ struct WalletPanelView: View { Text(currencyFormatter.string(from: NSNumber(value: (Double(nativeAsset?.price ?? "") ?? 0) * (nativeAsset?.totalBalance ?? 0.0))) ?? "") .font(.callout) } + .foregroundColor(Color(.braveLabel)) .padding(.vertical) HStack(spacing: 0) { Button { presentBuySendSwap() } label: { Image(braveSystemName: "leo.swap.horizontal") + .foregroundColor(Color(.braveLabel)) .imageScale(.large) .padding(.horizontal, 44) .padding(.vertical, 8) } .background(buySendSwapBackground) - Color.white.opacity(0.6) + Color(.braveLabel).opacity(0.6) .frame(width: pixelLength) Button { presentWalletWithContext(.transactionHistory) } label: { Image(braveSystemName: "leo.history") + .foregroundColor(Color(.braveLabel)) .imageScale(.large) .padding(.horizontal, 44) .padding(.vertical, 8) } } - .overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).strokeBorder(Color.white.opacity(0.6), style: .init(lineWidth: pixelLength))) + .overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).strokeBorder(Color(.braveLabel).opacity(0.6), style: .init(lineWidth: pixelLength))) } .padding(EdgeInsets(top: 12, leading: 12, bottom: 24, trailing: 12)) } } .foregroundColor(.white) .background( - BlockieMaterial(address: keyringStore.selectedAccount.address) + Color(.braveGroupedBackground) .ignoresSafeArea() ) .onChange(of: cryptoStore.pendingRequest) { newValue in