Skip to content

Commit

Permalink
Migrate MarketAdvancedSearchResults module to SwiftUI
Browse files Browse the repository at this point in the history
  • Loading branch information
ealymbaev committed Jun 3, 2024
1 parent 5e5206f commit 162811a
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 147 deletions.
38 changes: 12 additions & 26 deletions UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import Kingfisher
import MarketKit
import SwiftUI

struct MarketAdvancedSearchResultsView: View {
@StateObject var viewModel: MarketAdvancedSearchResultsViewModel
@StateObject var watchlistViewModel: WatchlistViewModel
@Binding var isParentPresented: Bool

@State private var sortBySelectorPresented = false
@State private var presentedFullCoin: FullCoin?

init(marketInfos: [MarketInfo], timePeriod: HsTimePeriod, isParentPresented: Binding<Bool>) {
_viewModel = StateObject(wrappedValue: MarketAdvancedSearchResultsViewModel(marketInfos: marketInfos, timePeriod: timePeriod))
_watchlistViewModel = StateObject(wrappedValue: WatchlistViewModel(page: .advancedSearchResults))
_isParentPresented = isParentPresented
}

var body: some View {
ThemeView {
VStack(spacing: 0) {
header()

ThemeList(viewModel.marketInfos, bottomSpacing: .margin16) { marketInfo in
let coin = marketInfo.fullCoin.coin

ClickableRow(action: {
presentedFullCoin = marketInfo.fullCoin
}) {
itemContent(
coin: coin,
marketCap: marketInfo.marketCap,
price: marketInfo.price.flatMap { ValueFormatter.instance.formatFull(currency: viewModel.currency, value: $0) } ?? "n/a".localized,
rank: marketInfo.marketCapRank,
diff: marketInfo.priceChangeValue(timePeriod: viewModel.timePeriod)
)
}
.watchlistSwipeActions(viewModel: watchlistViewModel, coinUid: coin.uid)
}
}
}
.navigationTitle("market.advanced_search_results.title".localized)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("button.close".localized) {
isParentPresented = false
}
}
}
.sheet(item: $presentedFullCoin) { fullCoin in
CoinPageViewNew(coinUid: fullCoin.coin.uid).ignoresSafeArea()
.onFirstAppear { stat(page: .advancedSearchResults, event: .openCoin(coinUid: fullCoin.coin.uid)) }
}
.alert(
isPresented: $sortBySelectorPresented,
title: "market.sort_by.title".localized,
viewItems: viewModel.sortBys.map { .init(text: $0.title, selected: viewModel.sortBy == $0) },
onTap: { index in
guard let index else {
return
}

viewModel.sortBy = viewModel.sortBys[index]
}
)
}

@ViewBuilder private func header() -> some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
Button(action: {
sortBySelectorPresented = true
}) {
Text(viewModel.sortBy.title)
}
.buttonStyle(SecondaryButtonStyle(style: .default, rightAccessory: .dropDown))
}
.padding(.horizontal, .margin16)
.padding(.vertical, .margin8)
}
.alert(
isPresented: $sortBySelectorPresented,
title: "market.sort_by.title".localized,
viewItems: viewModel.sortBys.map { .init(text: $0.title, selected: viewModel.sortBy == $0) },
onTap: { index in
guard let index else {
return
}

viewModel.sortBy = viewModel.sortBys[index]
}
)
}

@ViewBuilder private func itemContent(coin: Coin?, marketCap: Decimal?, price: String, rank: Int?, diff: Decimal?) -> some View {
CoinIconView(coin: coin)

VStack(spacing: 1) {
HStack(spacing: .margin8) {
Text(coin?.code ?? "CODE").textBody()
Spacer()
Text(price).textBody()
}

HStack(spacing: .margin8) {
HStack(spacing: .margin4) {
if let rank {
BadgeViewNew(text: "\(rank)")
}

if let marketCap, let formatted = ValueFormatter.instance.formatShort(currency: viewModel.currency, value: marketCap) {
Text(formatted).textSubhead2()
}
}
Spacer()
DiffText(diff)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Combine
import Foundation
import MarketKit

class MarketAdvancedSearchResultsViewModel: ObservableObject {
private let currencyManager = App.shared.currencyManager
private var cancellables = Set<AnyCancellable>()

private let internalMarketInfos: [MarketInfo]
let timePeriod: HsTimePeriod

@Published var marketInfos: [MarketInfo] = []

var sortBy: MarketModule.SortBy = .highestCap {
didSet {
syncState()
}
}

init(marketInfos: [MarketInfo], timePeriod: HsTimePeriod) {
internalMarketInfos = marketInfos
self.timePeriod = timePeriod

syncState()
}

private func syncState() {
marketInfos = internalMarketInfos.sorted(sortBy: sortBy, timePeriod: timePeriod)
}
}

extension MarketAdvancedSearchResultsViewModel {
var currency: Currency {
currencyManager.baseCurrency
}

var sortBys: [MarketModule.SortBy] {
[.highestCap, .lowestCap, .gainers, .losers]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct MarketAdvancedSearchView: View {
@State var signalsPresented = false
@State var priceChangePresented = false
@State var pricePeriodPresented = false
@State var resultsPresented = false

var body: some View {
ThemeNavigationView {
Expand Down Expand Up @@ -72,7 +73,7 @@ struct MarketAdvancedSearchView: View {
.disabled(true)
case let .loaded(marketInfos):
Button {
// todo
resultsPresented = true
} label: {
Text(marketInfos.isEmpty ? "market.advanced_search.empty_results".localized : "\("market.advanced_search.show_results".localized): \(marketInfos.count)")
}
Expand All @@ -87,6 +88,17 @@ struct MarketAdvancedSearchView: View {
.buttonStyle(PrimaryButtonStyle(style: .gray))
}
}

NavigationLink(
isActive: $resultsPresented,
destination: {
if case let .loaded(marketInfos) = viewModel.state {
MarketAdvancedSearchResultsView(marketInfos: marketInfos, timePeriod: viewModel.priceChangePeriod, isParentPresented: $isPresented)
}
}
) {
EmptyView()
}
}
.navigationTitle("market.advanced_search.title".localized)
.navigationBarTitleDisplayMode(.inline)
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 162811a

Please sign in to comment.