Skip to content

Commit

Permalink
Initial (not completed) implementation of CoinPage module in SwiftUI
Browse files Browse the repository at this point in the history
  • Loading branch information
ealymbaev committed Oct 12, 2023
1 parent e5a0366 commit 607aa1f
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 30 deletions.
24 changes: 24 additions & 0 deletions UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
import UIKit
import ThemeKit
import MarketKit
import SwiftUI
import ThemeKit
import UIKit

struct CoinAnalyticsModule {
static func view(fullCoin: FullCoin) -> some View {
CoinAnalyticsView(fullCoin: fullCoin)
}

static func viewController(fullCoin: FullCoin) -> CoinAnalyticsViewController {
let service = CoinAnalyticsService(
fullCoin: fullCoin,
marketKit: App.shared.marketKit,
currencyKit: App.shared.currencyKit,
subscriptionManager: App.shared.subscriptionManager
fullCoin: fullCoin,
marketKit: App.shared.marketKit,
currencyKit: App.shared.currencyKit,
subscriptionManager: App.shared.subscriptionManager
)
let technicalIndicatorService = TechnicalIndicatorService(
coinUid: fullCoin.coin.uid,
currencyKit: App.shared.currencyKit,
marketKit: App.shared.marketKit
coinUid: fullCoin.coin.uid,
currencyKit: App.shared.currencyKit,
marketKit: App.shared.marketKit
)
let coinIndicatorViewItemFactory = CoinIndicatorViewItemFactory()
let viewModel = CoinAnalyticsViewModel(
service: service,
technicalIndicatorService: technicalIndicatorService,
coinIndicatorViewItemFactory: coinIndicatorViewItemFactory
service: service,
technicalIndicatorService: technicalIndicatorService,
coinIndicatorViewItemFactory: coinIndicatorViewItemFactory
)

return CoinAnalyticsViewController(viewModel: viewModel)
}

}

extension CoinAnalyticsModule {

enum Rating: String, CaseIterable {
case excellent
case good
Expand All @@ -48,10 +50,21 @@ extension CoinAnalyticsModule {
switch self {
case .excellent: return .themeGreenD
case .good: return .themeYellowD
case .fair: return UIColor(hex: 0xff7a00)
case .fair: return UIColor(hex: 0xFF7A00)
case .poor: return .themeRedD
}
}
}
}

struct CoinAnalyticsView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController

let fullCoin: FullCoin

func makeUIViewController(context _: Context) -> UIViewController {
CoinAnalyticsModule.viewController(fullCoin: fullCoin)
}

func updateUIViewController(_: UIViewController, context _: Context) {}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import UIKit
import MarketKit
import SwiftUI
import UIKit

struct CoinMarketsModule {
static func view(coin: Coin) -> some View {
CoinMarketsView(coin: coin)
}

static func viewController(coin: Coin) -> CoinMarketsViewController {
let service = CoinMarketsService(
coin: coin,
marketKit: App.shared.marketKit,
currencyKit: App.shared.currencyKit
coin: coin,
marketKit: App.shared.marketKit,
currencyKit: App.shared.currencyKit
)

let viewModel = CoinMarketsViewModel(service: service)
let headerViewModel = MarketSingleSortHeaderViewModel(service: service, decorator: viewModel)

return CoinMarketsViewController(viewModel: viewModel, headerViewModel: headerViewModel, urlManager: UrlManager(inApp: false))
}
}

struct CoinMarketsView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController

let coin: Coin

func makeUIViewController(context _: Context) -> UIViewController {
CoinMarketsModule.viewController(coin: coin)
}

func updateUIViewController(_: UIViewController, context _: Context) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct CoinOverviewView: View {
let chartIndicatorRepository: IChartIndicatorsRepository
let chartPointFetcher: IChartPointFetcher

@State private var hasAppeared = false
@State private var chartIndicatorsShown = false

var body: some View {
Expand Down Expand Up @@ -109,6 +110,9 @@ struct CoinOverviewView: View {
}
}
.onAppear {
guard !hasAppeared else { return }
hasAppeared = true

viewModel.sync()
}
.sheet(isPresented: $chartIndicatorsShown) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import UIKit
import LanguageKit
import ThemeKit
import MarketKit
import SwiftUI
import ThemeKit
import UIKit

struct CoinPageModule {
static func view(fullCoin: FullCoin) -> some View {
let viewModel = CoinPageViewModelNew(fullCoin: fullCoin, favoritesManager: App.shared.favoritesManager)

let overviewView = CoinOverviewModule.view(coinUid: fullCoin.coin.uid)
let analyticsView = CoinAnalyticsModule.view(fullCoin: fullCoin)
let marketsView = CoinMarketsModule.view(coin: fullCoin.coin)

return CoinPageView(
viewModel: viewModel,
overviewView: { overviewView },
analyticsView: { analyticsView.ignoresSafeArea(edges: .bottom) },
marketsView: { marketsView.ignoresSafeArea(edges: .bottom) }
)
}

static func viewController(coinUid: String) -> UIViewController? {
guard let fullCoin = try? App.shared.marketKit.fullCoins(coinUids: [coinUid]).first else {
return nil
}

let service = CoinPageService(
fullCoin: fullCoin,
favoritesManager: App.shared.favoritesManager
fullCoin: fullCoin,
favoritesManager: App.shared.favoritesManager
)

let viewModel = CoinPageViewModel(service: service)
Expand All @@ -23,19 +38,17 @@ struct CoinPageModule {
// let tweetsController = CoinTweetsModule.viewController(fullCoin: fullCoin)

let viewController = CoinPageViewController(
viewModel: viewModel,
overviewController: overviewController,
analyticsController: analyticsController,
marketsController: marketsController
viewModel: viewModel,
overviewController: overviewController,
analyticsController: analyticsController,
marketsController: marketsController
)

return ThemeNavigationController(rootViewController: viewController)
}

}

extension CoinPageModule {

enum Tab: Int, CaseIterable {
case overview
case analytics
Expand All @@ -51,5 +64,4 @@ extension CoinPageModule {
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import SwiftUI

struct CoinPageView<Overview: View, Analytics: View, Markets: View>: View {
@ObservedObject var viewModel: CoinPageViewModelNew

@ViewBuilder let overviewView: Overview
@ViewBuilder let analyticsView: Analytics
@ViewBuilder let marketsView: Markets

@Environment(\.presentationMode) private var presentationMode
@State private var currentTabIndex: Int = Tab.overview.rawValue

var body: some View {
ThemeNavigationView {
ThemeView {
VStack(spacing: 0) {
TabHeaderView(
tabs: Tab.allCases.map { $0.title },
currentTabIndex: $currentTabIndex
)

TabView(selection: $currentTabIndex) {
overviewView.tag(Tab.overview.rawValue)
analyticsView.tag(Tab.analytics.rawValue)
marketsView.tag(Tab.markets.rawValue)
}
.tabViewStyle(.page(indexDisplayMode: .never))
.ignoresSafeArea(edges: .bottom)
}
}
.navigationTitle(viewModel.fullCoin.coin.code)
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("button.close".localized) {
presentationMode.wrappedValue.dismiss()
}
}

ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
viewModel.isFavorite.toggle()
}) {
Image(viewModel.isFavorite ? "filled_star_24" : "star_24")
.renderingMode(.template)
.foregroundColor(viewModel.isFavorite ? .themeJacob : .themeGray)
}
}
}
}
}
}

extension CoinPageView {
enum Tab: Int, CaseIterable {
case overview
case analytics
case markets

var title: String {
switch self {
case .overview: return "coin_page.overview".localized
case .analytics: return "coin_page.analytics".localized
case .markets: return "coin_page.markets".localized
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Combine
import MarketKit

class CoinPageViewModelNew: ObservableObject {
let fullCoin: FullCoin
private let favoritesManager: FavoritesManager

@Published var isFavorite: Bool {
didSet {
if isFavorite {
favoritesManager.add(coinUid: fullCoin.coin.uid)
} else {
favoritesManager.remove(coinUid: fullCoin.coin.uid)
}
}
}

init(fullCoin: FullCoin, favoritesManager: FavoritesManager) {
self.fullCoin = fullCoin
self.favoritesManager = favoritesManager

isFavorite = favoritesManager.isFavorite(coinUid: fullCoin.coin.uid)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import SwiftUI

struct TabButtonStyle: ButtonStyle {
let isActive: Bool

func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(maxWidth: .infinity, maxHeight: .infinity)
.font(.themeSubhead1)
.foregroundColor(isActive ? .themeLeah : .themeGray)
.contentShape(Rectangle())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import SwiftUI

struct TabHeaderView: View {
let tabs: [String]

@Binding var currentTabIndex: Int
@State private var offset: CGFloat = 0

var body: some View {
ZStack(alignment: .bottom) {
Rectangle()
.fill(Color.themeSteel10)
.frame(maxWidth: .infinity)
.frame(height: 1)

ZStack(alignment: .bottom) {
HStack(spacing: 0) {
ForEach(tabs.indices, id: \.self) { index in
Button(action: {
currentTabIndex = index
}) {
Text(tabs[index])
}
.buttonStyle(TabButtonStyle(isActive: index == currentTabIndex))
}
}
.frame(maxWidth: .infinity)

GeometryReader { geo in
RoundedRectangle(cornerRadius: 2, style: .continuous)
.fill(Color.themeJacob)
.frame(height: 4)
.frame(width: geo.size.width / CGFloat(tabs.count))
.offset(x: offset, y: 0)
.onChange(of: currentTabIndex) { index in
withAnimation(.spring().speed(1.5)) {
offset = geo.size.width / CGFloat(tabs.count) * CGFloat(index)
}
}
}
.frame(height: 2)
}
.padding(.horizontal, .margin12)
}
.frame(height: 44)
.clipped()
}
}

0 comments on commit 607aa1f

Please sign in to comment.