Skip to content
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
6 changes: 6 additions & 0 deletions Flipcash/Core/Screens/Main/ExchangedBalance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ extension Array where Element == ExchangedBalance {
fundable()
}

/// These balances without Dollars, for a picker that names a community currency: dollars
/// is the stable token, not one.
func excludingUSDF() -> [ExchangedBalance] {
filter { $0.stored.mint != .usdf }
}

/// Balances eligible to withdraw.
func withdrawable() -> [ExchangedBalance] {
fundable()
Expand Down
60 changes: 25 additions & 35 deletions Flipcash/Core/Screens/Main/SelectCurrencyScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SwiftUI
import FlipcashUI
import FlipcashCore

struct SelectCurrencyScreen<Header: View>: View {
struct SelectCurrencyScreen<Footer: View>: View {

@Binding var isPresented: Bool

Expand All @@ -19,39 +19,40 @@ struct SelectCurrencyScreen<Header: View>: View {
let action: (ExchangedBalance) -> Void
let isEnabled: (ExchangedBalance) -> Bool
let isSelected: ((ExchangedBalance) -> Bool)?
private let header: Header?
private let listTitle: String?
let excludesUSDF: Bool
private let footer: Footer?

private var balances: [ExchangedBalance] {
session.balances(for: ratesController.rateForBalanceCurrency())
let giveable = session.balances(for: ratesController.rateForBalanceCurrency())
.giveable()
return excludesUSDF ? giveable.excludingUSDF() : giveable
}

/// Creates a picker with `header` drawn above the currency list, and `listTitle` labelling the
/// list beneath it.
/// Creates a picker with `footer` drawn as the last item of the currency list.
///
/// The header spans the list's full width and sets its own margins. It stays on screen when
/// there is nothing giveable to list, since it is a choice of its own rather than one of the
/// list's rows.
/// The footer is a row of its own and sets its own margins. It stays on screen when there is
/// nothing giveable to list, since it is a choice of its own rather than one of the balances.
///
/// - Parameter isSelected: which row draws its checkmark. Defaults to the currency the wallet is
/// denominated in, which is what picking one here changes. Pass a closure when the screen is
/// picking for something else — a group chat's balance requirement, say — so the check follows
/// that choice instead of the wallet's.
/// - Parameter excludesUSDF: drops Dollars from the list, for a screen that picks a community
/// currency. Defaults to showing every giveable balance.
init(
isPresented: Binding<Bool>,
excludesUSDF: Bool = false,
isEnabled: @escaping (ExchangedBalance) -> Bool = { _ in true },
isSelected: ((ExchangedBalance) -> Bool)? = nil,
listTitle: String,
action: @escaping (ExchangedBalance) -> Void,
@ViewBuilder header: () -> Header
@ViewBuilder footer: () -> Footer
) {
self._isPresented = isPresented
self.excludesUSDF = excludesUSDF
self.isEnabled = isEnabled
self.isSelected = isSelected
self.action = action
self.header = header()
self.listTitle = listTitle
self.footer = footer()
}

var body: some View {
Expand All @@ -61,7 +62,7 @@ struct SelectCurrencyScreen<Header: View>: View {

NavigationStack {
Background(color: .backgroundMain) {
if balances.isEmpty && header == nil {
if balances.isEmpty && footer == nil {
Text("No currencies to give")
.font(.appTextMedium)
.foregroundStyle(Color.textSecondary)
Expand All @@ -71,24 +72,6 @@ struct SelectCurrencyScreen<Header: View>: View {
.accessibilityIdentifier("give-picker-empty")
} else {
List {
if let header {
header
.listRowInsets(EdgeInsets())
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)

if let listTitle, !balances.isEmpty {
Text(listTitle)
.font(.default(size: 15, weight: .semibold))
.foregroundStyle(Color.textMain.opacity(0.5))
.padding(.top, 24)
.padding(.bottom, 4)
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20))
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
}
}

Section {
ForEach(balances) { balance in
let enabled = isEnabled(balance)
Expand All @@ -108,6 +91,13 @@ struct SelectCurrencyScreen<Header: View>: View {
}
}
.listRowInsets(EdgeInsets())

if let footer {
footer
.listRowInsets(EdgeInsets())
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
Expand All @@ -124,7 +114,7 @@ struct SelectCurrencyScreen<Header: View>: View {
}
}

extension SelectCurrencyScreen where Header == EmptyView {
extension SelectCurrencyScreen where Footer == EmptyView {

/// Creates a picker listing only the giveable currencies.
///
Expand All @@ -137,11 +127,11 @@ extension SelectCurrencyScreen where Header == EmptyView {
action: @escaping (ExchangedBalance) -> Void
) {
self._isPresented = isPresented
self.excludesUSDF = false
self.isEnabled = isEnabled
self.isSelected = isSelected
self.action = action
self.header = nil
self.listTitle = nil
self.footer = nil
}
}

Expand Down
89 changes: 0 additions & 89 deletions Flipcash/Core/Screens/Main/Tips/New Group/AllCurrenciesCard.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// AnyCommunityCurrencyRow.swift
// Flipcash
//

import SwiftUI
import FlipcashCore
import FlipcashUI

/// What a group's balance requirement is named when it counts any community currency rather than
/// one token.
enum AnyCommunityCurrency {
static let name = "Any Community Currency"
}

/// The currency picker's "Any Community Currency" entry, the last item of its list. Laid out like
/// ``CurrencyBalanceRow`` so its icon and check sit on the same lines as the token rows'.
struct AnyCommunityCurrencyRow: View {

let isSelected: Bool
let action: () -> Void

var body: some View {
Button(action: action) {
HStack(spacing: 8) {
AllCurrenciesIcon(size: 24)

Text(AnyCommunityCurrency.name)
.font(.appBarButton)
.foregroundStyle(Color.textMain)

Spacer()

RadioButton(isSelected: isSelected)
.accessibilityHidden(true)
.padding(.leading, 12)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.listRowBackground(Color.clear)
.padding(.horizontal, 20)
.padding(.vertical, 20)
.accessibilityAddTraits(isSelected ? .isSelected : [])
.accessibilityIdentifier("any-community-currency-row")
}
}

/// The form's tappable currency row: the chosen token, or the any-currency glyph, and a chevron.
struct CommunityCurrencyRow<Icon: View>: View {

let name: String
let action: () -> Void
@ViewBuilder let icon: () -> Icon

var body: some View {
Button(action: action) {
HStack {
HStack(spacing: 8) {
icon()
Text(name)
.font(.appTextMedium)
.foregroundStyle(Color.textMain)
.lineLimit(1)
.truncationMode(.tail)
}
Spacer()
Image(systemName: "chevron.right")
.font(.appTextSmall)
.foregroundStyle(Color.textSecondary)
}
.padding(.horizontal, 16)
.frame(height: 56)
.frame(maxWidth: .infinity)
.background(Color.backgroundRow, in: RoundedRectangle(cornerRadius: Metrics.boxRadius, style: .continuous))
.contentShape(RoundedRectangle(cornerRadius: Metrics.boxRadius, style: .continuous))
}
.buttonStyle(.plain)
}
}

/// The coins glyph on a grey disc that stands in for a token's icon when a requirement counts any
/// community currency (node 10369:999).
struct AllCurrenciesIcon: View {

let size: CGFloat

var body: some View {
Circle()
// White at 15% over the sheet's charcoal is the design's #3B3B3D.
.fill(Color.white.opacity(0.15))
.frame(width: size, height: size)
.overlay {
Image.asset(.coins)
.renderingMode(.template)
.resizable()
.scaledToFit()
.frame(width: size * 0.625, height: size * 0.625)
.foregroundStyle(Color.textMain)
}
}
}
Loading
Loading