Skip to content

Commit

Permalink
Migrate CoinReports module into SwiftUI
Browse files Browse the repository at this point in the history
  • Loading branch information
ealymbaev committed Jun 14, 2024
1 parent 2528305 commit c2ae9da
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 284 deletions.
36 changes: 12 additions & 24 deletions UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,6 @@ struct CoinAnalyticsView: View {
value: reports
) {
CoinReportsView(coinUid: viewModel.coin.uid)
.navigationTitle("coin_analytics.reports".localized)
.ignoresSafeArea()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Kingfisher
import MarketKit
import SwiftUI

Expand Down

This file was deleted.

This file was deleted.

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

struct CoinReportsView: View {
@StateObject var viewModel: CoinReportsViewModel

@Environment(\.openURL) private var openURL

init(coinUid: String) {
_viewModel = StateObject(wrappedValue: CoinReportsViewModel(coinUid: coinUid))
}

var body: some View {
ThemeView {
switch viewModel.state {
case .loading:
ProgressView()
case let .loaded(reports):
list(reports: reports)
case .failed:
SyncErrorView {
viewModel.onRetry()
}
}
}
.navigationTitle("coin_analytics.reports".localized)
.navigationBarTitleDisplayMode(.inline)
}

@ViewBuilder private func list(reports: [CoinReport]) -> some View {
ScrollView {
LazyVStack(spacing: .margin12) {
ForEach(reports.indices, id: \.self) { index in
let report = reports[index]

ListSection {
ClickableRow(padding: EdgeInsets(top: .margin16, leading: .margin16, bottom: .margin16, trailing: .margin16)) {
if let url = URL(string: report.url) {
openURL(url)
}
} content: {
VStack(alignment: .leading, spacing: .margin12) {
VStack(alignment: .leading, spacing: .margin8) {
Text(report.author).themeCaptionSB()

VStack(alignment: .leading, spacing: .margin6) {
Text(report.title)
.themeHeadline2()
.lineLimit(3)

Text(report.body)
.themeSubhead2()
.lineLimit(2)
}
}

Text(DateHelper.instance.formatMonthYear(from: report.date)).themeMicro(color: .themeGray50)
}
}
}
}
}
.padding(EdgeInsets(top: .margin12, leading: .margin16, bottom: .margin32, trailing: .margin16))
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,76 +1,55 @@
import Combine
import Foundation
import HsExtensions
import MarketKit
import RxCocoa
import RxRelay
import RxSwift

class CoinReportsViewModel {
private let service: CoinReportsService
private let disposeBag = DisposeBag()
class CoinReportsViewModel: ObservableObject {
private let coinUid: String
private let marketKit = App.shared.marketKit
private var tasks = Set<AnyTask>()

private let viewItemsRelay = BehaviorRelay<[ViewItem]?>(value: nil)
private let loadingRelay = BehaviorRelay<Bool>(value: false)
private let syncErrorRelay = BehaviorRelay<Bool>(value: false)
@Published private(set) var state: State = .loading

init(service: CoinReportsService) {
self.service = service
init(coinUid: String) {
self.coinUid = coinUid

subscribe(disposeBag, service.stateObservable) { [weak self] in self?.sync(state: $0) }

sync(state: service.state)
sync()
}

private func sync(state: DataStatus<[CoinReport]>) {
switch state {
case .loading:
viewItemsRelay.accept(nil)
loadingRelay.accept(true)
syncErrorRelay.accept(false)
case let .completed(reports):
viewItemsRelay.accept(reports.map { viewItem(report: $0) })
loadingRelay.accept(false)
syncErrorRelay.accept(false)
case .failed:
viewItemsRelay.accept(nil)
loadingRelay.accept(false)
syncErrorRelay.accept(true)
private func sync() {
tasks = Set()

if case .failed = state {
state = .loading
}
}

private func viewItem(report: CoinReport) -> ViewItem {
ViewItem(
author: report.author,
title: report.title,
body: report.body,
date: DateHelper.instance.formatMonthYear(from: report.date),
url: report.url
)
Task { [weak self, marketKit, coinUid] in
do {
let reports = try await marketKit.coinReports(coinUid: coinUid)

await MainActor.run { [weak self] in
self?.state = .loaded(reports: reports)
}
} catch {
await MainActor.run { [weak self] in
self?.state = .failed
}
}
}
.store(in: &tasks)
}
}

extension CoinReportsViewModel {
var viewItemsDriver: Driver<[ViewItem]?> {
viewItemsRelay.asDriver()
}

var loadingDriver: Driver<Bool> {
loadingRelay.asDriver()
}

var syncErrorDriver: Driver<Bool> {
syncErrorRelay.asDriver()
}

func onTapRetry() {
service.refresh()
func onRetry() {
sync()
}
}

extension CoinReportsViewModel {
struct ViewItem {
let author: String
let title: String
let body: String
let date: String
let url: String
enum State {
case loading
case loaded(reports: [CoinReport])
case failed
}
}

0 comments on commit c2ae9da

Please sign in to comment.