Skip to content

Commit

Permalink
Fix #8600: Accounts Tab v2 (#8659)
Browse files Browse the repository at this point in the history
* Move Backup to `...` menu, and Add/Create Account specific for Accounts tab.

* Update `AccountsView` to v2 designs, create `AccountsStore` for building account cards with balances and price information.

* Create `AccountsStoreTests` unit test for fetching accounts and tokens with balance / total fiat for each account

* Card background using blockie material, refresh list on network change & show/hide test networks, small UI, tweaks

* 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.

* Add border to `...` icon in `MultipleCircleIconView` for consistency.
  • Loading branch information
StephenHeaps authored Jan 22, 2024
1 parent 11e4c96 commit 6872792
Show file tree
Hide file tree
Showing 20 changed files with 1,122 additions and 210 deletions.
352 changes: 268 additions & 84 deletions Sources/BraveWallet/Crypto/Accounts/AccountsView.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ struct AccountDetailsView: View {
}
.listStyle(InsetGroupedListStyle())
.listBackgroundColor(Color(UIColor.braveGroupedBackground))
.navigationTitle(Strings.Wallet.accountDetailsTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .cancellationAction) {
Expand Down
82 changes: 46 additions & 36 deletions Sources/BraveWallet/Crypto/AssetIconView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
Expand Down
123 changes: 86 additions & 37 deletions Sources/BraveWallet/Crypto/CryptoTabsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,38 @@ import SwiftUI
import BraveUI
import Strings

struct CryptoTabsView<DismissContent: ToolbarContent>: View {
private enum Tab: Equatable, Hashable, CaseIterable {
case portfolio
case activity
case accounts
case market

@ViewBuilder var tabLabel: some View {
switch self {
case .portfolio:
Label(Strings.Wallet.portfolioPageTitle, braveSystemImage: "leo.coins")
case .activity:
Label(Strings.Wallet.activityPageTitle, braveSystemImage: "leo.activity")
case .accounts:
Label(Strings.Wallet.accountsPageTitle, braveSystemImage: "leo.user.accounts")
case .market:
Label(Strings.Wallet.marketPageTitle, braveSystemImage: "leo.discover")
}
enum CryptoTab: Equatable, Hashable, CaseIterable {
case portfolio
case activity
case accounts
case market

@ViewBuilder var tabLabel: some View {
switch self {
case .portfolio:
Label(Strings.Wallet.portfolioPageTitle, braveSystemImage: "leo.coins")
case .activity:
Label(Strings.Wallet.activityPageTitle, braveSystemImage: "leo.activity")
case .accounts:
Label(Strings.Wallet.accountsPageTitle, braveSystemImage: "leo.user.accounts")
case .market:
Label(Strings.Wallet.marketPageTitle, braveSystemImage: "leo.discover")
}
}

}

struct CryptoTabsView<DismissContent: ToolbarContent>: View {
@ObservedObject var cryptoStore: CryptoStore
@ObservedObject var keyringStore: KeyringStore
var toolbarDismissContent: DismissContent

@State private var isShowingMainMenu: Bool = false
@State private var isTabShowingSettings: [Tab: Bool] = Tab.allCases.reduce(into: [Tab: Bool]()) { $0[$1] = false }
@State private var isTabShowingSettings: [CryptoTab: Bool] = CryptoTab.allCases.reduce(into: [CryptoTab: Bool]()) { $0[$1] = false }
@State private var isShowingSearch: Bool = false
@State private var isShowingBackup: Bool = false
@State private var isShowingAddAccount: Bool = false
@State private var fetchedPendingRequestsThisSession: Bool = false
@State private var selectedTab: Tab = .portfolio
@State private var selectedTab: CryptoTab = .portfolio

private var isConfirmationButtonVisible: Bool {
if case .transactions(let txs) = cryptoStore.pendingRequest {
Expand All @@ -64,9 +66,9 @@ struct CryptoTabsView<DismissContent: ToolbarContent>: View {
}
.navigationViewStyle(.stack)
.tabItem {
Tab.portfolio.tabLabel
CryptoTab.portfolio.tabLabel
}
.tag(Tab.portfolio)
.tag(CryptoTab.portfolio)

NavigationView {
TransactionsActivityView(
Expand All @@ -81,12 +83,13 @@ struct CryptoTabsView<DismissContent: ToolbarContent>: View {
}
.navigationViewStyle(.stack)
.tabItem {
Tab.activity.tabLabel
CryptoTab.activity.tabLabel
}
.tag(Tab.activity)
.tag(CryptoTab.activity)

NavigationView {
AccountsView(
store: cryptoStore.accountsStore,
cryptoStore: cryptoStore,
keyringStore: keyringStore
)
Expand All @@ -98,9 +101,9 @@ struct CryptoTabsView<DismissContent: ToolbarContent>: View {
}
.navigationViewStyle(.stack)
.tabItem {
Tab.accounts.tabLabel
CryptoTab.accounts.tabLabel
}
.tag(Tab.accounts)
.tag(CryptoTab.accounts)

NavigationView {
MarketView(
Expand All @@ -115,9 +118,9 @@ struct CryptoTabsView<DismissContent: ToolbarContent>: View {
}
.navigationViewStyle(.stack)
.tabItem {
Tab.market.tabLabel
CryptoTab.market.tabLabel
}
.tag(Tab.market)
.tag(CryptoTab.market)
}
.introspectTabBarController(customize: { tabBarController in
let appearance = UITabBarAppearance()
Expand Down Expand Up @@ -169,25 +172,71 @@ struct CryptoTabsView<DismissContent: ToolbarContent>: View {
)
.sheet(isPresented: $isShowingMainMenu) {
MainMenuView(
isFromPortfolio: selectedTab == .portfolio,
selectedTab: selectedTab,
isShowingSettings: Binding(get: {
self.isTabShowingSettings[selectedTab, default: false]
}, set: { isActive, _ in
self.isTabShowingSettings[selectedTab] = isActive
}),
isShowingBackup: $isShowingBackup,
isShowingAddAccount: $isShowingAddAccount,
keyringStore: keyringStore
)
.background(
Color.clear
.sheet(isPresented: Binding(get: {
isShowingBackup
}, set: { newValue in
if !newValue {
// dismiss menu if we're dismissing backup from menu
isShowingMainMenu = false
}
isShowingBackup = newValue
})) {
NavigationView {
BackupWalletView(
password: nil,
keyringStore: keyringStore
)
}
.navigationViewStyle(.stack)
.environment(\.modalPresentationMode, $isShowingBackup)
.accentColor(Color(.braveBlurpleTint))
}
)
.background(
Color.clear
.sheet(isPresented: Binding(get: {
isShowingAddAccount
}, set: { newValue in
if !newValue {
// dismiss menu if we're dismissing add account from menu
isShowingMainMenu = false
}
isShowingAddAccount = newValue
})) {
NavigationView {
AddAccountView(
keyringStore: keyringStore,
networkStore: cryptoStore.networkStore
)
}
.navigationViewStyle(StackNavigationViewStyle())
}
)
}
}

@ToolbarContentBuilder private var sharedToolbarItems: some ToolbarContent {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
cryptoStore.isPresentingAssetSearch = true
}) {
Label(Strings.Wallet.searchTitle, systemImage: "magnifyingglass")
.labelStyle(.iconOnly)
.foregroundColor(Color(.braveBlurpleTint))
if selectedTab == .portfolio {
Button(action: {
cryptoStore.isPresentingAssetSearch = true
}) {
Label(Strings.Wallet.searchTitle, systemImage: "magnifyingglass")
.labelStyle(.iconOnly)
.foregroundColor(Color(.braveBlurpleTint))
}
}
Button(action: { self.isShowingMainMenu = true }) {
Label(Strings.Wallet.otherWalletActionsAccessibilityTitle, braveSystemImage: "leo.more.horizontal")
Expand All @@ -199,7 +248,7 @@ struct CryptoTabsView<DismissContent: ToolbarContent>: View {
toolbarDismissContent
}

private func settingsNavigationLink(for tab: Tab) -> some View {
private func settingsNavigationLink(for tab: CryptoTab) -> some View {
NavigationLink(
destination: Web3SettingsView(
settingsStore: cryptoStore.settingsStore,
Expand Down
35 changes: 32 additions & 3 deletions Sources/BraveWallet/Crypto/MainMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import Preferences

struct MainMenuView: View {

let isFromPortfolio: Bool
let selectedTab: CryptoTab
@Binding var isShowingSettings: Bool
@Binding var isShowingBackup: Bool
@Binding var isShowingAddAccount: Bool
let keyringStore: KeyringStore

@ObservedObject private var isShowingBalances = Preferences.Wallet.isShowingBalances
Expand All @@ -37,6 +39,16 @@ struct MainMenuView: View {
}
.frame(height: rowHeight)

Button(action: {
isShowingBackup = true
}) {
MenuRowView(
iconBraveSystemName: "leo.safe",
title: "Back Up Now"
)
}
.frame(height: rowHeight)

Button(action: {
isShowingSettings = true
presentationMode.dismiss()
Expand All @@ -48,9 +60,12 @@ struct MainMenuView: View {
}
.frame(height: rowHeight)

if isFromPortfolio {
if selectedTab == .portfolio {
Divider()
portfolioSettings
} else if selectedTab == .accounts {
Divider()
accountsMenuItems
}

Divider()
Expand Down Expand Up @@ -124,6 +139,18 @@ struct MainMenuView: View {
)
.frame(height: rowHeight)
}

@ViewBuilder private var accountsMenuItems: some View {
Button(action: {
self.isShowingAddAccount = true
}) {
MenuRowView(
iconBraveSystemName: "leo.plus.add",
title: Strings.Wallet.addAccountTitle
)
}
.frame(height: rowHeight)
}
}

#if DEBUG
Expand All @@ -132,8 +159,10 @@ struct MainMenuView_Previews: PreviewProvider {
Color.white
.sheet(isPresented: .constant(true), content: {
MainMenuView(
isFromPortfolio: true,
selectedTab: .portfolio,
isShowingSettings: .constant(false),
isShowingBackup: .constant(false),
isShowingAddAccount: .constant(false),
keyringStore: .previewStoreWithWalletCreated
)
})
Expand Down
Loading

0 comments on commit 6872792

Please sign in to comment.