Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ABW-2170] Fix Account Card Layout #705

Merged
merged 3 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/Core/DesignSystem/Fonts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ extension SwiftUI.Font.App {
.custom(FontFamily.IBMPlexSans.semiBold, size: 18)
}

public var resourceLabel: SwiftUI.Font {
.custom(FontFamily.IBMPlexSans.semiBold, size: 11)
}

public var backButton: SwiftUI.Font {
.custom(FontFamily.IBMPlexSans.medium, size: 18)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum TextStyle {
case sheetTitle
case sectionHeader
case secondaryHeader
case resourceLabel
case body1Header
case body1HighImportance
case body1Regular
Expand All @@ -25,6 +26,7 @@ extension TextStyle {
case .sheetTitle: return .app.sheetTitle
case .sectionHeader: return .app.sectionHeader
case .secondaryHeader: return .app.secondaryHeader
case .resourceLabel: return .app.resourceLabel
case .body1Header: return .app.body1Header
case .body1HighImportance: return .app.body1HighImportance
case .body1Regular: return .app.body1Regular
Expand All @@ -42,7 +44,7 @@ extension TextStyle {

var lineSpacing: CGFloat {
switch self {
case .sheetTitle:
case .sheetTitle, .resourceLabel:
return 0
case .sectionHeader, .secondaryHeader, .body1Header,
.body1HighImportance, .body1Regular, .body1StandaloneLink, .body1Link:
Expand Down
158 changes: 81 additions & 77 deletions Sources/Features/AccountListFeature/Components/Row/Row+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ import FeaturePrelude
// MARK: - AccountList.Row.View
extension AccountList.Row {
public struct ViewState: Equatable {
struct FungibleResources: Equatable {
static let maxNumberOfIcons = 5

let icons: [TokenThumbnail.Content]
let additionalItemsText: String?
}

let name: String
let address: AccountAddress
let appearanceID: Profile.Network.Account.AppearanceID
Expand All @@ -36,12 +29,16 @@ extension AccountList.Row {
let tag: AccountTag?

let shouldShowSecurityPrompt: Bool
let fungibleResourceIcons: FungibleResources
let fungibleResourceIcons: [TokenThumbnail.Content]
let nonFungibleResourcesCount: Int
let poolUnitsCount: Int

var showMoreFungibles: Bool {
nonFungibleResourcesCount == 0 && poolUnitsCount == 0
}

init(state: State) {
self.name = state.account.displayName.rawValue
self.name = state.account.displayName.rawValue + state.account.displayName.rawValue + state.account.displayName.rawValue
self.address = state.account.address
self.appearanceID = state.account.appearanceID
self.isLoadingResources = state.portfolio.isLoading
Expand All @@ -54,34 +51,24 @@ extension AccountList.Row {

// Resources
guard let portfolio = state.portfolio.wrappedValue else {
self.fungibleResourceIcons = .init(icons: [], additionalItemsText: nil)
self.fungibleResourceIcons = []
self.nonFungibleResourcesCount = 0
self.poolUnitsCount = 0

return
}

self.fungibleResourceIcons = {
let fungibleResources = portfolio.fungibleResources
let xrdIcon: [TokenThumbnail.Content] = fungibleResources.xrdResource != nil ? [.xrd] : []

let otherIcons: [TokenThumbnail.Content] = fungibleResources.nonXrdResources
.map { .known($0.iconURL) }
let icons = xrdIcon + otherIcons
let hiddenCount = max(icons.count - FungibleResources.maxNumberOfIcons, 0)
let additionalItems = hiddenCount > 0 ? "+\(hiddenCount)" : nil

return .init(icons: icons.dropLast(hiddenCount), additionalItemsText: additionalItems)
}()
let fungibleResources = portfolio.fungibleResources
let xrdIcon: [TokenThumbnail.Content] = fungibleResources.xrdResource != nil ? [.xrd] : []
let otherIcons: [TokenThumbnail.Content] = fungibleResources.nonXrdResources.map { .known($0.iconURL) }
self.fungibleResourceIcons = xrdIcon + otherIcons

self.nonFungibleResourcesCount = portfolio.nonFungibleResources.count

self.poolUnitsCount = portfolio.poolUnitResources.radixNetworkStakes.count
+ portfolio.poolUnitResources.poolUnits.count
self.poolUnitsCount = portfolio.poolUnitResources.radixNetworkStakes.count + portfolio.poolUnitResources.poolUnits.count
}
}

@MainActor
public struct View: SwiftUI.View {
private let store: StoreOf<AccountList.Row>

Expand All @@ -93,7 +80,12 @@ extension AccountList.Row {
WithViewStore(store, observe: ViewState.init(state:), send: { .view($0) }) { viewStore in
VStack(alignment: .leading, spacing: .medium3) {
VStack(alignment: .leading, spacing: .zero) {
headerView(with: viewStore.name)
Text(viewStore.name)
.lineLimit(1)
.textStyle(.body1Header)
.foregroundColor(.app.white)
.frame(maxWidth: .infinity, alignment: .leading)

HStack {
AddressView(.address(.account(viewStore.address)))
.foregroundColor(.app.whiteTransparent)
Expand All @@ -108,6 +100,7 @@ extension AccountList.Row {
}

ownedResourcesList(viewStore)

if viewStore.shouldShowSecurityPrompt {
securityPromptView(viewStore)
}
Expand Down Expand Up @@ -135,25 +128,33 @@ extension AccountList.Row.View {

// Crates the view of the account owned resources
func ownedResourcesList(_ viewStore: ViewStoreOf<AccountList.Row>) -> some View {
HStack(spacing: .medium1) {
if !viewStore.fungibleResourceIcons.icons.isEmpty {
resourcesContainer(
text: viewStore.fungibleResourceIcons.additionalItemsText
) {
fungibleResourcesList(viewStore)
HStack(spacing: .small1) {
if !viewStore.fungibleResourceIcons.isEmpty {
let icons = viewStore.fungibleResourceIcons
if viewStore.showMoreFungibles {
ViewThatFits(in: .horizontal) {
FungibleResourcesSection(fungibles: icons, itemLimit: nil)
FungibleResourcesSection(fungibles: icons, itemLimit: 10)
}
} else {
ViewThatFits(in: .horizontal) {
FungibleResourcesSection(fungibles: icons, itemLimit: 5)
FungibleResourcesSection(fungibles: icons, itemLimit: 4)
FungibleResourcesSection(fungibles: icons, itemLimit: 3)
}
}
}

if viewStore.nonFungibleResourcesCount > 0 {
resourcesContainer(text: "\(viewStore.nonFungibleResourcesCount)") {
Labeled(text: "\(viewStore.nonFungibleResourcesCount)") {
Image(asset: AssetResource.nft)
.resizable()
.frame(Constants.iconSize)
}
}

if viewStore.poolUnitsCount > 0 {
resourcesContainer(text: "\(viewStore.poolUnitsCount)") {
Labeled(text: "\(viewStore.poolUnitsCount)") {
Image(asset: AssetResource.poolUnit)
.resizable()
.frame(Constants.iconSize)
Expand All @@ -165,42 +166,60 @@ extension AccountList.Row.View {
.cornerRadius(Constants.iconSize.rawValue / 4)
}

// Resources container to display a combination of any View + additional text (aka +10)
private func resourcesContainer(text: String?, @ViewBuilder content: () -> some View) -> some View {
// Negative spacing, so that the text number starts from the last icon.
// Need to be sure that the background of the text is properly displayed.
HStack(spacing: -Constants.iconSize.rawValue) {
content()
if let text {
// The text background needs to go behind the `content`
textContainer(text).zIndex(-1)
struct FungibleResourcesSection: View {
let fungibles: [TokenThumbnail.Content]
let itemLimit: Int?

var body: some View {
let displayedIconCount = min(fungibles.count, itemLimit ?? .max)
let displayedIcons = fungibles.prefix(displayedIconCount).identifiablyEnumerated()
let hiddenCount = fungibles.count - displayedIconCount
let label = hiddenCount > 0 ? "+\(hiddenCount)" : nil

HStack(alignment: .center, spacing: -Constants.iconSize.rawValue / 3) {
ForEach(displayedIcons) { item in
Labeled(text: item.offset == displayedIconCount - 1 ? label : nil, isFungible: true) {
TokenThumbnail(item.element, size: Constants.iconSize)
}
.zIndex(Double(-item.offset))
}
}
}
}

// The container displaying the resources number
private func textContainer(_ text: String) -> some View {
Text(text)
.foregroundColor(.white)
.padding(.leading, Constants.iconSize.rawValue + 4) // Padding so that the text is visible
.padding(.trailing, 4)
.frame(
minWidth: Constants.iconSize.rawValue * 2,
minHeight: Constants.iconSize.rawValue
)
.background(.app.whiteTransparent2)
.cornerRadius(Constants.iconSize.rawValue / 2)
}
// Resources container to display a combination of any View + additional text. Tighten when used on round icons.
struct Labeled<Content: View>: View {
let text: String?
var isFungible: Bool = false
let content: () -> Content

// The list of fungible resources
private func fungibleResourcesList(_ viewStore: ViewStoreOf<AccountList.Row>) -> some View {
HStack(alignment: .center, spacing: -Constants.iconSize.rawValue / 3) {
ForEach(viewStore.fungibleResourceIcons.icons.identifiablyEnumerated()) { item in
TokenThumbnail(item.element, size: Constants.iconSize)
.zIndex(Double(-item.offset))
var body: some View {
if let text {
ResourceLabel(text: text, isFungible: isFungible)
.overlay(alignment: .leading, content: content)
} else {
content()
}
}
}

struct ResourceLabel: View {
let text: String
let isFungible: Bool

var body: some View {
Text(text)
.lineLimit(1)
.textStyle(.resourceLabel)
.fixedSize()
.foregroundColor(.white)
.padding(.horizontal, .small2)
.frame(minWidth: .medium1, minHeight: Constants.iconSize.rawValue)
.padding(.leading, Constants.iconSize.rawValue - (isFungible ? .small3 : 0))
.background(.app.whiteTransparent2)
.cornerRadius(Constants.iconSize.rawValue / 2)
}
}
}

extension AccountList.Row.View {
Expand Down Expand Up @@ -260,21 +279,6 @@ public struct OffsetIdentified<Element>: Identifiable {
public let element: Element
}

extension AccountList.Row.View {
@ViewBuilder
private func headerView(
with name: String
) -> some SwiftUI.View {
HStack {
Text(name)
.foregroundColor(.app.white)
.textStyle(.body1Header)
.fixedSize()
Spacer()
}
}
}

extension AccountList.Row.ViewState.AccountTag {
var display: String {
switch self {
Expand Down
Loading