diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index 7d51a055ad..41f18af000 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -12698,7 +12698,7 @@ repositoryURL = "https://github.com/horizontalsystems/MarketKit.Swift"; requirement = { kind = exactVersion; - version = 2.2.8; + version = 2.3.0; }; }; D3604E7728F03B9F0066C366 /* XCRemoteSwiftPackageReference "ModuleKit.Swift" */ = { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/WalletConnectSessionManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/WalletConnectSessionManager.swift index a520f5d386..4c716f4ce5 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/WalletConnectSessionManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/WalletConnectSessionManager.swift @@ -96,15 +96,16 @@ class WalletConnectSessionManager { let activeSessions = storage.sessions(accountId: account.id) guard activeSessions.first(where: { session in session.topic == request.topic }) != nil, - let session = allSessions.first(where: { session in session.topic == request.topic }) else { + let session = allSessions.first(where: { session in session.topic == request.topic }) + else { return } let request = requestHandler.handle(session: session, request: request) switch request { - case .request(let request): sessionRequestReceivedRelay.accept(request) + case let .request(request): sessionRequestReceivedRelay.accept(request) case .handled: () - case .unsuccessful(error: let error): print("Error while parsing request: \(error)") + case let .unsuccessful(error): print("Error while parsing request: \(error?.localizedDescription ?? "nil")") } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsModule.swift index 457fe6c3f6..a2b22e3a6f 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsModule.swift @@ -4,16 +4,17 @@ import ThemeKit import UIKit struct CoinAnalyticsModule { - static func view(fullCoin: FullCoin) -> some View { - CoinAnalyticsView(fullCoin: fullCoin) + static func view(fullCoin: FullCoin, apiTag: String) -> some View { + CoinAnalyticsView(fullCoin: fullCoin, apiTag: apiTag) } - static func viewController(fullCoin: FullCoin) -> CoinAnalyticsViewController { + static func viewController(fullCoin: FullCoin, apiTag: String) -> CoinAnalyticsViewController { let service = CoinAnalyticsService( fullCoin: fullCoin, marketKit: App.shared.marketKit, currencyManager: App.shared.currencyManager, - subscriptionManager: App.shared.subscriptionManager + subscriptionManager: App.shared.subscriptionManager, + apiTag: apiTag ) let technicalIndicatorService = TechnicalIndicatorService( coinUid: fullCoin.coin.uid, @@ -61,9 +62,10 @@ struct CoinAnalyticsView: UIViewControllerRepresentable { typealias UIViewControllerType = UIViewController let fullCoin: FullCoin + let apiTag: String func makeUIViewController(context _: Context) -> UIViewController { - CoinAnalyticsModule.viewController(fullCoin: fullCoin) + CoinAnalyticsModule.viewController(fullCoin: fullCoin, apiTag: apiTag) } func updateUIViewController(_: UIViewController, context _: Context) {} diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsService.swift index 6012a42443..a0e2ad27bb 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsService.swift @@ -1,49 +1,49 @@ import Combine import EvmKit -import MarketKit -import HsToolKit import HsExtensions +import HsToolKit +import MarketKit class CoinAnalyticsService { private let fullCoin: FullCoin private let marketKit: MarketKit.Kit private let currencyManager: CurrencyManager private let subscriptionManager: SubscriptionManager + private let apiTag: String private var tasks = Set() private var cancellables = Set() @PostPublished private(set) var state: State = .loading - init(fullCoin: FullCoin, marketKit: MarketKit.Kit, currencyManager: CurrencyManager, subscriptionManager: SubscriptionManager) { + init(fullCoin: FullCoin, marketKit: MarketKit.Kit, currencyManager: CurrencyManager, subscriptionManager: SubscriptionManager, apiTag: String) { self.fullCoin = fullCoin self.marketKit = marketKit self.currencyManager = currencyManager self.subscriptionManager = subscriptionManager + self.apiTag = apiTag subscriptionManager.$isAuthenticated - .sink { [weak self] isAuthenticated in - if isAuthenticated { - self?.sync() - } + .sink { [weak self] isAuthenticated in + if isAuthenticated { + self?.sync() } - .store(in: &cancellables) + } + .store(in: &cancellables) } private func loadPreview() { - Task { [weak self, marketKit, fullCoin] in + Task { [weak self, marketKit, fullCoin, apiTag] in do { - let analyticsPreview = try await marketKit.analyticsPreview(coinUid: fullCoin.coin.uid) + let analyticsPreview = try await marketKit.analyticsPreview(coinUid: fullCoin.coin.uid, apiTag: apiTag) self?.state = .preview(analyticsPreview: analyticsPreview) } catch { self?.state = .failed(error) } }.store(in: &tasks) } - } extension CoinAnalyticsService { - var currency: Currency { currencyManager.baseCurrency } @@ -55,8 +55,8 @@ extension CoinAnalyticsService { var auditAddresses: [String]? { let addresses = fullCoin.tokens.compactMap { token in switch (token.blockchainType, token.type) { - case (.ethereum, .eip20(let address)): return address - case (.binanceSmartChain, .eip20(let address)): return address + case let (.ethereum, .eip20(address)): return address + case let (.binanceSmartChain, .eip20(address)): return address default: return nil } } @@ -78,36 +78,33 @@ extension CoinAnalyticsService { state = .loading if subscriptionManager.isAuthenticated { - Task { [weak self, subscriptionManager, marketKit, fullCoin, currency] in + Task { [weak self, subscriptionManager, marketKit, fullCoin, currency, apiTag] in try await subscriptionManager.fetch( - request: { - try await marketKit.analytics(coinUid: fullCoin.coin.uid, currencyCode: currency.code) - }, - onSuccess: { [weak self] analytics in - self?.state = .success(analytics: analytics) - }, - onInvalidAuthToken: { [weak self] in - self?.loadPreview() - }, - onFailure: { [weak self] error in - self?.state = .failed(error) - } + request: { + try await marketKit.analytics(coinUid: fullCoin.coin.uid, currencyCode: currency.code, apiTag: apiTag) + }, + onSuccess: { [weak self] analytics in + self?.state = .success(analytics: analytics) + }, + onInvalidAuthToken: { [weak self] in + self?.loadPreview() + }, + onFailure: { [weak self] error in + self?.state = .failed(error) + } ) }.store(in: &tasks) } else { loadPreview() } } - } extension CoinAnalyticsService { - enum State { case loading case failed(Error) case preview(analyticsPreview: AnalyticsPreview) case success(analytics: Analytics) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsViewController.swift index 545af2e911..40641f7579 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Analytics/CoinAnalyticsViewController.swift @@ -1,13 +1,13 @@ +import Chart import Combine -import UIKit -import RxSwift -import ThemeKit -import SectionsTableView -import SnapKit import ComponentKit import HUD import MarketKit -import Chart +import RxSwift +import SectionsTableView +import SnapKit +import ThemeKit +import UIKit class CoinAnalyticsViewController: ThemeViewController { private static let placeholderText = "•••" @@ -43,7 +43,8 @@ class CoinAnalyticsViewController: ThemeViewController { super.init() } - required init?(coder aDecoder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -108,19 +109,19 @@ class CoinAnalyticsViewController: ThemeViewController { } viewModel.indicatorViewItemsPublisher - .receive(on: DispatchQueue.main) - .sink { [weak self] in - self?.indicatorViewItem = $0 - self?.tableView.reload() - } - .store(in: &cancellables) + .receive(on: DispatchQueue.main) + .sink { [weak self] in + self?.indicatorViewItem = $0 + self?.tableView.reload() + } + .store(in: &cancellables) viewModel.subscriptionInfoPublisher - .receive(on: DispatchQueue.main) - .sink { [weak self] in - self?.openSubscriptionInfo() - } - .store(in: &cancellables) + .receive(on: DispatchQueue.main) + .sink { [weak self] in + self?.openSubscriptionInfo() + } + .store(in: &cancellables) viewModel.onLoad() } @@ -163,7 +164,7 @@ class CoinAnalyticsViewController: ThemeViewController { .listItem(text: "coin_analytics.cex_volume.info1".localized), .listItem(text: "coin_analytics.cex_volume.info2".localized), .listItem(text: "coin_analytics.cex_volume.info3".localized), - .listItem(text: "coin_analytics.cex_volume.info4".localized) + .listItem(text: "coin_analytics.cex_volume.info4".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -178,7 +179,7 @@ class CoinAnalyticsViewController: ThemeViewController { .listItem(text: "coin_analytics.dex_volume.info4".localized), .text(text: "coin_analytics.dex_volume.tracked_dexes".localized), .listItem(text: "coin_analytics.dex_volume.tracked_dexes.info1".localized), - .listItem(text: "coin_analytics.dex_volume.tracked_dexes.info2".localized) + .listItem(text: "coin_analytics.dex_volume.tracked_dexes.info2".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -192,7 +193,7 @@ class CoinAnalyticsViewController: ThemeViewController { .listItem(text: "coin_analytics.dex_liquidity.info3".localized), .text(text: "coin_analytics.dex_liquidity.tracked_dexes".localized), .listItem(text: "coin_analytics.dex_liquidity.tracked_dexes.info1".localized), - .listItem(text: "coin_analytics.dex_liquidity.tracked_dexes.info2".localized) + .listItem(text: "coin_analytics.dex_liquidity.tracked_dexes.info2".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -205,7 +206,7 @@ class CoinAnalyticsViewController: ThemeViewController { .listItem(text: "coin_analytics.active_addresses.info2".localized), .listItem(text: "coin_analytics.active_addresses.info3".localized), .listItem(text: "coin_analytics.active_addresses.info4".localized), - .listItem(text: "coin_analytics.active_addresses.info5".localized) + .listItem(text: "coin_analytics.active_addresses.info5".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -218,7 +219,7 @@ class CoinAnalyticsViewController: ThemeViewController { .listItem(text: "coin_analytics.transaction_count.info2".localized), .listItem(text: "coin_analytics.transaction_count.info3".localized), .listItem(text: "coin_analytics.transaction_count.info4".localized), - .listItem(text: "coin_analytics.transaction_count.info5".localized) + .listItem(text: "coin_analytics.transaction_count.info5".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -229,7 +230,7 @@ class CoinAnalyticsViewController: ThemeViewController { .header1(text: "coin_analytics.holders".localized), .listItem(text: "coin_analytics.holders.info1".localized), .listItem(text: "coin_analytics.holders.info2".localized), - .text(text: "coin_analytics.holders.tracked_blockchains".localized) + .text(text: "coin_analytics.holders.tracked_blockchains".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -242,7 +243,7 @@ class CoinAnalyticsViewController: ThemeViewController { .listItem(text: "coin_analytics.project_tvl.info2".localized), .listItem(text: "coin_analytics.project_tvl.info3".localized), .listItem(text: "coin_analytics.project_tvl.info4".localized), - .listItem(text: "coin_analytics.project_tvl.info5".localized) + .listItem(text: "coin_analytics.project_tvl.info5".localized), ]) parentNavigationController?.present(viewController, animated: true) @@ -250,14 +251,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openCexVolumeScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.cex_volume".localized, - description: "coin_analytics.overall_score.cex_volume".localized, - scores: [ - .excellent: "> \(formatUsd(value: 10, number: "number.million"))", - .good: "> \(formatUsd(value: 5, number: "number.million"))", - .fair: "> \(formatUsd(value: 1, number: "number.million"))", - .poor: "< \(formatUsd(value: 1, number: "number.million"))", - ] + title: "coin_analytics.cex_volume".localized, + description: "coin_analytics.overall_score.cex_volume".localized, + scores: [ + .excellent: "> \(formatUsd(value: 10, number: "number.million"))", + .good: "> \(formatUsd(value: 5, number: "number.million"))", + .fair: "> \(formatUsd(value: 1, number: "number.million"))", + .poor: "< \(formatUsd(value: 1, number: "number.million"))", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -265,14 +266,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openDexVolumeScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.dex_volume".localized, - description: "coin_analytics.overall_score.dex_volume".localized, - scores: [ - .excellent: "> \(formatUsd(value: 1, number: "number.million"))", - .good: "> \(formatUsd(value: 500, number: "number.thousand"))", - .fair: "> \(formatUsd(value: 100, number: "number.thousand"))", - .poor: "< \(formatUsd(value: 100, number: "number.thousand"))", - ] + title: "coin_analytics.dex_volume".localized, + description: "coin_analytics.overall_score.dex_volume".localized, + scores: [ + .excellent: "> \(formatUsd(value: 1, number: "number.million"))", + .good: "> \(formatUsd(value: 500, number: "number.thousand"))", + .fair: "> \(formatUsd(value: 100, number: "number.thousand"))", + .poor: "< \(formatUsd(value: 100, number: "number.thousand"))", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -280,14 +281,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openDexLiquidityScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.dex_liquidity".localized, - description: "coin_analytics.overall_score.dex_liquidity".localized, - scores: [ - .excellent: "> \(formatUsd(value: 2, number: "number.million"))", - .good: "> \(formatUsd(value: 1, number: "number.million"))", - .fair: "> \(formatUsd(value: 500, number: "number.thousand"))", - .poor: "< \(formatUsd(value: 500, number: "number.thousand"))", - ] + title: "coin_analytics.dex_liquidity".localized, + description: "coin_analytics.overall_score.dex_liquidity".localized, + scores: [ + .excellent: "> \(formatUsd(value: 2, number: "number.million"))", + .good: "> \(formatUsd(value: 1, number: "number.million"))", + .fair: "> \(formatUsd(value: 500, number: "number.thousand"))", + .poor: "< \(formatUsd(value: 500, number: "number.thousand"))", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -295,14 +296,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openAddressesScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.active_addresses".localized, - description: "coin_analytics.overall_score.active_addresses".localized, - scores: [ - .excellent: "> 500", - .good: "> 200", - .fair: "> 100", - .poor: "< 100", - ] + title: "coin_analytics.active_addresses".localized, + description: "coin_analytics.overall_score.active_addresses".localized, + scores: [ + .excellent: "> 500", + .good: "> 200", + .fair: "> 100", + .poor: "< 100", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -310,14 +311,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openTvlScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.project_tvl".localized, - description: "coin_analytics.overall_score.project_tvl".localized, - scores: [ - .excellent: "> \(formatUsd(value: 200, number: "number.million"))", - .good: "> \(formatUsd(value: 100, number: "number.million"))", - .fair: "> \(formatUsd(value: 50, number: "number.million"))", - .poor: "< \(formatUsd(value: 50, number: "number.million"))", - ] + title: "coin_analytics.project_tvl".localized, + description: "coin_analytics.overall_score.project_tvl".localized, + scores: [ + .excellent: "> \(formatUsd(value: 200, number: "number.million"))", + .good: "> \(formatUsd(value: 100, number: "number.million"))", + .fair: "> \(formatUsd(value: 50, number: "number.million"))", + .poor: "< \(formatUsd(value: 50, number: "number.million"))", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -325,14 +326,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openTransactionCountScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.transaction_count".localized, - description: "coin_analytics.overall_score.transaction_count".localized, - scores: [ - .excellent: "> \("number.thousand".localized("10"))", - .good: "> \("number.thousand".localized("5"))", - .fair: "> \("number.thousand".localized("1"))", - .poor: "< \("number.thousand".localized("1"))", - ] + title: "coin_analytics.transaction_count".localized, + description: "coin_analytics.overall_score.transaction_count".localized, + scores: [ + .excellent: "> \("number.thousand".localized("10"))", + .good: "> \("number.thousand".localized("5"))", + .fair: "> \("number.thousand".localized("1"))", + .poor: "< \("number.thousand".localized("1"))", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -340,14 +341,14 @@ class CoinAnalyticsViewController: ThemeViewController { private func openHoldersScoreInfo() { let viewController = CoinAnalyticsRatingScaleViewController( - title: "coin_analytics.holders".localized, - description: "coin_analytics.overall_score.holders".localized, - scores: [ - .excellent: "> \("number.thousand".localized("100"))", - .good: "> \("number.thousand".localized("50"))", - .fair: "> \("number.thousand".localized("30"))", - .poor: "< \("number.thousand".localized("30"))", - ] + title: "coin_analytics.holders".localized, + description: "coin_analytics.overall_score.holders".localized, + scores: [ + .excellent: "> \("number.thousand".localized("100"))", + .good: "> \("number.thousand".localized("50"))", + .fair: "> \("number.thousand".localized("30"))", + .poor: "< \("number.thousand".localized("30"))", + ] ) parentNavigationController?.present(ThemeNavigationController(rootViewController: viewController), animated: true) @@ -359,7 +360,7 @@ class CoinAnalyticsViewController: ThemeViewController { } private func openTvlRank() { - let viewController = MarketGlobalMetricModule.tvlInDefiViewController() + let viewController = MarketGlobalMetricModule.tvlInDefiViewController(apiTag: "coin_analytics") parentNavigationController?.pushViewController(viewController, animated: true) } @@ -395,11 +396,11 @@ class CoinAnalyticsViewController: ThemeViewController { private func openPeriodSelect() { let viewController = SelectorModule.bottomSingleSelectorViewController( - title: "coin_analytics.period.select_title".localized, - viewItems: viewModel.periodViewItems, - onSelect: { [weak self] index in - self?.viewModel.onSelectPeriod(index: index) - } + title: "coin_analytics.period.select_title".localized, + viewItems: viewModel.periodViewItems, + onSelect: { [weak self] index in + self?.viewModel.onSelectPeriod(index: index) + } ) present(viewController, animated: true) @@ -422,7 +423,7 @@ class CoinAnalyticsViewController: ThemeViewController { private func placeholderChartData() -> ChartData { var chartItems = [ChartItem]() - for i in 0..<8 { + for i in 0 ..< 8 { let baseTimeStamp = TimeInterval(i) * 100 let baseValue = Decimal(i) * 2 @@ -430,22 +431,20 @@ class CoinAnalyticsViewController: ThemeViewController { ChartItem(timestamp: baseTimeStamp).added(name: ChartData.rate, value: baseValue + 2), ChartItem(timestamp: baseTimeStamp + 25).added(name: ChartData.rate, value: baseValue + 6), ChartItem(timestamp: baseTimeStamp + 50).added(name: ChartData.rate, value: baseValue), - ChartItem(timestamp: baseTimeStamp + 75).added(name: ChartData.rate, value: baseValue + 9) + ChartItem(timestamp: baseTimeStamp + 75).added(name: ChartData.rate, value: baseValue + 9), ]) } chartItems.append( - ChartItem(timestamp: 800).added(name: ChartData.rate, value: 16) + ChartItem(timestamp: 800).added(name: ChartData.rate, value: 16) ) return ChartData(items: chartItems, startWindow: 0, endWindow: 800) } - } extension CoinAnalyticsViewController: SectionsDataSource { - - private func chartRow(id: String, title: String, valueInfo: String, chartCurveType: ChartConfiguration.CurveType, viewItem: Previewable, isLast: Bool, infoAction: @escaping () -> (), action: @escaping () -> ()) -> RowProtocol { + private func chartRow(id: String, title: String, valueInfo: String, chartCurveType: ChartConfiguration.CurveType, viewItem: Previewable, isLast: Bool, infoAction: @escaping () -> Void, action: @escaping () -> Void) -> RowProtocol { let value: String let chartData: ChartData let chartTrend: MovementTrend @@ -455,7 +454,7 @@ extension CoinAnalyticsViewController: SectionsDataSource { value = Self.placeholderText chartData = placeholderChartData() chartTrend = .ignored - case .regular(let viewItem): + case let .regular(viewItem): value = viewItem.value chartData = viewItem.chartData @@ -466,148 +465,148 @@ extension CoinAnalyticsViewController: SectionsDataSource { } return Row( - id: id, - height: MarketWideCardCell.height(), - autoDeselect: true, - bind: { cell, _ in - cell.set(backgroundStyle: .lawrence, isFirst: true, isLast: isLast) - cell.selectionStyle = viewItem.isPreview ? .none : .default - - cell.bind( - title: title, - value: value, - valueInfo: viewItem.isPreview ? nil : valueInfo, - chartData: chartData, - chartTrend: chartTrend, - chartCurveType: chartCurveType, - onTapInfo: infoAction - ) - }, - action: viewItem.isPreview ? nil : { _ in action() } + id: id, + height: MarketWideCardCell.height(), + autoDeselect: true, + bind: { cell, _ in + cell.set(backgroundStyle: .lawrence, isFirst: true, isLast: isLast) + cell.selectionStyle = viewItem.isPreview ? .none : .default + + cell.bind( + title: title, + value: value, + valueInfo: viewItem.isPreview ? nil : valueInfo, + chartData: chartData, + chartTrend: chartTrend, + chartCurveType: chartCurveType, + onTapInfo: infoAction + ) + }, + action: viewItem.isPreview ? nil : { _ in action() } ) } - private func previewableRow(id: String, title: String, value: Previewable? = nil, accessoryType: CellBuilderNew.CellElement.AccessoryType = .none, isFirst: Bool = false, isLast: Bool = false, action: Previewable<() -> ()>? = nil) -> RowProtocol { + private func previewableRow(id: String, title: String, value: Previewable? = nil, accessoryType: CellBuilderNew.CellElement.AccessoryType = .none, isFirst: Bool = false, isLast: Bool = false, action: Previewable<() -> Void>? = nil) -> RowProtocol { var rowValue: String? - var rowAction: (() -> ())? + var rowAction: (() -> Void)? if let value { switch value { case .preview: rowValue = Self.placeholderText - case .regular(let value): rowValue = value + case let .regular(value): rowValue = value } } if let action { switch action { case .preview: () - case .regular(let action): rowAction = action + case let .regular(action): rowAction = action } } return tableView.universalRow48( - id: id, - title: .subhead2(title), - value: rowValue.map { .subhead1($0) }, - accessoryType: accessoryType, - hash: rowValue, - autoDeselect: true, - isFirst: isFirst, - isLast: isLast, - action: rowAction + id: id, + title: .subhead2(title), + value: rowValue.map { .subhead1($0) }, + accessoryType: accessoryType, + hash: rowValue, + autoDeselect: true, + isFirst: isFirst, + isLast: isLast, + action: rowAction ) } private func indicatorSection(viewItem: CoinAnalyticsViewModel.IndicatorViewItem) -> SectionProtocol { let disableInfo = viewItem.loading || viewItem.error || viewItem.viewItems.isEmpty return Section( - id: "indicator-section", - headerState: .margin(height: 12), - rows: [ - Row(id: "indicator-advices-row", - height: IndicatorAdviceCell.height, - autoDeselect: false, - bind: { [weak self] cell, _ in - cell.set(backgroundStyle: .lawrence, isFirst: true) - cell.set(loading: viewItem.loading) - - if viewItem.error { - cell.setEmpty(value: "n/a".localized) - return - } - - if viewItem.viewItems.isEmpty { - cell.setEmpty(value: Self.placeholderText) - } else { - cell.set(viewItems: viewItem.viewItems) - } - - cell.onTapInfo = { [weak self] in - self?.openTechnicalIndicatorInfo() - } - } - ), - tableView.universalRow48( - id: "period", - title: .subhead2("coin_analytics.period".localized, color: .themeGray), - value: .subhead2(viewModel.period, color: viewItem.switchEnabled ? .themeLeah : .themeGray), - accessoryType: .dropdown, - autoDeselect: true, - action: viewItem.switchEnabled ? { [weak self] in - self?.openPeriodSelect() - } : nil - ), - tableView.universalRow48( - id: "details", - title: .subhead2("coin_analytics.details".localized, color: disableInfo ? .themeGray50 : .themeGray), - accessoryType: .disclosure, - autoDeselect: true, - isLast: true, - action: !disableInfo ? { [weak self] in - self?.openDetailAdvices() - } : nil - ), - ]) + id: "indicator-section", + headerState: .margin(height: 12), + rows: [ + Row(id: "indicator-advices-row", + height: IndicatorAdviceCell.height, + autoDeselect: false, + bind: { [weak self] cell, _ in + cell.set(backgroundStyle: .lawrence, isFirst: true) + cell.set(loading: viewItem.loading) + + if viewItem.error { + cell.setEmpty(value: "n/a".localized) + return + } + + if viewItem.viewItems.isEmpty { + cell.setEmpty(value: Self.placeholderText) + } else { + cell.set(viewItems: viewItem.viewItems) + } + + cell.onTapInfo = { [weak self] in + self?.openTechnicalIndicatorInfo() + } + }), + tableView.universalRow48( + id: "period", + title: .subhead2("coin_analytics.period".localized, color: .themeGray), + value: .subhead2(viewModel.period, color: viewItem.switchEnabled ? .themeLeah : .themeGray), + accessoryType: .dropdown, + autoDeselect: true, + action: viewItem.switchEnabled ? { [weak self] in + self?.openPeriodSelect() + } : nil + ), + tableView.universalRow48( + id: "details", + title: .subhead2("coin_analytics.details".localized, color: disableInfo ? .themeGray50 : .themeGray), + accessoryType: .disclosure, + autoDeselect: true, + isLast: true, + action: !disableInfo ? { [weak self] in + self?.openDetailAdvices() + } : nil + ), + ] + ) } - private func ratingRow(id: String, rating: Previewable, isFirst: Bool = false, isLast: Bool = false, action: @escaping () -> ()) -> RowProtocol { + private func ratingRow(id: String, rating: Previewable, isFirst: Bool = false, isLast: Bool = false, action: @escaping () -> Void) -> RowProtocol { let titleElements: [CellBuilderNew.CellElement] = [ .textElement(text: .subhead2("coin_analytics.overall_score".localized), parameters: .highHugging), .margin8, - .imageElement(image: .local(UIImage(named: "circle_information_20")?.withTintColor(.themeGray)), size: .image20) + .imageElement(image: .local(UIImage(named: "circle_information_20")?.withTintColor(.themeGray)), size: .image20), ] switch rating { case .preview: return CellBuilderNew.row( - rootElement: .hStack(titleElements + [ - .textElement(text: .subhead1(Self.placeholderText), parameters: .rightAlignment) - ]), - tableView: tableView, - id: id, - height: .heightCell48, - autoDeselect: true, - bind: { cell in - cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) - }, - action: action + rootElement: .hStack(titleElements + [ + .textElement(text: .subhead1(Self.placeholderText), parameters: .rightAlignment), + ]), + tableView: tableView, + id: id, + height: .heightCell48, + autoDeselect: true, + bind: { cell in + cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) + }, + action: action ) - case .regular(let rating): + case let .regular(rating): return CellBuilderNew.row( - rootElement: .hStack(titleElements + [ - .textElement(text: .subhead1(rating.title.uppercased(), color: rating.color), parameters: .rightAlignment), - .margin8, - .imageElement(image: .local(rating.image), size: .image24) - ]), - tableView: tableView, - id: id, - hash: rating.title, - height: .heightCell48, - autoDeselect: true, - bind: { cell in - cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) - }, - action: action + rootElement: .hStack(titleElements + [ + .textElement(text: .subhead1(rating.title.uppercased(), color: rating.color), parameters: .rightAlignment), + .margin8, + .imageElement(image: .local(rating.image), size: .image24), + ]), + tableView: tableView, + id: id, + hash: rating.title, + height: .heightCell48, + autoDeselect: true, + bind: { cell in + cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) + }, + action: action ) } } @@ -618,53 +617,53 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ chartRow( - id: "cex-volume", - title: "coin_analytics.cex_volume".localized, - valueInfo: "coin_analytics.last_30d".localized, - chartCurveType: .bars, - viewItem: viewItem.chart, - isLast: itemCount == 1, - infoAction: { [weak self] in - self?.openCexVolumeInfo() - }, - action: { [weak self] in - self?.openProDataChart(type: .cexVolume) - } - ) + id: "cex-volume", + title: "coin_analytics.cex_volume".localized, + valueInfo: "coin_analytics.last_30d".localized, + chartCurveType: .bars, + viewItem: viewItem.chart, + isLast: itemCount == 1, + infoAction: { [weak self] in + self?.openCexVolumeInfo() + }, + action: { [weak self] in + self?.openProDataChart(type: .cexVolume) + } + ), ] if let rating = viewItem.rating { rows.append( - ratingRow( - id: "cex-volume-rating", - rating: rating, - isLast: rows.count + 1 == itemCount, - action: { [weak self] in - self?.openCexVolumeScoreInfo() - } - ) + ratingRow( + id: "cex-volume-rating", + rating: rating, + isLast: rows.count + 1 == itemCount, + action: { [weak self] in + self?.openCexVolumeScoreInfo() + } + ) ) } if let rank = viewItem.rank { rows.append( - previewableRow( - id: "cex-volume-rank", - title: "coin_analytics.30_day_rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: rows.count + 1 == itemCount, - action: rank.previewableValue { _ in { [weak self] in - self?.openRanks(type: .cexVolume) - }} - ) + previewableRow( + id: "cex-volume-rank", + title: "coin_analytics.30_day_rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: rows.count + 1 == itemCount, + action: rank.previewableValue { _ in { [weak self] in + self?.openRanks(type: .cexVolume) + }} + ) ) } return Section( - id: "cex-volume", - headerState: .margin(height: .margin12), - rows: rows + id: "cex-volume", + headerState: .margin(height: .margin12), + rows: rows ) } @@ -674,53 +673,53 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ chartRow( - id: "dex-volume", - title: "coin_analytics.dex_volume".localized, - valueInfo: "coin_analytics.last_30d".localized, - chartCurveType: .bars, - viewItem: viewItem.chart, - isLast: itemCount == 1, - infoAction: { [weak self] in - self?.openDexVolumeInfo() - }, - action: { [weak self] in - self?.openProDataChart(type: .dexVolume) - } - ) + id: "dex-volume", + title: "coin_analytics.dex_volume".localized, + valueInfo: "coin_analytics.last_30d".localized, + chartCurveType: .bars, + viewItem: viewItem.chart, + isLast: itemCount == 1, + infoAction: { [weak self] in + self?.openDexVolumeInfo() + }, + action: { [weak self] in + self?.openProDataChart(type: .dexVolume) + } + ), ] if let rating = viewItem.rating { rows.append( - ratingRow( - id: "dex-volume-rating", - rating: rating, - isLast: rows.count + 1 == itemCount, - action: { [weak self] in - self?.openDexVolumeScoreInfo() - } - ) + ratingRow( + id: "dex-volume-rating", + rating: rating, + isLast: rows.count + 1 == itemCount, + action: { [weak self] in + self?.openDexVolumeScoreInfo() + } + ) ) } if let rank = viewItem.rank { rows.append( - previewableRow( - id: "dex-volume-rank", - title: "coin_analytics.30_day_rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: rows.count + 1 == itemCount, - action: rank.previewableValue { _ in { [weak self] in - self?.openRanks(type: .dexVolume) - }} - ) + previewableRow( + id: "dex-volume-rank", + title: "coin_analytics.30_day_rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: rows.count + 1 == itemCount, + action: rank.previewableValue { _ in { [weak self] in + self?.openRanks(type: .dexVolume) + }} + ) ) } return Section( - id: "dex-volume", - headerState: .margin(height: .margin12), - rows: rows + id: "dex-volume", + headerState: .margin(height: .margin12), + rows: rows ) } @@ -730,53 +729,53 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ chartRow( - id: "dex-liquidity", - title: "coin_analytics.dex_liquidity".localized, - valueInfo: "coin_analytics.current".localized, - chartCurveType: .line, - viewItem: viewItem.chart, - isLast: itemCount == 1, - infoAction: { [weak self] in - self?.openDexLiquidityInfo() - }, - action: { [weak self] in - self?.openProDataChart(type: .dexLiquidity) - } - ) + id: "dex-liquidity", + title: "coin_analytics.dex_liquidity".localized, + valueInfo: "coin_analytics.current".localized, + chartCurveType: .line, + viewItem: viewItem.chart, + isLast: itemCount == 1, + infoAction: { [weak self] in + self?.openDexLiquidityInfo() + }, + action: { [weak self] in + self?.openProDataChart(type: .dexLiquidity) + } + ), ] if let rating = viewItem.rating { rows.append( - ratingRow( - id: "dex-liquidity-rating", - rating: rating, - isLast: rows.count + 1 == itemCount, - action: { [weak self] in - self?.openDexLiquidityScoreInfo() - } - ) + ratingRow( + id: "dex-liquidity-rating", + rating: rating, + isLast: rows.count + 1 == itemCount, + action: { [weak self] in + self?.openDexLiquidityScoreInfo() + } + ) ) } if let rank = viewItem.rank { rows.append( - previewableRow( - id: "dex-liquidity-rank", - title: "coin_analytics.rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: rows.count + 1 == itemCount, - action: rank.previewableValue { _ in { [weak self] in - self?.openRanks(type: .dexLiquidity) - }} - ) + previewableRow( + id: "dex-liquidity-rank", + title: "coin_analytics.rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: rows.count + 1 == itemCount, + action: rank.previewableValue { _ in { [weak self] in + self?.openRanks(type: .dexLiquidity) + }} + ) ) } return Section( - id: "dex-liquidity", - headerState: .margin(height: .margin12), - rows: rows + id: "dex-liquidity", + headerState: .margin(height: .margin12), + rows: rows ) } @@ -786,64 +785,64 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ chartRow( - id: "addresses", - title: "coin_analytics.active_addresses".localized, - valueInfo: "coin_analytics.current".localized, - chartCurveType: .line, - viewItem: viewItem.chart, - isLast: itemCount == 1, - infoAction: { [weak self] in - self?.openAddressesInfo() - }, - action: { [weak self] in - self?.openProDataChart(type: .activeAddresses) - } - ) + id: "addresses", + title: "coin_analytics.active_addresses".localized, + valueInfo: "coin_analytics.current".localized, + chartCurveType: .line, + viewItem: viewItem.chart, + isLast: itemCount == 1, + infoAction: { [weak self] in + self?.openAddressesInfo() + }, + action: { [weak self] in + self?.openProDataChart(type: .activeAddresses) + } + ), ] if let rating = viewItem.rating { rows.append( - ratingRow( - id: "addresses-rating", - rating: rating, - isLast: rows.count + 1 == itemCount, - action: { [weak self] in - self?.openAddressesScoreInfo() - } - ) + ratingRow( + id: "addresses-rating", + rating: rating, + isLast: rows.count + 1 == itemCount, + action: { [weak self] in + self?.openAddressesScoreInfo() + } + ) ) } if let count30d = viewItem.count30d { rows.append( - previewableRow( - id: "addresses-count-30d", - title: "coin_analytics.active_addresses.30_day_unique_addresses".localized, - value: count30d, - isLast: rows.count + 1 == itemCount - ) + previewableRow( + id: "addresses-count-30d", + title: "coin_analytics.active_addresses.30_day_unique_addresses".localized, + value: count30d, + isLast: rows.count + 1 == itemCount + ) ) } if let rank = viewItem.rank { rows.append( - previewableRow( - id: "addresses-rank", - title: "coin_analytics.30_day_rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: rows.count + 1 == itemCount, - action: rank.previewableValue { _ in { [weak self] in - self?.openRanks(type: .address) - }} - ) + previewableRow( + id: "addresses-rank", + title: "coin_analytics.30_day_rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: rows.count + 1 == itemCount, + action: rank.previewableValue { _ in { [weak self] in + self?.openRanks(type: .address) + }} + ) ) } return Section( - id: "addresses", - headerState: .margin(height: .margin12), - rows: rows + id: "addresses", + headerState: .margin(height: .margin12), + rows: rows ) } @@ -853,64 +852,64 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ chartRow( - id: "tx-count", - title: "coin_analytics.transaction_count".localized, - valueInfo: "coin_analytics.last_30d".localized, - chartCurveType: .bars, - viewItem: viewItem.chart, - isLast: itemCount == 1, - infoAction: { [weak self] in - self?.openTransactionCountInfo() - }, - action: { [weak self] in - self?.openProDataChart(type: .txCount) - } - ) + id: "tx-count", + title: "coin_analytics.transaction_count".localized, + valueInfo: "coin_analytics.last_30d".localized, + chartCurveType: .bars, + viewItem: viewItem.chart, + isLast: itemCount == 1, + infoAction: { [weak self] in + self?.openTransactionCountInfo() + }, + action: { [weak self] in + self?.openProDataChart(type: .txCount) + } + ), ] if let rating = viewItem.rating { rows.append( - ratingRow( - id: "tx-count-rating", - rating: rating, - isLast: rows.count + 1 == itemCount, - action: { [weak self] in - self?.openTransactionCountScoreInfo() - } - ) + ratingRow( + id: "tx-count-rating", + rating: rating, + isLast: rows.count + 1 == itemCount, + action: { [weak self] in + self?.openTransactionCountScoreInfo() + } + ) ) } if let volume = viewItem.volume { rows.append( - previewableRow( - id: "tx-volume", - title: "coin_analytics.30_day_volume".localized, - value: volume, - isLast: rows.count + 1 == itemCount - ) + previewableRow( + id: "tx-volume", + title: "coin_analytics.30_day_volume".localized, + value: volume, + isLast: rows.count + 1 == itemCount + ) ) } if let rank = viewItem.rank { rows.append( - previewableRow( - id: "tx-count-rank", - title: "coin_analytics.30_day_rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: rows.count + 1 == itemCount, - action: rank.previewableValue { _ in { [weak self] in - self?.openRanks(type: .txCount) - }} - ) + previewableRow( + id: "tx-count-rank", + title: "coin_analytics.30_day_rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: rows.count + 1 == itemCount, + action: rank.previewableValue { _ in { [weak self] in + self?.openRanks(type: .txCount) + }} + ) ) } return Section( - id: "tx-count", - headerState: .margin(height: .margin12), - rows: rows + id: "tx-count", + headerState: .margin(height: .margin12), + rows: rows ) } @@ -919,7 +918,7 @@ extension CoinAnalyticsViewController: SectionsDataSource { let imageUrl: String? let name: String let value: String? - let action: (() -> ())? + let action: (() -> Void)? } let value: String? @@ -937,18 +936,18 @@ extension CoinAnalyticsViewController: SectionsDataSource { chartItems = [ (0.5, UIColor.themeGray.withAlphaComponent(0.8)), (0.35, UIColor.themeGray.withAlphaComponent(0.6)), - (0.15, UIColor.themeGray.withAlphaComponent(0.4)) + (0.15, UIColor.themeGray.withAlphaComponent(0.4)), ] - case .regular(let viewItem): + case let .regular(viewItem): value = viewItem.value blockchains = viewItem.holderViewItems.map { viewItem in Blockchain( - imageUrl: viewItem.imageUrl, - name: viewItem.name, - value: viewItem.value, - action: { [weak self] in - self?.openMajorHolders(blockchain: viewItem.blockchain) - } + imageUrl: viewItem.imageUrl, + name: viewItem.name, + value: viewItem.value, + action: { [weak self] in + self?.openMajorHolders(blockchain: viewItem.blockchain) + } ) } chartItems = viewItem.holderViewItems.map { ($0.percent, $0.blockchain.type.brandColor) } @@ -956,78 +955,78 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ Row( - id: "holders", - height: MarketWideCardCell.height(hasChart: false, bottomMargin: .margin12), - bind: { [weak self] cell, _ in - cell.set(backgroundStyle: .lawrence, isFirst: true) - cell.selectionStyle = .none - - cell.bind( - title: "coin_analytics.holders".localized, - value: value, - valueInfo: viewItem.isPreview ? nil : "coin_analytics.current".localized, - onTapInfo: { - self?.openHoldersInfo() - } - ) - } + id: "holders", + height: MarketWideCardCell.height(hasChart: false, bottomMargin: .margin12), + bind: { [weak self] cell, _ in + cell.set(backgroundStyle: .lawrence, isFirst: true) + cell.selectionStyle = .none + + cell.bind( + title: "coin_analytics.holders".localized, + value: value, + valueInfo: viewItem.isPreview ? nil : "coin_analytics.current".localized, + onTapInfo: { + self?.openHoldersInfo() + } + ) + } ), Row( - id: "holders-pie", - height: CoinAnalyticsHoldersCell.chartHeight + .margin16, - bind: { cell, _ in - cell.set(backgroundStyle: .lawrence) - cell.topSeparatorView.isHidden = true - cell.bind(items: chartItems) - } - ) + id: "holders-pie", + height: CoinAnalyticsHoldersCell.chartHeight + .margin16, + bind: { cell, _ in + cell.set(backgroundStyle: .lawrence) + cell.topSeparatorView.isHidden = true + cell.bind(items: chartItems) + } + ), ] if let rating { rows.append( - ratingRow( - id: "holders-rating", - rating: rating, - isLast: false, - action: { [weak self] in - self?.openHoldersScoreInfo() - } - ) + ratingRow( + id: "holders-rating", + rating: rating, + isLast: false, + action: { [weak self] in + self?.openHoldersScoreInfo() + } + ) ) } rows.append(contentsOf: blockchains.enumerated().map { index, blockchain in tableView.universalRow56( - id: "holders-blockchain-\(index)", - image: .url(blockchain.imageUrl, placeholder: "placeholder_rectangle_32"), - title: .subhead2(blockchain.name), - value: .subhead1(blockchain.value), - accessoryType: .disclosure, - autoDeselect: true, - isLast: index == blockchains.count - 1 && rank == nil, - action: blockchain.action + id: "holders-blockchain-\(index)", + image: .url(blockchain.imageUrl, placeholder: "placeholder_rectangle_32"), + title: .subhead2(blockchain.name), + value: .subhead1(blockchain.value), + accessoryType: .disclosure, + autoDeselect: true, + isLast: index == blockchains.count - 1 && rank == nil, + action: blockchain.action ) }) if let rank { rows.append( - previewableRow( - id: "holders-rank", - title: "coin_analytics.holders_rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: true, - action: rank.previewableValue { _ in { [weak self] in - self?.openRanks(type: .holders) - }} - ) + previewableRow( + id: "holders-rank", + title: "coin_analytics.holders_rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: true, + action: rank.previewableValue { _ in { [weak self] in + self?.openRanks(type: .holders) + }} + ) ) } return Section( - id: "holders", - headerState: .margin(height: .margin12), - rows: rows + id: "holders", + headerState: .margin(height: .margin12), + rows: rows ) } @@ -1037,68 +1036,68 @@ extension CoinAnalyticsViewController: SectionsDataSource { var rows: [RowProtocol] = [ chartRow( - id: "tvl", - title: "coin_analytics.project_tvl".localized, - valueInfo: "coin_analytics.current".localized, - chartCurveType: .line, - viewItem: viewItem.chart, - isLast: itemCount == 1, - infoAction: { [weak self] in - self?.openTvlInfo() - }, - action: { [weak self] in - self?.openProDataChart(type: .tvl) - } - ) + id: "tvl", + title: "coin_analytics.project_tvl".localized, + valueInfo: "coin_analytics.current".localized, + chartCurveType: .line, + viewItem: viewItem.chart, + isLast: itemCount == 1, + infoAction: { [weak self] in + self?.openTvlInfo() + }, + action: { [weak self] in + self?.openProDataChart(type: .tvl) + } + ), ] if let rating = viewItem.rating { rows.append( - ratingRow( - id: "tvl-rating", - rating: rating, - isLast: rows.count + 1 == itemCount, - action: { [weak self] in - self?.openTvlScoreInfo() - } - ) + ratingRow( + id: "tvl-rating", + rating: rating, + isLast: rows.count + 1 == itemCount, + action: { [weak self] in + self?.openTvlScoreInfo() + } + ) ) } if let rank = viewItem.rank { rows.append( - previewableRow( - id: "tvl-rank", - title: "coin_analytics.rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: rows.count + 1 == itemCount, - action: rank.previewableValue { _ in { [weak self] in - self?.openTvlRank() - }} - ) + previewableRow( + id: "tvl-rank", + title: "coin_analytics.rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: rows.count + 1 == itemCount, + action: rank.previewableValue { _ in { [weak self] in + self?.openTvlRank() + }} + ) ) } if let ratio = viewItem.ratio { rows.append( - previewableRow( - id: "tvl-ratio", - title: "coin_analytics.tvl_ratio".localized, - value: ratio, - isLast: rows.count + 1 == itemCount - ) + previewableRow( + id: "tvl-ratio", + title: "coin_analytics.tvl_ratio".localized, + value: ratio, + isLast: rows.count + 1 == itemCount + ) ) } return Section( - id: "tvl", - headerState: .margin(height: .margin12), - rows: rows + id: "tvl", + headerState: .margin(height: .margin12), + rows: rows ) } - private func valueRankSection(id: String, title: String, viewItem: CoinAnalyticsViewModel.ValueRankViewItem, onTapRank: @escaping () -> ()) -> SectionProtocol { + private func valueRankSection(id: String, title: String, viewItem: CoinAnalyticsViewModel.ValueRankViewItem, onTapRank: @escaping () -> Void) -> SectionProtocol { let value: String? let valueInfo: String? @@ -1106,68 +1105,68 @@ extension CoinAnalyticsViewController: SectionsDataSource { case .preview: value = Self.placeholderText valueInfo = nil - case .regular(let _value): + case let .regular(_value): value = _value valueInfo = "coin_analytics.last_30d".localized } var rows: [RowProtocol] = [ Row( - id: id, - height: MarketWideCardCell.height(hasChart: false), - bind: { cell, _ in - cell.set(backgroundStyle: .lawrence, isFirst: true) - cell.selectionStyle = .none - - cell.bind( - title: title, - value: value, - valueInfo: valueInfo - ) - } - ) + id: id, + height: MarketWideCardCell.height(hasChart: false), + bind: { cell, _ in + cell.set(backgroundStyle: .lawrence, isFirst: true) + cell.selectionStyle = .none + + cell.bind( + title: title, + value: value, + valueInfo: valueInfo + ) + } + ), ] if let rank = viewItem.rank { rows.append( - previewableRow( - id: "\(id)-rank", - title: "coin_analytics.30_day_rank".localized, - value: rank, - accessoryType: .disclosure, - isLast: true, - action: rank.previewableValue { _ in onTapRank } - ) + previewableRow( + id: "\(id)-rank", + title: "coin_analytics.30_day_rank".localized, + value: rank, + accessoryType: .disclosure, + isLast: true, + action: rank.previewableValue { _ in onTapRank } + ) ) } return Section( - id: id, - headerState: .margin(height: .margin12), - footerState: viewItem.description.map { tableView.sectionFooter(text: $0, bottomMargin: .margin12) } ?? .margin(height: 0), - rows: rows + id: id, + headerState: .margin(height: .margin12), + footerState: viewItem.description.map { tableView.sectionFooter(text: $0, bottomMargin: .margin12) } ?? .margin(height: 0), + rows: rows ) } private func feeSection(viewItem: CoinAnalyticsViewModel.ValueRankViewItem) -> SectionProtocol { valueRankSection( - id: "fee", - title: "coin_analytics.project_fee".localized, - viewItem: viewItem, - onTapRank: { [weak self] in - self?.openRanks(type: .fee) - } + id: "fee", + title: "coin_analytics.project_fee".localized, + viewItem: viewItem, + onTapRank: { [weak self] in + self?.openRanks(type: .fee) + } ) } private func revenueSection(viewItem: CoinAnalyticsViewModel.ValueRankViewItem) -> SectionProtocol { valueRankSection( - id: "revenue", - title: "coin_analytics.project_revenue".localized, - viewItem: viewItem, - onTapRank: { [weak self] in - self?.openRanks(type: .revenue) - } + id: "revenue", + title: "coin_analytics.project_revenue".localized, + viewItem: viewItem, + onTapRank: { [weak self] in + self?.openRanks(type: .revenue) + } ) } @@ -1183,73 +1182,73 @@ extension CoinAnalyticsViewController: SectionsDataSource { if let reports { rows.append( - previewableRow( - id: "reports", - title: "coin_analytics.reports".localized, - value: reports, - accessoryType: .disclosure, - isFirst: rows.isEmpty, - isLast: rows.count == rowCount - 1, - action: reports.previewableValue { _ in { [weak self] in - self?.openReports() - }} - ) + previewableRow( + id: "reports", + title: "coin_analytics.reports".localized, + value: reports, + accessoryType: .disclosure, + isFirst: rows.isEmpty, + isLast: rows.count == rowCount - 1, + action: reports.previewableValue { _ in { [weak self] in + self?.openReports() + }} + ) ) } if let investors { rows.append( - previewableRow( - id: "investors", - title: "coin_analytics.funding".localized, - value: investors, - accessoryType: .disclosure, - isFirst: rows.isEmpty, - isLast: rows.count == rowCount - 1, - action: investors.previewableValue { _ in { [weak self] in - self?.openInvestors() - }} - ) + previewableRow( + id: "investors", + title: "coin_analytics.funding".localized, + value: investors, + accessoryType: .disclosure, + isFirst: rows.isEmpty, + isLast: rows.count == rowCount - 1, + action: investors.previewableValue { _ in { [weak self] in + self?.openInvestors() + }} + ) ) } if let treasuries { rows.append( - previewableRow( - id: "treasuries", - title: "coin_analytics.treasuries".localized, - value: treasuries, - accessoryType: .disclosure, - isFirst: rows.isEmpty, - isLast: rows.count == rowCount - 1, - action: treasuries.previewableValue { _ in { [weak self] in - self?.openTreasuries() - }} - ) + previewableRow( + id: "treasuries", + title: "coin_analytics.treasuries".localized, + value: treasuries, + accessoryType: .disclosure, + isFirst: rows.isEmpty, + isLast: rows.count == rowCount - 1, + action: treasuries.previewableValue { _ in { [weak self] in + self?.openTreasuries() + }} + ) ) } if let auditAddresses { rows.append( - previewableRow( - id: "audits", - title: "coin_analytics.audits".localized, - accessoryType: .disclosure, - isFirst: rows.isEmpty, - isLast: rows.count == rowCount - 1, - action: auditAddresses.previewableValue { addresses in { [weak self] in - self?.openAudits(addresses: addresses) - }} - ) + previewableRow( + id: "audits", + title: "coin_analytics.audits".localized, + accessoryType: .disclosure, + isFirst: rows.isEmpty, + isLast: rows.count == rowCount - 1, + action: auditAddresses.previewableValue { addresses in { [weak self] in + self?.openAudits(addresses: addresses) + }} + ) ) } return Section( - id: "investor-data", - headerState: .margin(height: .margin12), - rows: [ - tableView.headerInfoRow(id: "investor-data-header", title: "coin_analytics.other_data".localized) - ] + rows + id: "investor-data", + headerState: .margin(height: .margin12), + rows: [ + tableView.headerInfoRow(id: "investor-data-header", title: "coin_analytics.other_data".localized), + ] + rows ) } @@ -1298,20 +1297,19 @@ extension CoinAnalyticsViewController: SectionsDataSource { } if let otherDataSection = otherDataSection( - investors: viewItem.investors, - treasuries: viewItem.treasuries, - reports: viewItem.reports, - auditAddresses: viewItem.auditAddresses + investors: viewItem.investors, + treasuries: viewItem.treasuries, + reports: viewItem.reports, + auditAddresses: viewItem.auditAddresses ) { sections.append(otherDataSection) } sections.append( - Section(id: "footer", headerState: .margin(height: .margin32)) + Section(id: "footer", headerState: .margin(height: .margin32)) ) } return sections } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewModule.swift index 7535a4d60b..918d4d2332 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewModule.swift @@ -3,7 +3,7 @@ import MarketKit import SwiftUI struct CoinOverviewModule { - static func view(coinUid: String) -> some View { + static func view(coinUid: String, apiTag: String) -> some View { let repository = ChartIndicatorsRepository( localStorage: App.shared.localStorage, subscriptionManager: App.shared.subscriptionManager @@ -24,7 +24,8 @@ struct CoinOverviewModule { currencyManager: App.shared.currencyManager, languageManager: LanguageManager.shared, accountManager: App.shared.accountManager, - walletManager: App.shared.walletManager + walletManager: App.shared.walletManager, + apiTag: apiTag ) return CoinOverviewView( @@ -35,14 +36,15 @@ struct CoinOverviewModule { ) } - static func viewController(coinUid: String) -> CoinOverviewViewController { + static func viewController(coinUid: String, apiTag: String) -> CoinOverviewViewController { let service = CoinOverviewService( coinUid: coinUid, marketKit: App.shared.marketKit, currencyManager: App.shared.currencyManager, languageManager: LanguageManager.shared, accountManager: App.shared.accountManager, - walletManager: App.shared.walletManager + walletManager: App.shared.walletManager, + apiTag: apiTag ) let repository = ChartIndicatorsRepository( diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewService.swift index f2d9f1b757..6f300b473d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewService.swift @@ -1,8 +1,8 @@ -import UIKit -import RxSwift -import RxCocoa -import MarketKit import HsExtensions +import MarketKit +import RxCocoa +import RxSwift +import UIKit class CoinOverviewService { private var tasks = Set() @@ -13,6 +13,7 @@ class CoinOverviewService { private let languageManager: LanguageManager private let accountManager: AccountManager private let walletManager: WalletManager + private let apiTag: String private let stateRelay = PublishRelay>() private(set) var state: DataStatus = .loading { @@ -21,58 +22,59 @@ class CoinOverviewService { } } - init(coinUid: String, marketKit: MarketKit.Kit, currencyManager: CurrencyManager, languageManager: LanguageManager, accountManager: AccountManager, walletManager: WalletManager) { + init(coinUid: String, marketKit: MarketKit.Kit, currencyManager: CurrencyManager, languageManager: LanguageManager, accountManager: AccountManager, walletManager: WalletManager, apiTag: String) { self.coinUid = coinUid self.marketKit = marketKit self.currencyManager = currencyManager self.languageManager = languageManager self.accountManager = accountManager self.walletManager = walletManager + self.apiTag = apiTag } private func sync(info: MarketInfoOverview) { let account = accountManager.activeAccount let tokens = info.fullCoin.tokens - .filter { - switch $0.type { - case .unsupported(_, let reference): return reference != nil - default: return true - } + .filter { + switch $0.type { + case let .unsupported(_, reference): return reference != nil + default: return true } + } let walletTokens = walletManager.activeWallets.map { $0.token } let tokenItems = tokens - .sorted { lhsToken, rhsToken in - let lhsTypeOrder = lhsToken.type.order - let rhsTypeOrder = rhsToken.type.order - - guard lhsTypeOrder == rhsTypeOrder else { - return lhsTypeOrder < rhsTypeOrder - } + .sorted { lhsToken, rhsToken in + let lhsTypeOrder = lhsToken.type.order + let rhsTypeOrder = rhsToken.type.order - return lhsToken.blockchainType.order < rhsToken.blockchainType.order + guard lhsTypeOrder == rhsTypeOrder else { + return lhsTypeOrder < rhsTypeOrder } - .map { token in - let state: TokenItemState - - if let account = account, !account.watchAccount, account.type.supports(token: token) { - if walletTokens.contains(token) { - state = .alreadyAdded - } else { - state = .canBeAdded - } + + return lhsToken.blockchainType.order < rhsToken.blockchainType.order + } + .map { token in + let state: TokenItemState + + if let account = account, !account.watchAccount, account.type.supports(token: token) { + if walletTokens.contains(token) { + state = .alreadyAdded } else { - state = .cannotBeAdded + state = .canBeAdded } - - return TokenItem( - token: token, - state: state - ) + } else { + state = .cannotBeAdded } + return TokenItem( + token: token, + state: state + ) + } + state = .completed(Item(info: info, tokens: tokenItems, guideUrl: guideUrl)) } @@ -102,11 +104,9 @@ class CoinOverviewService { default: return nil } } - } extension CoinOverviewService { - var stateObservable: Observable> { stateRelay.asObservable() } @@ -120,9 +120,14 @@ extension CoinOverviewService { state = .loading - Task { [weak self, marketKit, coinUid, currencyManager, languageManager] in + Task { [weak self, marketKit, coinUid, currencyManager, languageManager, apiTag] in do { - let info = try await marketKit.marketInfoOverview(coinUid: coinUid, currencyCode: currencyManager.baseCurrency.code, languageCode: languageManager.currentLanguage) + let info = try await marketKit.marketInfoOverview( + coinUid: coinUid, + currencyCode: currencyManager.baseCurrency.code, + languageCode: languageManager.currentLanguage, + apiTag: apiTag + ) self?.sync(info: info) } catch { self?.state = .failed(error) @@ -131,7 +136,7 @@ extension CoinOverviewService { } func editWallet(index: Int, add: Bool) throws { - guard case .completed(let item) = state else { + guard case let .completed(item) = state else { throw EditWalletError.invalidState } @@ -151,11 +156,9 @@ extension CoinOverviewService { sync(info: item.info) } - } extension CoinOverviewService { - struct Item { let info: MarketInfoOverview let tokens: [TokenItem] @@ -177,5 +180,4 @@ extension CoinOverviewService { case invalidState case noActiveAccount } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModelNew.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModelNew.swift index 948d9b8acf..9ff4e514e2 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModelNew.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModelNew.swift @@ -12,19 +12,21 @@ class CoinOverviewViewModelNew: ObservableObject { private let languageManager: LanguageManager private let accountManager: AccountManager private let walletManager: WalletManager + private let apiTag: String private let viewItemFactory = CoinOverviewViewItemFactory() let currency: Currency @Published private(set) var state: DataStatus = .loading - init(coinUid: String, marketKit: MarketKit.Kit, currencyManager: CurrencyManager, languageManager: LanguageManager, accountManager: AccountManager, walletManager: WalletManager) { + init(coinUid: String, marketKit: MarketKit.Kit, currencyManager: CurrencyManager, languageManager: LanguageManager, accountManager: AccountManager, walletManager: WalletManager, apiTag: String) { self.coinUid = coinUid self.marketKit = marketKit self.currencyManager = currencyManager self.languageManager = languageManager self.accountManager = accountManager self.walletManager = walletManager + self.apiTag = apiTag currency = currencyManager.baseCurrency } @@ -119,9 +121,14 @@ extension CoinOverviewViewModelNew { state = .loading - Task { [weak self, marketKit, coinUid, currencyManager, languageManager] in + Task { [weak self, marketKit, coinUid, currencyManager, languageManager, apiTag] in do { - let info = try await marketKit.marketInfoOverview(coinUid: coinUid, currencyCode: currencyManager.baseCurrency.code, languageCode: languageManager.currentLanguage) + let info = try await marketKit.marketInfoOverview( + coinUid: coinUid, + currencyCode: currencyManager.baseCurrency.code, + languageCode: languageManager.currentLanguage, + apiTag: apiTag + ) self?.handleSuccess(info: info) } catch { self?.handleFailure(error: error) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageModule.swift index 8691ab1b39..b7b2479ac2 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageModule.swift @@ -4,11 +4,11 @@ import ThemeKit import UIKit struct CoinPageModule { - static func view(fullCoin: FullCoin) -> some View { + static func view(fullCoin: FullCoin, apiTag: String) -> 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 overviewView = CoinOverviewModule.view(coinUid: fullCoin.coin.uid, apiTag: apiTag) + let analyticsView = CoinAnalyticsModule.view(fullCoin: fullCoin, apiTag: apiTag) let marketsView = CoinMarketsModule.view(coin: fullCoin.coin) return CoinPageView( @@ -19,7 +19,7 @@ struct CoinPageModule { ) } - static func viewController(coinUid: String) -> UIViewController? { + static func viewController(coinUid: String, apiTag: String) -> UIViewController? { guard let fullCoin = try? App.shared.marketKit.fullCoins(coinUids: [coinUid]).first else { return nil } @@ -31,9 +31,9 @@ struct CoinPageModule { let viewModel = CoinPageViewModel(service: service) - let overviewController = CoinOverviewModule.viewController(coinUid: coinUid) + let overviewController = CoinOverviewModule.viewController(coinUid: coinUid, apiTag: apiTag) let marketsController = CoinMarketsModule.view(coin: fullCoin.coin).toViewController() - let analyticsController = CoinAnalyticsModule.viewController(fullCoin: fullCoin) + let analyticsController = CoinAnalyticsModule.viewController(fullCoin: fullCoin, apiTag: apiTag) // let tweetsController = CoinTweetsModule.viewController(fullCoin: fullCoin) let viewController = CoinPageViewController( diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Ranks/CoinRankViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Ranks/CoinRankViewController.swift index 2f764c2371..8590163b27 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Ranks/CoinRankViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/Ranks/CoinRankViewController.swift @@ -1,9 +1,9 @@ -import UIKit -import RxSwift -import ThemeKit -import SectionsTableView import ComponentKit import HUD +import RxSwift +import SectionsTableView +import ThemeKit +import UIKit class CoinRankViewController: ThemeViewController { private let viewModel: CoinRankViewModel @@ -23,7 +23,8 @@ class CoinRankViewController: ThemeViewController { super.init() } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -83,56 +84,54 @@ class CoinRankViewController: ThemeViewController { private func scrollToTop() { tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .bottom, animated: true) } - } extension CoinRankViewController: SectionsDataSource { - private func row(viewItem: CoinRankViewModel.ViewItem, index: Int, isLast: Bool) -> RowProtocol { CellBuilderNew.row( - rootElement: .hStack([ - .text { component in - component.font = .captionSB - component.textColor = .themeGray - component.text = viewItem.rank - component.textAlignment = .center - - component.snp.remakeConstraints { maker in - maker.width.equalTo(40) - } - }, - .margin8, - .image32 { component in - component.setImage(urlString: viewItem.imageUrl, placeholder: UIImage(named: "placeholder_circle_32")) - }, - .vStackCentered([ - .textElement(text: .body(viewItem.code)), - .margin(1), - .textElement(text: .subhead2(viewItem.name)), - ]), - .textElement(text: .body(viewItem.value), parameters: .rightAlignment) - ]), - layoutMargins: UIEdgeInsets(top: 0, left: .margin8, bottom: 0, right: CellBuilderNew.defaultMargin), - tableView: tableView, - id: "row-\(index)", - height: .heightDoubleLineCell, - autoDeselect: true, - bind: { cell in - cell.set(backgroundStyle: .transparent, isLast: isLast) - }, - action: { [weak self] in - if let viewController = CoinPageModule.viewController(coinUid: viewItem.uid) { - self?.present(viewController, animated: true) + rootElement: .hStack([ + .text { component in + component.font = .captionSB + component.textColor = .themeGray + component.text = viewItem.rank + component.textAlignment = .center + + component.snp.remakeConstraints { maker in + maker.width.equalTo(40) } + }, + .margin8, + .image32 { component in + component.setImage(urlString: viewItem.imageUrl, placeholder: UIImage(named: "placeholder_circle_32")) + }, + .vStackCentered([ + .textElement(text: .body(viewItem.code)), + .margin(1), + .textElement(text: .subhead2(viewItem.name)), + ]), + .textElement(text: .body(viewItem.value), parameters: .rightAlignment), + ]), + layoutMargins: UIEdgeInsets(top: 0, left: .margin8, bottom: 0, right: CellBuilderNew.defaultMargin), + tableView: tableView, + id: "row-\(index)", + height: .heightDoubleLineCell, + autoDeselect: true, + bind: { cell in + cell.set(backgroundStyle: .transparent, isLast: isLast) + }, + action: { [weak self] in + if let viewController = CoinPageModule.viewController(coinUid: viewItem.uid, apiTag: "coin_rank") { + self?.present(viewController, animated: true) } + } ) } private func bind(cell: MarketHeaderCell) { cell.set( - title: viewModel.title, - description: viewModel.description, - imageMode: .remote(imageUrl: viewModel.imageUid.headerImageUrl) + title: viewModel.title, + description: viewModel.description, + imageMode: .remote(imageUrl: viewModel.imageUid.headerImageUrl) ) } @@ -143,26 +142,25 @@ extension CoinRankViewController: SectionsDataSource { return [ Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { [weak self] cell, _ in - self?.bind(cell: cell) - } - ) - ] + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { [weak self] cell, _ in + self?.bind(cell: cell) + } + ), + ] ), Section( - id: "coins", - headerState: .static(view: headerView, height: CoinRankHeaderView.height), - footerState: .marginColor(height: .margin32, color: .clear), - rows: viewItems.enumerated().map { index, viewItem in - row(viewItem: viewItem, index: index, isLast: index == viewItems.count - 1) - } - ) + id: "coins", + headerState: .static(view: headerView, height: CoinRankHeaderView.height), + footerState: .marginColor(height: .margin32, color: .clear), + rows: viewItems.enumerated().map { index, viewItem in + row(viewItem: viewItem, index: index, isLast: index == viewItems.count - 1) + } + ), ] } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WalletConnectAppShowWorker/WalletConnectAppShowView.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WalletConnectAppShowWorker/WalletConnectAppShowView.swift index b2dbc51f77..49561dd01a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WalletConnectAppShowWorker/WalletConnectAppShowView.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WalletConnectAppShowWorker/WalletConnectAppShowView.swift @@ -91,7 +91,7 @@ class WalletConnectAppShowView { private func handle(request: WalletConnectRequest) { let result = requestViewFactory.viewController(request: request) switch result { - case let .unsuccessful(error): + case .unsuccessful: print("Can't create view") return case let .controller(controller): diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WidgetCoinAppShowWorker/WidgetCoinAppShowModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WidgetCoinAppShowWorker/WidgetCoinAppShowModule.swift index 14a110b15d..78aec4174b 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WidgetCoinAppShowWorker/WidgetCoinAppShowModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Main/Workers/WidgetCoinAppShowWorker/WidgetCoinAppShowModule.swift @@ -9,7 +9,6 @@ class WidgetCoinAppShowModule { } extension WidgetCoinAppShowModule: IEventHandler { - @MainActor func handle(event: Any, eventType: EventHandler.EventType) async throws { guard eventType.contains(.deepLink) else { @@ -28,7 +27,8 @@ extension WidgetCoinAppShowModule: IEventHandler { } guard let uid, - let viewController = CoinPageModule.viewController(coinUid: uid) else { + let viewController = CoinPageModule.viewController(coinUid: uid, apiTag: "widget") + else { throw EventHandler.HandleError.noSuitableHandler } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/FilteredList/MarketFilteredListService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/FilteredList/MarketFilteredListService.swift index 573a23b5c3..576feee5fb 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/FilteredList/MarketFilteredListService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/FilteredList/MarketFilteredListService.swift @@ -1,11 +1,11 @@ import Combine -import RxSwift -import RxRelay -import MarketKit import HsExtensions +import MarketKit +import RxRelay +import RxSwift protocol IMarketFilteredListProvider { - func marketInfo(currencyCode: String) async throws -> [MarketInfo] + func marketInfos(currencyCode: String) async throws -> [MarketInfo] } class MarketFilteredListService: IMarketMultiSortHeaderService { @@ -37,7 +37,7 @@ class MarketFilteredListService: IMarketMultiSortHeaderService { Task { [weak self, provider, currency] in do { - let marketInfos = try await provider.marketInfo(currencyCode: currency.code) + let marketInfos = try await provider.marketInfos(currencyCode: currency.code) self?.sync(marketInfos: marketInfos) } catch { self?.state = .failed(error: error) @@ -50,17 +50,15 @@ class MarketFilteredListService: IMarketMultiSortHeaderService { } private func syncIfPossible() { - guard case .loaded(let marketInfos, _, _) = state else { + guard case let .loaded(marketInfos, _, _) = state else { return } sync(marketInfos: marketInfos, reorder: true) } - } extension MarketFilteredListService: IMarketListService { - var statePublisher: AnyPublisher, Never> { $state } @@ -68,23 +66,19 @@ extension MarketFilteredListService: IMarketListService { func refresh() { syncMarketInfos() } - } extension MarketFilteredListService: IMarketListCoinUidService { - func coinUid(index: Int) -> String? { - guard case .loaded(let marketInfos, _, _) = state, index < marketInfos.count else { + guard case let .loaded(marketInfos, _, _) = state, index < marketInfos.count else { return nil } return marketInfos[index].fullCoin.coin.uid } - } extension MarketFilteredListService: IMarketListDecoratorService { - var initialMarketFieldIndex: Int { 0 } @@ -97,10 +91,9 @@ extension MarketFilteredListService: IMarketListDecoratorService { .day } - func onUpdate(marketFieldIndex: Int) { - if case .loaded(let marketInfos, _, _) = state { + func onUpdate(marketFieldIndex _: Int) { + if case let .loaded(marketInfos, _, _) = state { state = .loaded(items: marketInfos, softUpdate: false, reorder: false) } } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketAdvancedSearchResults/MarketAdvancedSearchResultViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketAdvancedSearchResults/MarketAdvancedSearchResultViewController.swift index 168d22a4e0..f5eead1d17 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketAdvancedSearchResults/MarketAdvancedSearchResultViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketAdvancedSearchResults/MarketAdvancedSearchResultViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class MarketAdvancedSearchResultViewController: MarketListViewController { private let multiSortHeaderView: MarketMultiSortHeaderView @@ -13,12 +13,13 @@ class MarketAdvancedSearchResultViewController: MarketListViewController { init(listViewModel: IMarketListViewModel, headerViewModel: MarketMultiSortHeaderViewModel) { multiSortHeaderView = MarketMultiSortHeaderView(viewModel: headerViewModel) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "market_advanced_search_results") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -27,5 +28,4 @@ class MarketAdvancedSearchResultViewController: MarketListViewController { title = "market.advanced_search_results.title".localized } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryModule.swift index 6a3511c64d..d47d90aedd 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryModule.swift @@ -1,15 +1,15 @@ -import UIKit -import ThemeKit import Chart import MarketKit +import ThemeKit +import UIKit struct MarketCategoryModule { - - static func viewController(category: CoinCategory) -> UIViewController { + static func viewController(category: CoinCategory, apiTag: String) -> UIViewController { let service = MarketCategoryService( - category: category, - marketKit: App.shared.marketKit, - languageManager: LanguageManager.shared + category: category, + marketKit: App.shared.marketKit, + languageManager: LanguageManager.shared, + apiTag: apiTag ) let listService = MarketFilteredListService(currencyManager: App.shared.currencyManager, provider: service) @@ -29,5 +29,4 @@ struct MarketCategoryModule { return ThemeNavigationController(rootViewController: viewController) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryService.swift index 1362c510c5..23fb5ea098 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryService.swift @@ -4,27 +4,24 @@ class MarketCategoryService { let category: CoinCategory private let marketKit: MarketKit.Kit private let languageManager: LanguageManager + private let apiTag: String - init(category: CoinCategory, marketKit: MarketKit.Kit, languageManager: LanguageManager) { + init(category: CoinCategory, marketKit: MarketKit.Kit, languageManager: LanguageManager, apiTag: String) { self.category = category self.marketKit = marketKit self.languageManager = languageManager + self.apiTag = apiTag } - } extension MarketCategoryService { - var currentLanguage: String { languageManager.currentLanguage } - } extension MarketCategoryService: IMarketFilteredListProvider { - - func marketInfo(currencyCode: String) async throws -> [MarketInfo] { - try await marketKit.marketInfos(categoryUid: category.uid, currencyCode: currencyCode) + func marketInfos(currencyCode: String) async throws -> [MarketInfo] { + try await marketKit.marketInfos(categoryUid: category.uid, currencyCode: currencyCode, apiTag: apiTag) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryViewController.swift index c3ecdc1490..9ac5be6acf 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketCategory/MarketCategoryViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class MarketCategoryViewController: MarketListViewController { private let viewModel: MarketCategoryViewModel @@ -24,17 +24,18 @@ class MarketCategoryViewController: MarketListViewController { chartCell = ChartCell(viewModel: chartViewModel, configuration: .baseChart) chartRow = StaticRow( - cell: chartCell, - id: "chartView", - height: chartCell.cellHeight + cell: chartCell, + id: "chartView", + height: chartCell.cellHeight ) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "market_category") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -56,16 +57,16 @@ class MarketCategoryViewController: MarketListViewController { override func topSections(loaded: Bool) -> [SectionProtocol] { var sections = [Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { [weak self] cell, _ in - self?.bind(cell: cell) - } - ) - ] + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { [weak self] cell, _ in + self?.bind(cell: cell) + } + ), + ] )] if loaded { @@ -77,10 +78,9 @@ class MarketCategoryViewController: MarketListViewController { private func bind(cell: MarketHeaderCell) { cell.set( - title: viewModel.title, - description: viewModel.description, - imageMode: .remote(imageUrl: viewModel.imageUrl) + title: viewModel.title, + description: viewModel.description, + imageMode: .remote(imageUrl: viewModel.imageUrl) ) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketDiscovery/MarketDiscoveryViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketDiscovery/MarketDiscoveryViewController.swift index 1442390170..e619b71086 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketDiscovery/MarketDiscoveryViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketDiscovery/MarketDiscoveryViewController.swift @@ -1,11 +1,11 @@ import Combine -import UIKit -import RxSwift -import SnapKit import ComponentKit import HUD +import RxSwift import SectionsTableView +import SnapKit import ThemeKit +import UIKit class MarketDiscoveryViewController: ThemeSearchViewController { private let viewModel: MarketDiscoveryViewModel @@ -34,7 +34,8 @@ class MarketDiscoveryViewController: ThemeSearchViewController { hidesBottomBarWhenPushed = true } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -112,9 +113,9 @@ class MarketDiscoveryViewController: ThemeSearchViewController { subscribe(disposeBag, viewModel.failDriver) { HudHelper.instance.show(banner: .error(string: "alert.unknown_error".localized)) } $filter - .receive(on: DispatchQueue.main) - .sink { [weak self] in self?.viewModel.onUpdate(filter: $0 ?? "") } - .store(in: &cancellables) + .receive(on: DispatchQueue.main) + .sink { [weak self] in self?.viewModel.onUpdate(filter: $0 ?? "") } + .store(in: &cancellables) isLoaded = true } @@ -161,7 +162,7 @@ class MarketDiscoveryViewController: ThemeSearchViewController { } private func onSelect(viewItem: MarketDiscoveryViewModel.SearchViewItem) { - guard let module = CoinPageModule.viewController(coinUid: viewItem.uid) else { + guard let module = CoinPageModule.viewController(coinUid: viewItem.uid, apiTag: "market_discovery") else { return } @@ -171,7 +172,7 @@ class MarketDiscoveryViewController: ThemeSearchViewController { private func rowActions(index: Int) -> [RowAction] { let type: RowActionType let iconName: String - let action: (UITableViewCell?) -> () + let action: (UITableViewCell?) -> Void if viewModel.isFavorite(index: index) { type = .destructive @@ -189,39 +190,35 @@ class MarketDiscoveryViewController: ThemeSearchViewController { return [ RowAction( - pattern: .icon(image: UIImage(named: iconName)?.withTintColor(type.iconColor), background: type.backgroundColor), - action: action - ) + pattern: .icon(image: UIImage(named: iconName)?.withTintColor(type.iconColor), background: type.backgroundColor), + action: action + ), ] } - } extension MarketDiscoveryViewController: UICollectionViewDataSource { - - func numberOfSections(in collectionView: UICollectionView) -> Int { + func numberOfSections(in _: UICollectionView) -> Int { 2 } - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_: UICollectionView, numberOfItemsInSection section: Int) -> Int { section == 0 ? 1 : discoveryViewItems.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: indexPath.section == 0 ? MarketDiscoveryTitleCell.self : MarketDiscoveryCell.self), for: indexPath) } - } extension MarketDiscoveryViewController: UICollectionViewDelegate { - - func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + func collectionView(_: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if let cell = cell as? MarketDiscoveryCell { cell.set(viewItem: discoveryViewItems[indexPath.item]) } } - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + func collectionView(_: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard indexPath.section != 0 else { return } @@ -230,84 +227,79 @@ extension MarketDiscoveryViewController: UICollectionViewDelegate { case .topCoins: let viewController = MarketTopModule.viewController() present(viewController, animated: true) - case .category(let category): - let viewController = MarketCategoryModule.viewController(category: category) + case let .category(category): + let viewController = MarketCategoryModule.viewController(category: category, apiTag: "market_discovery") present(viewController, animated: true) } } - } extension MarketDiscoveryViewController: UICollectionViewDelegateFlowLayout { - - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { indexPath.section == 0 ? - CGSize(width: collectionView.width, height: .heightSingleLineCell) : - CGSize(width: (collectionView.width - .margin16 * 2 - .margin12) / 2, height: MarketDiscoveryCell.cellHeight) + CGSize(width: collectionView.width, height: .heightSingleLineCell) : + CGSize(width: (collectionView.width - .margin16 * 2 - .margin12) / 2, height: MarketDiscoveryCell.cellHeight) } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + func collectionView(_: UICollectionView, layout _: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { section == 0 ? - .zero : - UIEdgeInsets(top: .margin12, left: .margin16, bottom: .margin32, right: .margin16) + .zero : + UIEdgeInsets(top: .margin12, left: .margin16, bottom: .margin32, right: .margin16) } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + func collectionView(_: UICollectionView, layout _: UICollectionViewLayout, minimumInteritemSpacingForSectionAt _: Int) -> CGFloat { .margin12 } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + func collectionView(_: UICollectionView, layout _: UICollectionViewLayout, minimumLineSpacingForSectionAt _: Int) -> CGFloat { .margin12 } - } extension MarketDiscoveryViewController: SectionsDataSource { - func buildSections() -> [SectionProtocol] { [ Section( - id: "coins", - headerState: .margin(height: .margin12), - footerState: .margin(height: .margin32), - rows: searchViewItems.enumerated().map { index, viewItem in - let isLast = index == searchViewItems.count - 1 - - return CellBuilderNew.row( - rootElement: .hStack([ - .image32 { component in - component.setImage(urlString: viewItem.imageUrl, placeholder: UIImage(named: viewItem.placeholderImageName)) - }, - .vStackCentered([ - .text { component in - component.font = .body - component.textColor = .themeLeah - component.text = viewItem.code - }, - .margin(3), - .text { component in - component.font = .subhead2 - component.textColor = .themeGray - component.text = viewItem.name - } - ]) - ]), - tableView: tableView, - id: "coin_\(viewItem.uid)", - hash: "\(viewItem.favorite)", - height: .heightDoubleLineCell, - autoDeselect: true, - rowActionProvider: { [weak self] in self?.rowActions(index: index) ?? [] }, - bind: { cell in - cell.set(backgroundStyle: .transparent, isLast: isLast) + id: "coins", + headerState: .margin(height: .margin12), + footerState: .margin(height: .margin32), + rows: searchViewItems.enumerated().map { index, viewItem in + let isLast = index == searchViewItems.count - 1 + + return CellBuilderNew.row( + rootElement: .hStack([ + .image32 { component in + component.setImage(urlString: viewItem.imageUrl, placeholder: UIImage(named: viewItem.placeholderImageName)) + }, + .vStackCentered([ + .text { component in + component.font = .body + component.textColor = .themeLeah + component.text = viewItem.code }, - action: { [weak self] in - self?.onSelect(viewItem: viewItem) - } - ) - } - ) + .margin(3), + .text { component in + component.font = .subhead2 + component.textColor = .themeGray + component.text = viewItem.name + }, + ]), + ]), + tableView: tableView, + id: "coin_\(viewItem.uid)", + hash: "\(viewItem.favorite)", + height: .heightDoubleLineCell, + autoDeselect: true, + rowActionProvider: { [weak self] in self?.rowActions(index: index) ?? [] }, + bind: { cell in + cell.set(backgroundStyle: .transparent, isLast: isLast) + }, + action: { [weak self] in + self?.onSelect(viewItem: viewItem) + } + ) + } + ), ] } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/DefiCap/MarketGlobalDefiMetricService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/DefiCap/MarketGlobalDefiMetricService.swift index 6a1e4e25f4..1191bfd633 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/DefiCap/MarketGlobalDefiMetricService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/DefiCap/MarketGlobalDefiMetricService.swift @@ -1,8 +1,8 @@ import Combine -import RxSwift -import RxRelay -import MarketKit import HsExtensions +import MarketKit +import RxRelay +import RxSwift class MarketGlobalDefiMetricService: IMarketSingleSortHeaderService { typealias Item = DefiItem @@ -38,7 +38,7 @@ class MarketGlobalDefiMetricService: IMarketSingleSortHeaderService { Task { [weak self, marketKit, currency] in do { - let marketInfos = try await marketKit.marketInfos(top: MarketModule.MarketTop.top100.rawValue, currencyCode: currency.code, defi: true) + let marketInfos = try await marketKit.marketInfos(top: MarketModule.MarketTop.top100.rawValue, currencyCode: currency.code, defi: true, apiTag: "global_metrics_defi_cap") let rankedItems = marketInfos.enumerated().map { index, info in Item(marketInfo: info, tvlRank: index + 1) @@ -56,7 +56,7 @@ class MarketGlobalDefiMetricService: IMarketSingleSortHeaderService { } private func syncIfPossible() { - guard case .loaded(let items, _, _) = state else { + guard case let .loaded(items, _, _) = state else { return } @@ -72,11 +72,9 @@ class MarketGlobalDefiMetricService: IMarketSingleSortHeaderService { } } } - } extension MarketGlobalDefiMetricService: IMarketListService { - var statePublisher: AnyPublisher, Never> { $state } @@ -84,23 +82,19 @@ extension MarketGlobalDefiMetricService: IMarketListService { func refresh() { syncMarketInfos() } - } extension MarketGlobalDefiMetricService: IMarketListCoinUidService { - func coinUid(index: Int) -> String? { - guard case .loaded(let items, _, _) = state, index < items.count else { + guard case let .loaded(items, _, _) = state, index < items.count else { return nil } return items[index].marketInfo.fullCoin.coin.uid } - } extension MarketGlobalDefiMetricService: IMarketListDecoratorService { - var currency: Currency { currencyManager.baseCurrency } @@ -109,19 +103,16 @@ extension MarketGlobalDefiMetricService: IMarketListDecoratorService { .day } - func onUpdate(marketFieldIndex: Int) { - if case .loaded(let items, _, _) = state { + func onUpdate(marketFieldIndex _: Int) { + if case let .loaded(items, _, _) = state { state = .loaded(items: items, softUpdate: false, reorder: false) } } - } extension MarketGlobalDefiMetricService { - struct DefiItem { let marketInfo: MarketInfo let tvlRank: Int } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricModule.swift index 2f922e83c7..cb0981e64b 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricModule.swift @@ -1,16 +1,15 @@ -import UIKit -import ThemeKit import Chart +import ThemeKit +import UIKit struct MarketGlobalMetricModule { - static func viewController(type: MarketGlobalModule.MetricsType) -> UIViewController { let viewController: UIViewController switch type { case .totalMarketCap, .volume24h: viewController = globalMetricViewController(type: type) case .defiCap: viewController = defiCapViewController() - case .tvlInDefi: viewController = tvlInDefiViewController() + case .tvlInDefi: viewController = tvlInDefiViewController(apiTag: "global_metrics") } return ThemeNavigationController(rootViewController: viewController) @@ -18,14 +17,14 @@ struct MarketGlobalMetricModule { private static func globalMetricViewController(type: MarketGlobalModule.MetricsType) -> UIViewController { let service = MarketGlobalMetricService( - marketKit: App.shared.marketKit, - currencyManager: App.shared.currencyManager, - metricsType: type + marketKit: App.shared.marketKit, + currencyManager: App.shared.currencyManager, + metricsType: type ) let watchlistToggleService = MarketWatchlistToggleService( - coinUidService: service, - favoritesManager: App.shared.favoritesManager + coinUidService: service, + favoritesManager: App.shared.favoritesManager ) let decorator = MarketListMarketFieldDecorator(service: service) @@ -34,8 +33,8 @@ struct MarketGlobalMetricModule { let chartFetcher = MarketGlobalFetcher(currencyManager: App.shared.currencyManager, marketKit: App.shared.marketKit, metricsType: type) let chartService = MetricChartService( - chartFetcher: chartFetcher, - interval: .day1 + chartFetcher: chartFetcher, + interval: .day1 ) let factory = MetricChartFactory(currentLocale: LanguageManager.shared.currentLocale) @@ -46,13 +45,13 @@ struct MarketGlobalMetricModule { private static func defiCapViewController() -> UIViewController { let service = MarketGlobalDefiMetricService( - marketKit: App.shared.marketKit, - currencyManager: App.shared.currencyManager + marketKit: App.shared.marketKit, + currencyManager: App.shared.currencyManager ) let watchlistToggleService = MarketWatchlistToggleService( - coinUidService: service, - favoritesManager: App.shared.favoritesManager + coinUidService: service, + favoritesManager: App.shared.favoritesManager ) let decorator = MarketListDefiDecorator(service: service) @@ -61,8 +60,8 @@ struct MarketGlobalMetricModule { let chartFetcher = MarketGlobalFetcher(currencyManager: App.shared.currencyManager, marketKit: App.shared.marketKit, metricsType: .defiCap) let chartService = MetricChartService( - chartFetcher: chartFetcher, - interval: .day1 + chartFetcher: chartFetcher, + interval: .day1 ) let factory = MetricChartFactory(currentLocale: LanguageManager.shared.currentLocale) @@ -71,15 +70,16 @@ struct MarketGlobalMetricModule { return MarketGlobalMetricViewController(listViewModel: listViewModel, headerViewModel: headerViewModel, chartViewModel: chartViewModel, metricsType: MarketGlobalModule.MetricsType.defiCap) } - static func tvlInDefiViewController() -> UIViewController { + static func tvlInDefiViewController(apiTag: String) -> UIViewController { let service = MarketGlobalTvlMetricService( - marketKit: App.shared.marketKit, - currencyManager: App.shared.currencyManager + marketKit: App.shared.marketKit, + currencyManager: App.shared.currencyManager, + apiTag: apiTag ) let watchlistToggleService = MarketWatchlistToggleService( - coinUidService: service, - favoritesManager: App.shared.favoritesManager + coinUidService: service, + favoritesManager: App.shared.favoritesManager ) let decorator = MarketListTvlDecorator(service: service) @@ -88,8 +88,8 @@ struct MarketGlobalMetricModule { let chartFetcher = MarketGlobalTvlFetcher(marketKit: App.shared.marketKit, currencyManager: App.shared.currencyManager, marketGlobalTvlPlatformService: service) let chartService = MetricChartService( - chartFetcher: chartFetcher, - interval: .day1 + chartFetcher: chartFetcher, + interval: .day1 ) service.chartService = chartService @@ -98,5 +98,4 @@ struct MarketGlobalMetricModule { return MarketGlobalTvlMetricViewController(listViewModel: listViewModel, headerViewModel: headerViewModel, chartViewModel: chartViewModel) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricService.swift index 4b6ff4e370..e28dc57a73 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricService.swift @@ -1,8 +1,8 @@ import Combine -import RxSwift -import RxRelay -import MarketKit import HsExtensions +import MarketKit +import RxRelay +import RxSwift class MarketGlobalMetricService: IMarketSingleSortHeaderService { typealias Item = MarketInfo @@ -19,7 +19,8 @@ class MarketGlobalMetricService: IMarketSingleSortHeaderService { syncIfPossible() } } - var metricsType: MarketGlobalModule.MetricsType + + private let metricsType: MarketGlobalModule.MetricsType let initialMarketFieldIndex: Int @@ -39,9 +40,9 @@ class MarketGlobalMetricService: IMarketSingleSortHeaderService { state = .loading } - Task { [weak self, marketKit, currency] in + Task { [weak self, marketKit, currency, metricsType] in do { - let marketInfos = try await marketKit.marketInfos(top: MarketModule.MarketTop.top100.rawValue, currencyCode: currency.code) + let marketInfos = try await marketKit.marketInfos(top: MarketModule.MarketTop.top100.rawValue, currencyCode: currency.code, apiTag: "global_metrics_\(metricsType)") self?.sync(marketInfos: marketInfos) } catch { self?.state = .failed(error: error) @@ -61,17 +62,15 @@ class MarketGlobalMetricService: IMarketSingleSortHeaderService { } private func syncIfPossible() { - guard case .loaded(let marketInfos, _, _) = state else { + guard case let .loaded(marketInfos, _, _) = state else { return } sync(marketInfos: marketInfos, reorder: true) } - } extension MarketGlobalMetricService: IMarketListService { - var statePublisher: AnyPublisher, Never> { $state } @@ -79,23 +78,19 @@ extension MarketGlobalMetricService: IMarketListService { func refresh() { syncMarketInfos() } - } extension MarketGlobalMetricService: IMarketListCoinUidService { - func coinUid(index: Int) -> String? { - guard case .loaded(let marketInfos, _, _) = state, index < marketInfos.count else { + guard case let .loaded(marketInfos, _, _) = state, index < marketInfos.count else { return nil } return marketInfos[index].fullCoin.coin.uid } - } extension MarketGlobalMetricService: IMarketListDecoratorService { - var currency: Currency { currencyManager.baseCurrency } @@ -104,10 +99,9 @@ extension MarketGlobalMetricService: IMarketListDecoratorService { .day } - func onUpdate(marketFieldIndex: Int) { - if case .loaded(let marketInfos, _, _) = state { + func onUpdate(marketFieldIndex _: Int) { + if case let .loaded(marketInfos, _, _) = state { state = .loaded(items: marketInfos, softUpdate: false, reorder: false) } } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricViewController.swift index 190ece7ef9..e4e400f653 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/MarketGlobalMetricViewController.swift @@ -1,10 +1,10 @@ -import UIKit -import SnapKit -import ThemeKit -import SectionsTableView import Chart import ComponentKit import RxSwift +import SectionsTableView +import SnapKit +import ThemeKit +import UIKit class MarketGlobalMetricViewController: MarketListViewController { private let metricsType: MarketGlobalModule.MetricsType @@ -33,16 +33,16 @@ class MarketGlobalMetricViewController: MarketListViewController { chartCell = ChartCell(viewModel: chartViewModel, configuration: configuration) chartRow = StaticRow( - cell: chartCell, - id: "chartView", - height: chartCell.cellHeight + cell: chartCell, + id: "chartView", + height: chartCell.cellHeight ) - super.init(listViewModel: listViewModel) - + super.init(listViewModel: listViewModel, apiTag: "market_global_metrics_\(metricsType)") } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -71,30 +71,29 @@ class MarketGlobalMetricViewController: MarketListViewController { return [ Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { [weak self] cell, _ in - self?.bind(cell: cell) - } - ) - ] + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { [weak self] cell, _ in + self?.bind(cell: cell) + } + ), + ] ), Section( - id: "chart", - rows: [chartRow] - ) + id: "chart", + rows: [chartRow] + ), ] } private func bind(cell: MarketHeaderCell) { cell.set( - title: metricsType.title, - description: metricsType.description, - imageMode: .remote(imageUrl: metricsType.imageUid.headerImageUrl) + title: metricsType.title, + description: metricsType.description, + imageMode: .remote(imageUrl: metricsType.imageUid.headerImageUrl) ) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricService.swift index 7d5057bb67..5171e9c4ba 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricService.swift @@ -1,13 +1,14 @@ -import Foundation import Combine -import MarketKit +import Foundation import HsExtensions +import MarketKit class MarketGlobalTvlMetricService { typealias Item = DefiCoin private let marketKit: MarketKit.Kit private let currencyManager: CurrencyManager + private let apiTag: String private var tasks = Set() private var cancellables = Set() @@ -50,9 +51,10 @@ class MarketGlobalTvlMetricService { } } - init(marketKit: MarketKit.Kit, currencyManager: CurrencyManager) { + init(marketKit: MarketKit.Kit, currencyManager: CurrencyManager, apiTag: String) { self.marketKit = marketKit self.currencyManager = currencyManager + self.apiTag = apiTag syncDefiCoins() } @@ -64,9 +66,9 @@ class MarketGlobalTvlMetricService { internalState = .loading } - Task { [weak self, marketKit, currency] in + Task { [weak self, marketKit, currency, apiTag] in do { - let defiCoins = try await marketKit.defiCoins(currencyCode: currency.code) + let defiCoins = try await marketKit.defiCoins(currencyCode: currency.code, apiTag: apiTag) self?.internalState = .loaded(defiCoins: defiCoins) } catch { self?.internalState = .failed(error: error) @@ -78,21 +80,21 @@ class MarketGlobalTvlMetricService { switch internalState { case .loading: state = .loading - case .loaded(let defiCoins): + case let .loaded(defiCoins): let defiCoins = defiCoins - .filter { defiCoin in - switch marketPlatformField { - case .all: return true - default: return defiCoin.chains.contains(marketPlatformField.chain) - } - } - .sorted { lhsDefiCoin, rhsDefiCoin in - let lhsTvl = lhsDefiCoin.tvl(marketPlatformField: marketPlatformField) ?? 0 - let rhsTvl = rhsDefiCoin.tvl(marketPlatformField: marketPlatformField) ?? 0 - return sortDirectionAscending ? lhsTvl < rhsTvl : lhsTvl > rhsTvl + .filter { defiCoin in + switch marketPlatformField { + case .all: return true + default: return defiCoin.chains.contains(marketPlatformField.chain) } + } + .sorted { lhsDefiCoin, rhsDefiCoin in + let lhsTvl = lhsDefiCoin.tvl(marketPlatformField: marketPlatformField) ?? 0 + let rhsTvl = rhsDefiCoin.tvl(marketPlatformField: marketPlatformField) ?? 0 + return sortDirectionAscending ? lhsTvl < rhsTvl : lhsTvl > rhsTvl + } state = .loaded(items: defiCoins, softUpdate: false, reorder: reorder) - case .failed(let error): + case let .failed(error): state = .failed(error: error) } } @@ -113,22 +115,18 @@ class MarketGlobalTvlMetricService { } chartService.$interval - .sink { [weak self] in self?.priceChangePeriod = $0 } - .store(in: &cancellables) + .sink { [weak self] in self?.priceChangePeriod = $0 } + .store(in: &cancellables) } - } extension MarketGlobalTvlMetricService { - var currency: Currency { currencyManager.baseCurrency } - } extension MarketGlobalTvlMetricService: IMarketListService { - var statePublisher: AnyPublisher, Never> { $state } @@ -136,41 +134,34 @@ extension MarketGlobalTvlMetricService: IMarketListService { func refresh() { syncDefiCoins() } - } extension MarketGlobalTvlMetricService: IMarketListCoinUidService { - func coinUid(index: Int) -> String? { - guard case .loaded(let defiCoins, _, _) = state, index < defiCoins.count else { + guard case let .loaded(defiCoins, _, _) = state, index < defiCoins.count else { return nil } switch defiCoins[index].type { - case .fullCoin(let fullCoin): return fullCoin.coin.uid + case let .fullCoin(fullCoin): return fullCoin.coin.uid default: return nil } } - } extension MarketGlobalTvlMetricService { - private enum State { case loading case loaded(defiCoins: [DefiCoin]) case failed(error: Error) } - } extension DefiCoin { - func tvl(marketPlatformField: MarketModule.MarketPlatformField) -> Decimal? { switch marketPlatformField { case .all: return tvl default: return chainTvls[marketPlatformField.chain] } } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricViewController.swift index 55b823f1a5..903683a9f3 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketGlobalMetric/TvlInDefi/MarketGlobalTvlMetricViewController.swift @@ -1,10 +1,10 @@ -import UIKit -import SnapKit -import ThemeKit -import SectionsTableView import Chart import ComponentKit import RxSwift +import SectionsTableView +import SnapKit +import ThemeKit +import UIKit class MarketGlobalTvlMetricViewController: MarketListViewController { private let disposeBag = DisposeBag() @@ -29,17 +29,18 @@ class MarketGlobalTvlMetricViewController: MarketListViewController { chartCell = ChartCell(viewModel: chartViewModel, configuration: .baseChart) chartRow = StaticRow( - cell: chartCell, - id: "chartView", - height: chartCell.cellHeight + cell: chartCell, + id: "chartView", + height: chartCell.cellHeight ) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "market_global_tvl_metrics") sortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -68,21 +69,21 @@ class MarketGlobalTvlMetricViewController: MarketListViewController { return [ Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { [weak self] cell, _ in - self?.bind(cell: cell) - } - ) - ] + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { [weak self] cell, _ in + self?.bind(cell: cell) + } + ), + ] ), Section( - id: "chart", - rows: [chartRow] - ) + id: "chart", + rows: [chartRow] + ), ] } @@ -90,10 +91,9 @@ class MarketGlobalTvlMetricViewController: MarketListViewController { let metricsType: MarketGlobalModule.MetricsType = .tvlInDefi cell.set( - title: metricsType.title, - description: metricsType.description, - imageMode: .remote(imageUrl: metricsType.imageUid.headerImageUrl) + title: metricsType.title, + description: metricsType.description, + imageMode: .remote(imageUrl: metricsType.imageUid.headerImageUrl) ) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketList/MarketListViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketList/MarketListViewController.swift index f0c93d25ca..df4d45b753 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketList/MarketListViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketList/MarketListViewController.swift @@ -1,21 +1,22 @@ -import UIKit -import RxSwift -import RxCocoa -import ThemeKit -import SectionsTableView import ComponentKit import HUD +import RxCocoa +import RxSwift +import SectionsTableView +import ThemeKit +import UIKit protocol IMarketListViewModel { var viewItemDataDriver: Driver { get } var loadingDriver: Driver { get } var syncErrorDriver: Driver { get } - var scrollToTopSignal: Signal<()> { get } + var scrollToTopSignal: Signal { get } func refresh() } class MarketListViewController: ThemeViewController { private let listViewModel: IMarketListViewModel + private let apiTag: String private let disposeBag = DisposeBag() let tableView = SectionsTableView(style: .plain) @@ -29,10 +30,11 @@ class MarketListViewController: ThemeViewController { var headerView: UITableViewHeaderFooterView? { nil } var emptyView: UIView? { nil } var refreshEnabled: Bool { true } - func topSections(loaded: Bool) -> [SectionProtocol] { [] } + func topSections(loaded _: Bool) -> [SectionProtocol] { [] } - init(listViewModel: IMarketListViewModel) { + init(listViewModel: IMarketListViewModel, apiTag: String) { self.listViewModel = listViewModel + self.apiTag = apiTag super.init() @@ -43,7 +45,8 @@ class MarketListViewController: ThemeViewController { } } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -143,7 +146,7 @@ class MarketListViewController: ThemeViewController { } func onSelect(viewItem: MarketModule.ListViewItem) { - guard let uid = viewItem.uid, let module = CoinPageModule.viewController(coinUid: uid) else { + guard let uid = viewItem.uid, let module = CoinPageModule.viewController(coinUid: uid, apiTag: apiTag) else { HudHelper.instance.show(banner: .attention(string: "market.project_has_no_coin".localized)) return } @@ -162,7 +165,7 @@ class MarketListViewController: ThemeViewController { let type: RowActionType let iconName: String - let action: (UITableViewCell?) -> () + let action: (UITableViewCell?) -> Void if isFavorite { type = .destructive @@ -180,9 +183,9 @@ class MarketListViewController: ThemeViewController { return [ RowAction( - pattern: .icon(image: UIImage(named: iconName)?.withTintColor(type.iconColor), background: type.backgroundColor), - action: action - ) + pattern: .icon(image: UIImage(named: iconName)?.withTintColor(type.iconColor), background: type.backgroundColor), + action: action + ), ] } @@ -197,11 +200,9 @@ class MarketListViewController: ThemeViewController { func showRemovedFromWatchlist() { HudHelper.instance.show(banner: .removedFromWatchlist) } - } extension MarketListViewController: SectionsDataSource { - func buildSections() -> [SectionProtocol] { let headerState: ViewState @@ -213,28 +214,27 @@ extension MarketListViewController: SectionsDataSource { return topSections(loaded: viewItems != nil) + [ Section( - id: "coins", - headerState: headerState, - footerState: .marginColor(height: .margin32, color: .clear) , - rows: viewItems.map { viewItems in - viewItems.enumerated().map { index, viewItem in - MarketModule.marketListCell( - tableView: tableView, - backgroundStyle: .transparent, - listViewItem: viewItem, - isFirst: false, - isLast: index == viewItems.count - 1, - rowActionProvider: { [weak self] in - self?.rowActions(index: index) ?? [] - }, - action: { [weak self] in - self?.onSelect(viewItem: viewItem) - } - ) - } - } ?? [] - ) + id: "coins", + headerState: headerState, + footerState: .marginColor(height: .margin32, color: .clear), + rows: viewItems.map { viewItems in + viewItems.enumerated().map { index, viewItem in + MarketModule.marketListCell( + tableView: tableView, + backgroundStyle: .transparent, + listViewItem: viewItem, + isFirst: false, + isLast: index == viewItems.count - 1, + rowActionProvider: { [weak self] in + self?.rowActions(index: index) ?? [] + }, + action: { [weak self] in + self?.onSelect(viewItem: viewItem) + } + ) + } + } ?? [] + ), ] } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketModule.swift index 7138df4034..ee009052d4 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketModule.swift @@ -1,10 +1,10 @@ +import ComponentKit import Foundation -import UIKit -import ThemeKit +import Kingfisher import MarketKit -import ComponentKit import SectionsTableView -import Kingfisher +import ThemeKit +import UIKit enum RowActionType { case additive @@ -25,82 +25,81 @@ enum RowActionType { } } -struct MarketModule { - +enum MarketModule { static func viewController() -> UIViewController { let service = MarketService(userDefaultsStorage: App.shared.userDefaultsStorage, launchScreenManager: App.shared.launchScreenManager) let viewModel = MarketViewModel(service: service) return MarketViewController(viewModel: viewModel) } - static func marketListCell(tableView: UITableView, backgroundStyle: BaseThemeCell.BackgroundStyle, listViewItem: MarketModule.ListViewItem, isFirst: Bool, isLast: Bool, rowActionProvider: (() -> [RowAction])?, action: (() -> ())?) -> RowProtocol { + static func marketListCell(tableView: UITableView, backgroundStyle: BaseThemeCell.BackgroundStyle, listViewItem: MarketModule.ListViewItem, isFirst: Bool, isLast: Bool, rowActionProvider: (() -> [RowAction])?, action: (() -> Void)?) -> RowProtocol { CellBuilderNew.row( - rootElement: .hStack([ - .image32 { component in - component.imageView.contentMode = .scaleAspectFill - component.imageView.clipsToBounds = true - component.imageView.cornerRadius = listViewItem.iconShape.radius - component.imageView.layer.cornerCurve = .continuous - component.imageView.kf.setImage( - with: URL(string: listViewItem.iconUrl), - placeholder: UIImage(named: listViewItem.iconPlaceholderName), - options: [.onlyLoadFirstFrame] - ) - }, - .vStackCentered([ - .hStack([ - .text { component in - component.font = .body - component.textColor = .themeLeah - component.text = listViewItem.leftPrimaryValue - }, - .text { component in - component.font = .body - component.textColor = .themeLeah - component.textAlignment = .right - component.setContentCompressionResistancePriority(.required, for: .horizontal) - component.text = listViewItem.rightPrimaryValue - } - ]), - .margin(1), - .hStack([ - .badge { component in - if let badge = listViewItem.badge { - component.isHidden = false - component.badgeView.set(style: .small) - component.badgeView.text = badge - component.badgeView.change = listViewItem.badgeSecondaryValue - } else { - component.isHidden = true - } - }, - .margin4, - .text { component in - component.font = .subhead2 - component.textColor = .themeGray - component.text = listViewItem.leftSecondaryValue - }, - .text { component in - component.setContentCompressionResistancePriority(.required, for: .horizontal) - component.setContentHuggingPriority(.required, for: .horizontal) - component.textAlignment = .right - let marketFieldData = marketFieldPreference(dataValue: listViewItem.rightSecondaryValue) - component.font = .subhead2 - component.textColor = marketFieldData.color - component.text = marketFieldData.value + rootElement: .hStack([ + .image32 { component in + component.imageView.contentMode = .scaleAspectFill + component.imageView.clipsToBounds = true + component.imageView.cornerRadius = listViewItem.iconShape.radius + component.imageView.layer.cornerCurve = .continuous + component.imageView.kf.setImage( + with: URL(string: listViewItem.iconUrl), + placeholder: UIImage(named: listViewItem.iconPlaceholderName), + options: [.onlyLoadFirstFrame] + ) + }, + .vStackCentered([ + .hStack([ + .text { component in + component.font = .body + component.textColor = .themeLeah + component.text = listViewItem.leftPrimaryValue + }, + .text { component in + component.font = .body + component.textColor = .themeLeah + component.textAlignment = .right + component.setContentCompressionResistancePriority(.required, for: .horizontal) + component.text = listViewItem.rightPrimaryValue + }, + ]), + .margin(1), + .hStack([ + .badge { component in + if let badge = listViewItem.badge { + component.isHidden = false + component.badgeView.set(style: .small) + component.badgeView.text = badge + component.badgeView.change = listViewItem.badgeSecondaryValue + } else { + component.isHidden = true } - ]) - ]) + }, + .margin4, + .text { component in + component.font = .subhead2 + component.textColor = .themeGray + component.text = listViewItem.leftSecondaryValue + }, + .text { component in + component.setContentCompressionResistancePriority(.required, for: .horizontal) + component.setContentHuggingPriority(.required, for: .horizontal) + component.textAlignment = .right + let marketFieldData = marketFieldPreference(dataValue: listViewItem.rightSecondaryValue) + component.font = .subhead2 + component.textColor = marketFieldData.color + component.text = marketFieldData.value + }, + ]), ]), - tableView: tableView, - id: "\(listViewItem.uid ?? "")-\(listViewItem.leftPrimaryValue)", - height: .heightDoubleLineCell, - autoDeselect: true, - rowActionProvider: rowActionProvider, - bind: { cell in - cell.set(backgroundStyle: backgroundStyle, isFirst: isFirst, isLast: isLast) - }, - action: action + ]), + tableView: tableView, + id: "\(listViewItem.uid ?? "")-\(listViewItem.leftPrimaryValue)", + height: .heightDoubleLineCell, + autoDeselect: true, + rowActionProvider: rowActionProvider, + bind: { cell in + cell.set(backgroundStyle: backgroundStyle, isFirst: isFirst, isLast: isLast) + }, + action: action ) } @@ -110,7 +109,7 @@ struct MarketModule { let color: UIColor switch dataValue { - case .valueDiff(let currencyValue, let diff): + case let .valueDiff(currencyValue, diff): title = nil if let currencyValue = currencyValue, let diff = diff { @@ -121,7 +120,7 @@ struct MarketModule { value = "----" color = .themeGray50 } - case .diff(let diff): + case let .diff(diff): title = nil value = diff.flatMap { ValueFormatter.instance.format(percentValue: $0) } ?? "----" if let diff = diff { @@ -129,11 +128,11 @@ struct MarketModule { } else { color = .themeGray50 } - case .volume(let volume): + case let .volume(volume): title = "market.top.volume.title".localized value = volume color = .themeGray - case .marketCap(let marketCap): + case let .marketCap(marketCap): title = "market.top.market_cap.title".localized value = marketCap color = .themeGray @@ -141,11 +140,9 @@ struct MarketModule { return (title: title, value: value, color: color) } - } extension MarketModule { - enum Tab: Int, CaseIterable { case overview case posts @@ -178,6 +175,17 @@ extension MarketModule { case .topLosers: return "market.top.top_losers".localized } } + + var raw: String { + switch self { + case .highestCap: return "highestCap" + case .lowestCap: return "lowestCap" + case .highestVolume: return "highestVolume" + case .lowestVolume: return "lowestVolume" + case .topGainers: return "topGainers" + case .topLosers: return "topLosers" + } + } } enum MarketField: Int, CaseIterable { @@ -192,6 +200,14 @@ extension MarketModule { case .volume: return "market.market_field.vol".localized } } + + var raw: String { + switch self { + case .price: return "price" + case .marketCap: return "marketCap" + case .volume: return "volume" + } + } } enum MarketTop: Int, CaseIterable { @@ -200,7 +216,7 @@ extension MarketModule { case top300 = 300 var title: String { - "\(self.rawValue)" + "\(rawValue)" } } @@ -268,11 +284,9 @@ extension MarketModule { } } } - } extension MarketKit.MarketInfo { - func priceChangeValue(type: MarketModule.PriceChangeType) -> Decimal? { switch type { case .day: return priceChange24h @@ -283,11 +297,9 @@ extension MarketKit.MarketInfo { case .year: return priceChange1y } } - } extension Array where Element == MarketKit.MarketInfo { - func sorted(sortingField: MarketModule.SortingField, priceChangeType: MarketModule.PriceChangeType) -> [MarketKit.MarketInfo] { sorted { lhsMarketInfo, rhsMarketInfo in switch sortingField { @@ -307,11 +319,9 @@ extension Array where Element == MarketKit.MarketInfo { } } } - } -extension MarketModule { // ViewModel Items - +extension MarketModule { // ViewModel Items enum MarketDataValue { case valueDiff(CurrencyValue?, Decimal?) case diff(Decimal?) @@ -354,7 +364,5 @@ extension MarketModule { // ViewModel Items case .full: return 0 } } - } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketNftTopCollections/MarketNftTopCollectionsViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketNftTopCollections/MarketNftTopCollectionsViewController.swift index f61290a551..980543234b 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketNftTopCollections/MarketNftTopCollectionsViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketNftTopCollections/MarketNftTopCollectionsViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class MarketNftTopCollectionsViewController: MarketListViewController { private let viewModel: MarketNftTopCollectionsViewModel @@ -15,12 +15,13 @@ class MarketNftTopCollectionsViewController: MarketListViewController { self.viewModel = viewModel multiSortHeaderView = MarketMultiSortHeaderView(viewModel: headerViewModel) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,24 +38,24 @@ class MarketNftTopCollectionsViewController: MarketListViewController { dismiss(animated: true) } - override func topSections(loaded: Bool) -> [SectionProtocol] { + override func topSections(loaded _: Bool) -> [SectionProtocol] { [ Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { cell, _ in - cell.set( - title: "top_nft_collections.title".localized, - description: "top_nft_collections.description".localized, - imageMode: .remote(imageUrl: "nft".headerImageUrl) - ) - } - ) - ] - ) + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { cell, _ in + cell.set( + title: "top_nft_collections.title".localized, + description: "top_nft_collections.description".localized, + imageMode: .remote(imageUrl: "nft".headerImageUrl) + ) + } + ), + ] + ), ] } @@ -67,5 +68,4 @@ class MarketNftTopCollectionsViewController: MarketListViewController { present(ThemeNavigationController(rootViewController: module), animated: true) } } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/Category/MarketOverviewCategoryDataSource.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/Category/MarketOverviewCategoryDataSource.swift index c3bf9157fd..2760e599b7 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/Category/MarketOverviewCategoryDataSource.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/Category/MarketOverviewCategoryDataSource.swift @@ -1,7 +1,7 @@ -import UIKit -import RxSwift import RxCocoa +import RxSwift import SectionsTableView +import UIKit class MarketOverviewCategoryDataSource { private let viewModel: MarketOverviewCategoryViewModel @@ -25,32 +25,22 @@ class MarketOverviewCategoryDataSource { return } - let viewController = MarketCategoryModule.viewController(category: category) + let viewController = MarketCategoryModule.viewController(category: category, apiTag: "market_overview") self?.presentDelegate?.present(viewController: viewController) } } - - private func onSelect(listViewItem: MarketModule.ListViewItem) { - guard let uid = listViewItem.uid, let module = CoinPageModule.viewController(coinUid: uid) else { - return - } - - presentDelegate?.present(viewController: module) - } - } extension MarketOverviewCategoryDataSource: IMarketOverviewDataSource { - var isReady: Bool { viewItemsRelay.value != nil } - var updateObservable: Observable<()> { + var updateObservable: Observable { viewItemsRelay.map { _ in () } } - func sections(tableView: SectionsTableView) -> [SectionProtocol] { + func sections(tableView _: SectionsTableView) -> [SectionProtocol] { guard let viewItems = viewItemsRelay.value else { return [] } @@ -59,39 +49,38 @@ extension MarketOverviewCategoryDataSource: IMarketOverviewDataSource { return [ Section( - id: "categories_header", - footerState: .margin(height: .margin8), - rows: [ - Row( - id: "categories_header_cell", - height: .heightCell48, - bind: { [weak self] cell, _ in - cell.set(backgroundStyle: .transparent) - - cell.buttonMode = .seeAll - let onSeeAll: () -> () = { [weak self] in - self?.presentDelegate?.push(viewController: MarketDiscoveryModule.viewController()) - } - cell.onSeeAll = onSeeAll - cell.onTapTitle = onSeeAll - - cell.titleImage = UIImage(named: "categories_20") - cell.title = "market.top.section.header.top_sectors".localized - } - ) - ] + id: "categories_header", + footerState: .margin(height: .margin8), + rows: [ + Row( + id: "categories_header_cell", + height: .heightCell48, + bind: { [weak self] cell, _ in + cell.set(backgroundStyle: .transparent) + + cell.buttonMode = .seeAll + let onSeeAll: () -> Void = { [weak self] in + self?.presentDelegate?.push(viewController: MarketDiscoveryModule.viewController()) + } + cell.onSeeAll = onSeeAll + cell.onTapTitle = onSeeAll + + cell.titleImage = UIImage(named: "categories_20") + cell.title = "market.top.section.header.top_sectors".localized + } + ), + ] ), Section( - id: "categories", - rows: [ - StaticRow( - cell: categoryCell, - id: "metrics", - height: MarketOverviewCategoryCell.cellHeight - ) - ] - ) + id: "categories", + rows: [ + StaticRow( + cell: categoryCell, + id: "metrics", + height: MarketOverviewCategoryCell.cellHeight + ), + ] + ), ] } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/MarketOverviewService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/MarketOverviewService.swift index 934ebe25a5..daaa410a5a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/MarketOverviewService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/MarketOverviewService.swift @@ -1,9 +1,9 @@ -import Foundation import Combine -import RxSwift -import RxRelay -import MarketKit +import Foundation import HsExtensions +import MarketKit +import RxRelay +import RxSwift class MarketOverviewService { private let marketKit: MarketKit.Kit @@ -42,21 +42,19 @@ class MarketOverviewService { } }.store(in: &tasks) } - } extension MarketOverviewService { - var currency: Currency { currencyManager.baseCurrency } func load() { currencyManager.$baseCurrency - .sink { [weak self] _ in - self?.syncState() - } - .store(in: &cancellables) + .sink { [weak self] _ in + self?.syncState() + } + .store(in: &cancellables) subscribe(disposeBag, appManager.willEnterForegroundObservable) { [weak self] in self?.syncState() } @@ -66,14 +64,11 @@ extension MarketOverviewService { func refresh() { syncState() } - } extension MarketOverviewService { - struct Item { let marketOverview: MarketOverview let topMovers: TopMovers } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopCoins/MarketOverviewTopCoinsDataSource.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopCoins/MarketOverviewTopCoinsDataSource.swift index 0c8f82e8ab..c4c851917e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopCoins/MarketOverviewTopCoinsDataSource.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopCoins/MarketOverviewTopCoinsDataSource.swift @@ -19,27 +19,26 @@ class MarketOverviewTopCoinsDataSource: BaseMarketOverviewTopListDataSource { } super.init( - topListViewModel: viewModel, - presentDelegate: presentDelegate, - rightSelectorMode: .selector, - imageName: imageName, - title: title + topListViewModel: viewModel, + presentDelegate: presentDelegate, + rightSelectorMode: .selector, + imageName: imageName, + title: title ) } override func didTapSeeAll() { let module = MarketTopModule.viewController( - marketTop: viewModel.marketTop, - sortingField: viewModel.listType.sortingField, - marketField: viewModel.listType.marketField + marketTop: viewModel.marketTop, + sortingField: viewModel.listType.sortingField, + marketField: viewModel.listType.marketField ) presentDelegate?.present(viewController: module) } override func onSelect(listViewItem: MarketModule.ListViewItem) { - if let uid = listViewItem.uid, let module = CoinPageModule.viewController(coinUid: uid) { + if let uid = listViewItem.uid, let module = CoinPageModule.viewController(coinUid: uid, apiTag: "market_overview_top_coins") { presentDelegate?.present(viewController: module) } } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopPlatforms/MarketOverviewTopPlatformsDataSource.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopPlatforms/MarketOverviewTopPlatformsDataSource.swift index d5178182e1..4d01547c0e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopPlatforms/MarketOverviewTopPlatformsDataSource.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketOverview/TopPlatforms/MarketOverviewTopPlatformsDataSource.swift @@ -7,11 +7,11 @@ class MarketOverviewTopPlatformsDataSource: BaseMarketOverviewTopListDataSource self.viewModel = viewModel super.init( - topListViewModel: viewModel, - presentDelegate: presentDelegate, - rightSelectorMode: .selector, - imageName: "blocks_24", - title: "market.top.top_platforms".localized + topListViewModel: viewModel, + presentDelegate: presentDelegate, + rightSelectorMode: .selector, + imageName: "blocks_24", + title: "market.top.top_platforms".localized ) } @@ -25,7 +25,6 @@ class MarketOverviewTopPlatformsDataSource: BaseMarketOverviewTopListDataSource return } - presentDelegate?.present(viewController: TopPlatformModule.viewController(topPlatform: topPlatform)) + presentDelegate?.present(viewController: TopPlatformModule.viewController(topPlatform: topPlatform, apiTag: "market_overview")) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopModule.swift index beb0e2fc3d..19ffc1a3af 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopModule.swift @@ -1,19 +1,18 @@ -import UIKit import ThemeKit +import UIKit struct MarketTopModule { - static func viewController(marketTop: MarketModule.MarketTop = .top100, sortingField: MarketModule.SortingField = .highestCap, marketField: MarketModule.MarketField = .price) -> UIViewController { let service = MarketTopService( - marketKit: App.shared.marketKit, - currencyManager: App.shared.currencyManager, - marketTop: marketTop, - sortingField: sortingField, - marketField: marketField + marketKit: App.shared.marketKit, + currencyManager: App.shared.currencyManager, + marketTop: marketTop, + sortingField: sortingField, + marketField: marketField ) let watchlistToggleService = MarketWatchlistToggleService( - coinUidService: service, - favoritesManager: App.shared.favoritesManager + coinUidService: service, + favoritesManager: App.shared.favoritesManager ) let decorator = MarketListMarketFieldDecorator(service: service) @@ -24,5 +23,4 @@ struct MarketTopModule { return ThemeNavigationController(rootViewController: viewController) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopService.swift index 72d3f2a770..ef41ed1329 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopService.swift @@ -1,8 +1,8 @@ import Combine -import RxSwift -import RxRelay -import MarketKit import HsExtensions +import MarketKit +import RxRelay +import RxSwift class MarketTopService: IMarketMultiSortHeaderService { typealias Item = MarketInfo @@ -33,6 +33,7 @@ class MarketTopService: IMarketMultiSortHeaderService { } let initialMarketFieldIndex: Int + private let apiTag: String init(marketKit: MarketKit.Kit, currencyManager: CurrencyManager, marketTop: MarketModule.MarketTop, sortingField: MarketModule.SortingField, marketField: MarketModule.MarketField) { self.marketKit = marketKit @@ -41,6 +42,8 @@ class MarketTopService: IMarketMultiSortHeaderService { self.sortingField = sortingField initialMarketFieldIndex = marketField.rawValue + apiTag = "market_top_\(marketTop.rawValue)_\(sortingField.raw)_\(marketField.raw)" + syncMarketInfos() } @@ -51,9 +54,9 @@ class MarketTopService: IMarketMultiSortHeaderService { internalState = .loading } - Task { [weak self, marketKit, currency] in + Task { [weak self, marketKit, currency, apiTag] in do { - let marketInfos = try await marketKit.marketInfos(top: 1000, currencyCode: currency.code) + let marketInfos = try await marketKit.marketInfos(top: 1000, currencyCode: currency.code, apiTag: apiTag) self?.internalState = .loaded(marketInfos: marketInfos) } catch { self?.internalState = .failed(error: error) @@ -65,10 +68,10 @@ class MarketTopService: IMarketMultiSortHeaderService { switch internalState { case .loading: state = .loading - case .loaded(let marketInfos): + case let .loaded(marketInfos): let marketInfos: [MarketInfo] = Array(marketInfos.prefix(marketTop.rawValue)) state = .loaded(items: marketInfos.sorted(sortingField: sortingField, priceChangeType: priceChangeType), softUpdate: false, reorder: reorder) - case .failed(let error): + case let .failed(error): state = .failed(error: error) } } @@ -80,11 +83,9 @@ class MarketTopService: IMarketMultiSortHeaderService { syncState(reorder: true) } - } extension MarketTopService: IMarketListService { - var statePublisher: AnyPublisher, Never> { $state } @@ -92,23 +93,19 @@ extension MarketTopService: IMarketListService { func refresh() { syncMarketInfos() } - } extension MarketTopService: IMarketListCoinUidService { - func coinUid(index: Int) -> String? { - guard case .loaded(let marketInfos, _, _) = state, index < marketInfos.count else { + guard case let .loaded(marketInfos, _, _) = state, index < marketInfos.count else { return nil } return marketInfos[index].fullCoin.coin.uid } - } extension MarketTopService: IMarketListDecoratorService { - var currency: Currency { currencyManager.baseCurrency } @@ -117,20 +114,17 @@ extension MarketTopService: IMarketListDecoratorService { .day } - func onUpdate(marketFieldIndex: Int) { - if case .loaded(let marketInfos, _, _) = state { + func onUpdate(marketFieldIndex _: Int) { + if case let .loaded(marketInfos, _, _) = state { state = .loaded(items: marketInfos, softUpdate: false, reorder: false) } } - } extension MarketTopService { - private enum State { case loading case loaded(marketInfos: [MarketInfo]) case failed(error: Error) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopViewController.swift index e6f74d5e85..9f90e5af20 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTop/MarketTopViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class MarketTopViewController: MarketListViewController { private let multiSortHeaderView: MarketMultiSortHeaderView @@ -13,12 +13,13 @@ class MarketTopViewController: MarketListViewController { init(listViewModel: IMarketListViewModel, headerViewModel: MarketMultiSortHeaderViewModel) { multiSortHeaderView = MarketMultiSortHeaderView(viewModel: headerViewModel, hasLeftSelector: true) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "market_top") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -35,25 +36,24 @@ class MarketTopViewController: MarketListViewController { dismiss(animated: true) } - override func topSections(loaded: Bool) -> [SectionProtocol] { + override func topSections(loaded _: Bool) -> [SectionProtocol] { [ Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { cell, _ in - cell.set( - title: "market.top.title".localized, - description: "market.top.description".localized, - imageMode: .remote(imageUrl: "top_coins".headerImageUrl) - ) - } - ) - ] - ) + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { cell, _ in + cell.set( + title: "market.top.title".localized, + description: "market.top.description".localized, + imageMode: .remote(imageUrl: "top_coins".headerImageUrl) + ) + } + ), + ] + ), ] } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTopPlatforms/MarketTopPlatformsViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTopPlatforms/MarketTopPlatformsViewController.swift index 3eac515019..56a8bab4d5 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTopPlatforms/MarketTopPlatformsViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketTopPlatforms/MarketTopPlatformsViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class MarketTopPlatformsViewController: MarketListViewController { private let viewModel: MarketTopPlatformsViewModel @@ -15,12 +15,13 @@ class MarketTopPlatformsViewController: MarketListViewController { self.viewModel = viewModel multiSortHeaderView = MarketMultiSortHeaderView(viewModel: headerViewModel) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,24 +38,24 @@ class MarketTopPlatformsViewController: MarketListViewController { dismiss(animated: true) } - override func topSections(loaded: Bool) -> [SectionProtocol] { + override func topSections(loaded _: Bool) -> [SectionProtocol] { [ Section( - id: "header", - rows: [ - Row( - id: "header", - height: MarketHeaderCell.height, - bind: { cell, _ in - cell.set( - title: "top_platforms.title".localized, - description: "top_platforms.description".localized, - imageMode: .remote(imageUrl: "top_platforms".headerImageUrl) - ) - } - ) - ] - ) + id: "header", + rows: [ + Row( + id: "header", + height: MarketHeaderCell.height, + bind: { cell, _ in + cell.set( + title: "top_platforms.title".localized, + description: "top_platforms.description".localized, + imageMode: .remote(imageUrl: "top_platforms".headerImageUrl) + ) + } + ), + ] + ), ] } @@ -63,7 +64,6 @@ class MarketTopPlatformsViewController: MarketListViewController { return } - present(TopPlatformModule.viewController(topPlatform: topPlatform), animated: true) + present(TopPlatformModule.viewController(topPlatform: topPlatform, apiTag: "market_top_platforms"), animated: true) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistService.swift index 6f4166cfac..2d846635c6 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistService.swift @@ -1,8 +1,8 @@ import Combine -import RxSwift -import RxRelay -import MarketKit import HsExtensions +import MarketKit +import RxRelay +import RxSwift class MarketWatchlistService: IMarketMultiSortHeaderService { typealias Item = MarketInfo @@ -47,7 +47,7 @@ class MarketWatchlistService: IMarketMultiSortHeaderService { private func syncCoinUids() { coinUids = favoritesManager.allCoinUids - if case .loaded(let marketInfos, _, _) = state { + if case let .loaded(marketInfos, _, _) = state { let newMarketInfos = marketInfos.filter { marketInfo in coinUids.contains(marketInfo.fullCoin.coin.uid) } @@ -75,7 +75,7 @@ class MarketWatchlistService: IMarketMultiSortHeaderService { Task { [weak self, marketKit, coinUids, currency] in do { - let marketInfos = try await marketKit.marketInfos(coinUids: coinUids, currencyCode: currency.code) + let marketInfos = try await marketKit.marketInfos(coinUids: coinUids, currencyCode: currency.code, apiTag: "watchlist") self?.sync(marketInfos: marketInfos) } catch { self?.state = .failed(error: error) @@ -88,27 +88,25 @@ class MarketWatchlistService: IMarketMultiSortHeaderService { } private func syncIfPossible() { - guard case .loaded(let marketInfos, _, _) = state else { + guard case let .loaded(marketInfos, _, _) = state else { return } sync(marketInfos: marketInfos, reorder: true) } - } extension MarketWatchlistService: IMarketListService { - var statePublisher: AnyPublisher, Never> { $state } func load() { currencyManager.$baseCurrency - .sink { [weak self] _ in - self?.syncMarketInfos() - } - .store(in: &cancellables) + .sink { [weak self] _ in + self?.syncMarketInfos() + } + .store(in: &cancellables) subscribe(disposeBag, favoritesManager.coinUidsUpdatedObservable) { [weak self] in self?.syncCoinUids() } subscribe(disposeBag, appManager.willEnterForegroundObservable) { [weak self] in self?.syncMarketInfos() } @@ -119,23 +117,19 @@ extension MarketWatchlistService: IMarketListService { func refresh() { syncMarketInfos() } - } extension MarketWatchlistService: IMarketListCoinUidService { - func coinUid(index: Int) -> String? { - guard case .loaded(let marketInfos, _, _) = state, index < marketInfos.count else { + guard case let .loaded(marketInfos, _, _) = state, index < marketInfos.count else { return nil } return marketInfos[index].fullCoin.coin.uid } - } extension MarketWatchlistService: IMarketListDecoratorService { - var initialMarketFieldIndex: Int { userDefaultsStorage.value(for: keyMarketField) ?? 0 } @@ -149,11 +143,10 @@ extension MarketWatchlistService: IMarketListDecoratorService { } func onUpdate(marketFieldIndex: Int) { - if case .loaded(let marketInfos, _, _) = state { + if case let .loaded(marketInfos, _, _) = state { state = .loaded(items: marketInfos, softUpdate: false, reorder: false) } userDefaultsStorage.set(value: marketFieldIndex, for: keyMarketField) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistViewController.swift index 12fc3b94a8..486419ea9c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/MarketWatchlist/MarketWatchlistViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class MarketWatchlistViewController: MarketListViewController { weak var parentNavigationController: UINavigationController? @@ -19,12 +19,13 @@ class MarketWatchlistViewController: MarketListViewController { self.viewModel = viewModel multiSortHeaderView = MarketMultiSortHeaderView(viewModel: headerViewModel, hasTopSeparator: false) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "market_watchlist") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,10 +38,7 @@ class MarketWatchlistViewController: MarketListViewController { viewModel.onLoad() } - override func showAddedToWatchlist() { - } - - override func showRemovedFromWatchlist() { - } + override func showAddedToWatchlist() {} + override func showRemovedFromWatchlist() {} } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformModule.swift index e501ba6369..3ed131c0af 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformModule.swift @@ -1,12 +1,11 @@ -import UIKit -import ThemeKit -import MarketKit import Chart +import MarketKit +import ThemeKit +import UIKit struct TopPlatformModule { - - static func viewController(topPlatform: TopPlatform) -> UIViewController { - let service = TopPlatformService(topPlatform: topPlatform, marketKit: App.shared.marketKit) + static func viewController(topPlatform: TopPlatform, apiTag: String) -> UIViewController { + let service = TopPlatformService(topPlatform: topPlatform, marketKit: App.shared.marketKit, apiTag: apiTag) let listService = MarketFilteredListService(currencyManager: App.shared.currencyManager, provider: service) let watchlistToggleService = MarketWatchlistToggleService(coinUidService: listService, favoritesManager: App.shared.favoritesManager) @@ -24,5 +23,4 @@ struct TopPlatformModule { return ThemeNavigationController(rootViewController: viewController) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformService.swift index 1cb062b88e..2a98d79c85 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformService.swift @@ -3,18 +3,17 @@ import MarketKit class TopPlatformService { let topPlatform: TopPlatform private let marketKit: MarketKit.Kit + private let apiTag: String - init(topPlatform: TopPlatform, marketKit: MarketKit.Kit) { + init(topPlatform: TopPlatform, marketKit: MarketKit.Kit, apiTag: String) { self.topPlatform = topPlatform self.marketKit = marketKit + self.apiTag = apiTag } - } extension TopPlatformService: IMarketFilteredListProvider { - - func marketInfo(currencyCode: String) async throws -> [MarketInfo] { - try await marketKit.topPlatformMarketInfos(blockchain: topPlatform.blockchain.uid, currencyCode: currencyCode) + func marketInfos(currencyCode: String) async throws -> [MarketInfo] { + try await marketKit.topPlatformMarketInfos(blockchain: topPlatform.blockchain.uid, currencyCode: currencyCode, apiTag: apiTag) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformViewController.swift index f622c1a973..e1dd139348 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Market/TopPlatform/TopPlatformViewController.swift @@ -1,7 +1,7 @@ -import UIKit +import SectionsTableView import SnapKit import ThemeKit -import SectionsTableView +import UIKit class TopPlatformViewController: MarketListViewController { private let viewModel: TopPlatformViewModel @@ -22,17 +22,18 @@ class TopPlatformViewController: MarketListViewController { chartCell = ChartCell(viewModel: chartViewModel, configuration: .baseChart) chartRow = StaticRow( - cell: chartCell, - id: "chartView", - height: chartCell.cellHeight + cell: chartCell, + id: "chartView", + height: chartCell.cellHeight ) - super.init(listViewModel: listViewModel) + super.init(listViewModel: listViewModel, apiTag: "market_top_platform") multiSortHeaderView.viewController = self } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -54,16 +55,16 @@ class TopPlatformViewController: MarketListViewController { override func topSections(loaded: Bool) -> [SectionProtocol] { var sections = [Section( - id: "header", - rows: [ - Row( - id: "header", - height: TopPlatformHeaderCell.height, - bind: { [weak self] cell, _ in - self?.bind(cell: cell) - } - ) - ] + id: "header", + rows: [ + Row( + id: "header", + height: TopPlatformHeaderCell.height, + bind: { [weak self] cell, _ in + self?.bind(cell: cell) + } + ), + ] )] if loaded { @@ -75,10 +76,9 @@ class TopPlatformViewController: MarketListViewController { private func bind(cell: TopPlatformHeaderCell) { cell.set( - title: viewModel.title, - description: viewModel.description, - imageUrl: viewModel.imageUrl + title: viewModel.title, + description: viewModel.description, + imageUrl: viewModel.imageUrl ) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift index fe445bf894..99b2142567 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift @@ -95,8 +95,6 @@ extension SendTonService: ISendService { return Single.error(SendTransactionError.wrongAmount) } - let memo = memoService.memo // todo - return Single.create { [adapter] observer in let task = Task { [adapter] in do { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/TransactionInfo/TransactionInfoViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/TransactionInfo/TransactionInfoViewController.swift index 12c8535453..b2b2c9c2d7 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/TransactionInfo/TransactionInfoViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/TransactionInfo/TransactionInfoViewController.swift @@ -87,7 +87,7 @@ class TransactionInfoViewController: ThemeViewController { } private func openCoin(coinUid: String) { - guard let module = CoinPageModule.viewController(coinUid: coinUid) else { + guard let module = CoinPageModule.viewController(coinUid: coinUid, apiTag: "transaction_info") else { return } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceDataSource.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceDataSource.swift index 571cf3a1de..b198e52dfb 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceDataSource.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceDataSource.swift @@ -209,7 +209,7 @@ class WalletTokenBalanceDataSource: NSObject { } private func openCoinPage(coin: Coin) { - if let viewController = CoinPageModule.viewController(coinUid: coin.uid) { + if let viewController = CoinPageModule.viewController(coinUid: coin.uid, apiTag: "wallet_token_balance") { parentViewController?.present(viewController, animated: true) } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/PendingRequest/WalletConnectMainPendingRequestService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/PendingRequest/WalletConnectMainPendingRequestService.swift index 076986679e..f78f280237 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/PendingRequest/WalletConnectMainPendingRequestService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/PendingRequest/WalletConnectMainPendingRequestService.swift @@ -94,7 +94,7 @@ extension WalletConnectMainPendingRequestService { let result = requestHandler.handle(session: session, request: request) switch result { case let .unsuccessful(error): - print("Cant select request because: \(error)") + print("Cant select request because: \(error?.localizedDescription ?? "nil")") case .handled: () case let .request(request): showPendingRequestRelay.accept(request) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/WalletConnectMainViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/WalletConnectMainViewController.swift index 1113580e54..155cbfa66e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/WalletConnectMainViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Main/WalletConnectMainViewController.swift @@ -219,7 +219,7 @@ class WalletConnectMainViewController: ThemeViewController { private func showPending(request: WalletConnectRequest) { let result = requestViewFactory.viewController(request: request) switch result { - case let .unsuccessful(error): + case .unsuccessful: print("Can't create view") return case let .controller(controller):