Skip to content

Commit

Permalink
Fix brave/brave-ios#8375: Wallet account blockie v2 (brave/brave-ios#…
Browse files Browse the repository at this point in the history
  • Loading branch information
nuo-xu authored Dec 7, 2023
1 parent b58cb63 commit a480103
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 71 deletions.
60 changes: 37 additions & 23 deletions Sources/BraveWallet/Blockies/Blockies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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..<length {
Expand All @@ -72,31 +75,43 @@ class Blockies {
let mirrorCopy = row.reversed()
data.append(row + mirrorCopy)
}
return data
return data.flatMap { $0 }
}

let size = CGSize(width: CGFloat(length) * scale, height: CGFloat(length) * scale)
let renderer = UIGraphicsImageRenderer(size: size)
let data = data()
let width = sqrt(Double(data.count))

let image = renderer.image { context in
backgroundColor.setFill()
context.fill(.init(origin: .zero, size: size))
spotColor.setFill()
for (y, row) in data.enumerated() {
for (x, value) in row.enumerated() {
let rect = CGRect(x: x, y: y, width: 1, height: 1)
.applying(CGAffineTransform(scaleX: scale, y: scale))
if value > 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
}
}
Expand All @@ -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 {
Expand All @@ -122,7 +136,7 @@ struct Blockie: View {
.clipShape(Circle())
} else {
base
.clipShape(Rectangle())
.clipShape(RoundedRectangle(cornerRadius: 4))
}
}
}
Expand Down
15 changes: 2 additions & 13 deletions Sources/BraveWallet/Crypto/Accounts/AccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct MultipleAccountBlockiesView: View {
var body: some View {
MultipleCircleIconView(
models: accountAddresses,
shape: .rectangle,
iconSize: blockieSize,
maxIconSize: maxBlockieSize,
iconDotSize: blockieDotSize,
Expand Down
4 changes: 3 additions & 1 deletion Sources/BraveWallet/Crypto/AssetIconView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 3 additions & 1 deletion Sources/BraveWallet/Crypto/Market/MarketView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraveWallet/Crypto/NFT/NFTDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraveWallet/Crypto/Portfolio/PortfolioView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
65 changes: 48 additions & 17 deletions Sources/BraveWallet/MultipleCircleIconView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<IconView: View, Model>: 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..<numberOfIcons, id: \.self) { index in
iconView(models[index])
.frame(width: min(iconSize, maxIconSize), height: min(iconSize, maxIconSize))
.overlay(Circle().stroke(Color(.secondaryBraveGroupedBackground), lineWidth: 1))
.clipShape(ContainerRelativeShape())
.overlay(ContainerRelativeShape().stroke(Color(.secondaryBraveGroupedBackground), lineWidth: 1))
.containerShape(ContainerShape(shape: shape))
.zIndex(Double(numberOfIcons - index))
}
if models.count > 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)
)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraveWallet/NetworkIcon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ struct SignInWithEthereumView: View {
AddressView(address: account.address) {
AccountView(
address: account.address,
name: account.name,
blockieShape: .rectangle
name: account.name
)
}

Expand Down
Loading

0 comments on commit a480103

Please sign in to comment.