diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewController.swift index d82123d097..05bea222cf 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewController.swift @@ -1,12 +1,12 @@ -import UIKit -import RxSwift -import ThemeKit -import SectionsTableView -import SnapKit -import HUD import Chart import ComponentKit import Down +import HUD +import RxSwift +import SectionsTableView +import SnapKit +import ThemeKit +import UIKit class CoinOverviewViewController: ThemeViewController { private let viewModel: CoinOverviewViewModel @@ -44,15 +44,15 @@ class CoinOverviewViewController: ThemeViewController { chartCell = ChartCell(viewModel: chartViewModel, configuration: .coinChart) chartRow = StaticRow( - cell: chartCell, - id: "chartView", - height: chartCell.cellHeight + cell: chartCell, + id: "chartView", + height: chartCell.cellHeight ) chartConfigurationRow = StaticRow( - cell: chartConfigurationCell, - id: "chartConfiguration", - height: .heightCell48 + cell: chartConfigurationCell, + id: "chartConfiguration", + height: .heightCell48 ) super.init() @@ -60,7 +60,8 @@ class CoinOverviewViewController: ThemeViewController { hidesBottomBarWhenPushed = true } - required init?(coder aDecoder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -161,32 +162,32 @@ class CoinOverviewViewController: ThemeViewController { private func syncChartConfigurationCell() { CellBuilderNew.buildStatic( - cell: chartConfigurationCell, - rootElement: .hStack([ - .textElement(text: .body("coin_overview.indicators".localized)), - .secondaryButton { [weak self] component in - component.isHidden = false - component.button.set(style: .default) - let title = (self?.chartIndicatorShown ?? false) ? "coin_overview.indicators.hide".localized : "coin_overview.indicators.show".localized - component.button.setTitle(title, for: .normal) - component.onTap = { - self?.chartViewModel.onToggleIndicators() - } - }, - .margin(8), - .secondaryCircleButton { component in - component.isHidden = false - component.button.set(image: UIImage(named: "setting_20")) - component.button.isEnabled = true - component.onTap = { [weak self] in - self?.chartViewModel.onTapChartSettings() - } - }, - .image24 { component in - component.isHidden = true - component.imageView.image = UIImage(named: "lock_24") + cell: chartConfigurationCell, + rootElement: .hStack([ + .textElement(text: .body("coin_overview.indicators".localized)), + .secondaryButton { [weak self] component in + component.isHidden = false + component.button.set(style: .default) + let title = (self?.chartIndicatorShown ?? false) ? "coin_overview.indicators.hide".localized : "coin_overview.indicators.show".localized + component.button.setTitle(title, for: .normal) + component.onTap = { + self?.chartViewModel.onToggleIndicators() } - ]) + }, + .margin(8), + .secondaryCircleButton { component in + component.isHidden = false + component.button.set(image: UIImage(named: "setting_20")) + component.button.isEnabled = true + component.onTap = { [weak self] in + self?.chartViewModel.onTapChartSettings() + } + }, + .image24 { component in + component.isHidden = true + component.imageView.image = UIImage(named: "lock_24") + }, + ]) ) } @@ -200,67 +201,92 @@ class CoinOverviewViewController: ThemeViewController { private func openChartSettings() { parentNavigationController?.present(chartRouter.viewController(), animated: true) } - } extension CoinOverviewViewController { - - private func linkRow(id: String, image: String, title: String, isFirst: Bool, isLast: Bool, action: @escaping () -> ()) -> RowProtocol { + private func linkRow(id: String, image: String, title: String, isFirst: Bool, isLast: Bool, action: @escaping () -> Void) -> RowProtocol { tableView.universalRow48( - id: id, - image: .local(UIImage(named: image)?.withTintColor(.themeGray)), - title: .body(title), - accessoryType: .disclosure, - autoDeselect: true, - isFirst: isFirst, - isLast: isLast, - action: action + id: id, + image: .local(UIImage(named: image)?.withTintColor(.themeGray)), + title: .body(title), + accessoryType: .disclosure, + autoDeselect: true, + isFirst: isFirst, + isLast: isLast, + action: action ) } private func coinInfoSection(viewItem: CoinOverviewViewModel.CoinViewItem) -> SectionProtocol { Section( - id: "coin-info", - rows: [ - tableView.universalRow56( - id: "coin-info", - image: .url(viewItem.imageUrl, placeholder: viewItem.imagePlaceholderName), - title: .body(viewItem.name, color: .themeGray), - value: .subhead1(viewItem.marketCapRank, color: .themeGray), - backgroundStyle: .transparent, - isFirst: true, - isLast: false - ) - ] + id: "coin-info", + rows: [ + tableView.universalRow56( + id: "coin-info", + image: .url(viewItem.imageUrl, placeholder: viewItem.imagePlaceholderName), + title: .body(viewItem.name, color: .themeGray), + value: .subhead1(viewItem.marketCapRank, color: .themeGray), + backgroundStyle: .transparent, + isFirst: true, + isLast: false + ), + ] ) } private var chartSection: SectionProtocol { Section( - id: "chart", - rows: [chartRow] + id: "chart", + rows: [chartRow] ) } - private func descriptionSection(description: String) -> SectionProtocol { - var rows: [RowProtocol] = [ - tableView.subtitleRow(text: "chart.about.header".localized) - ] + private func descriptionSection(description: String) -> SectionProtocol? { + guard let attributedText = try? markdownParser.attributedString(from: description) else { + return nil + } - descriptionTextCell.contentText = try? markdownParser.attributedString(from: description) - rows.append( - StaticRow( - cell: descriptionTextCell, - id: "about_cell", - dynamicHeight: { [weak self] containerWidth in - self?.descriptionTextCell.cellHeight(containerWidth: containerWidth) ?? 0 - } - )) + let backgroundStyle: BaseThemeCell.BackgroundStyle = .lawrence + let layoutMargins = UIEdgeInsets(top: .margin12, left: .margin16, bottom: .margin12, right: .margin16) + + let descriptionWarning = "coin_overview.description_warning".localized + let descriptionWarningFont: UIFont = .subhead2 + let descriptionWarningPadding: CGFloat = .margin24 return Section( - id: "description", - headerState: .margin(height: .margin12), - rows: rows + id: "description", + headerState: .margin(height: .margin12), + rows: [ + tableView.subtitleRow(text: "coin_overview.overview".localized), + CellBuilderNew.row( + rootElement: .vStack([ + .text { component in + component.attributedText = attributedText + component.numberOfLines = 0 + }, + .margin(descriptionWarningPadding), + .text { component in + component.font = descriptionWarningFont + component.textColor = .themeJacob + component.numberOfLines = 0 + component.text = descriptionWarning + }, + ]), + layoutMargins: layoutMargins, + tableView: tableView, + id: "description", + dynamicHeight: { containerWidth in + let textWidth = containerWidth - BaseThemeCell.margin(backgroundStyle: backgroundStyle).width - layoutMargins.width + return attributedText.height(containerWidth: textWidth) + + descriptionWarningPadding + + descriptionWarning.height(forContainerWidth: textWidth, font: descriptionWarningFont) + + layoutMargins.height + }, + bind: { cell in + cell.set(backgroundStyle: backgroundStyle, isFirst: true, isLast: true) + } + ), + ] ) } @@ -271,35 +297,35 @@ extension CoinOverviewViewController { let isLast = links.isEmpty let guideRow = linkRow( - id: "guide", - image: "academy_1_24", - title: "coin_overview.guide".localized, - isFirst: true, - isLast: isLast, - action: { [weak self] in - let module = MarkdownModule.viewController(url: guideUrl) - self?.parentNavigationController?.pushViewController(module, animated: true) - } + id: "guide", + image: "academy_1_24", + title: "coin_overview.guide".localized, + isFirst: true, + isLast: isLast, + action: { [weak self] in + let module = MarkdownModule.viewController(url: guideUrl) + self?.parentNavigationController?.pushViewController(module, animated: true) + } ) guideRows.append(guideRow) } return Section( - id: "links", - headerState: .margin(height: .margin12), - rows: [tableView.subtitleRow(text: "coin_overview.links".localized)] + guideRows + links.enumerated().map { index, link in - linkRow( - id: link.title, - image: link.iconName, - title: link.title, - isFirst: guideRows.isEmpty && index == 0, - isLast: index == links.count - 1, - action: { [weak self] in - self?.openLink(url: link.url) - } - ) - } + id: "links", + headerState: .margin(height: .margin12), + rows: [tableView.subtitleRow(text: "coin_overview.links".localized)] + guideRows + links.enumerated().map { index, link in + linkRow( + id: link.title, + image: link.iconName, + title: link.title, + isFirst: guideRows.isEmpty && index == 0, + isLast: index == links.count - 1, + action: { [weak self] in + self?.openLink(url: link.url) + } + ) + } ) } @@ -318,63 +344,42 @@ extension CoinOverviewViewController { private func poweredBySection(text: String) -> SectionProtocol { Section( - id: "powered-by", - headerState: .margin(height: .margin32), - rows: [ - Row( - id: "powered-by", - dynamicHeight: { containerWidth in - BrandFooterCell.height(containerWidth: containerWidth, title: text) - }, - bind: { cell, _ in - cell.title = text - } - ) - ] + id: "powered-by", + headerState: .margin(height: .margin32), + rows: [ + Row( + id: "powered-by", + dynamicHeight: { containerWidth in + BrandFooterCell.height(containerWidth: containerWidth, title: text) + }, + bind: { cell, _ in + cell.title = text + } + ), + ] ) } private func performanceSection(viewItems: [[CoinOverviewViewModel.PerformanceViewItem]]) -> SectionProtocol { Section( - id: "return_of_investments_section", - headerState: .margin(height: .margin12), - rows: [ - Row( - id: "return_of_investments_cell", - dynamicHeight: { _ in - PerformanceTableViewCell.height(viewItems: viewItems) - }, - bind: { cell, _ in - cell.bind(viewItems: viewItems) - } - ) - ] - ) - } - - private func categoriesSection(categories: [String]) -> SectionProtocol { - let text = categories.joined(separator: ", ") - - return Section( - id: "categories", - headerState: .margin(height: .margin12), - rows: [ - tableView.subtitleRow(text: "coin_overview.category".localized), - Row( - id: "categories", - dynamicHeight: { width in - TextCell.height(containerWidth: width, text: text) - }, - bind: { cell, _ in - cell.contentText = text - } - ) - ] + id: "return_of_investments_section", + headerState: .margin(height: .margin12), + rows: [ + Row( + id: "return_of_investments_cell", + dynamicHeight: { _ in + PerformanceTableViewCell.height(viewItems: viewItems) + }, + bind: { cell, _ in + cell.bind(viewItems: viewItems) + } + ), + ] ) } private func typeRow(viewItem: CoinOverviewViewModel.TypeViewItem, index: Int, isFirst: Bool, isLast: Bool) -> RowProtocol { - var action: (() -> ())? + var action: (() -> Void)? if let reference = viewItem.reference { action = { @@ -383,116 +388,116 @@ extension CoinOverviewViewController { } return CellBuilderNew.row( - rootElement: .hStack([ - .imageElement(image: .url(viewItem.iconUrl, placeholder: "placeholder_rectangle_32"), size: .image32), - .vStackCentered([ - .textElement(text: .body(viewItem.title)), - .margin(1), - .textElement(text: .subhead2(viewItem.subtitle), parameters: .truncatingMiddle) - ]), - .secondaryCircleButton { [weak self] component in - component.isHidden = !viewItem.showAdd - component.button.set(image: UIImage(named: "add_to_wallet_2_20")) - component.onTap = { - self?.viewModel.onTapAddToWallet(index: index) - } - }, - .secondaryCircleButton { [weak self] component in - component.isHidden = !viewItem.showAdded - component.button.set(image: UIImage(named: "filled_wallet_20")) - component.button.isSelected = true + rootElement: .hStack([ + .imageElement(image: .url(viewItem.iconUrl, placeholder: "placeholder_rectangle_32"), size: .image32), + .vStackCentered([ + .textElement(text: .body(viewItem.title)), + .margin(1), + .textElement(text: .subhead2(viewItem.subtitle), parameters: .truncatingMiddle), + ]), + .secondaryCircleButton { [weak self] component in + component.isHidden = !viewItem.showAdd + component.button.set(image: UIImage(named: "add_to_wallet_2_20")) + component.onTap = { + self?.viewModel.onTapAddToWallet(index: index) + } + }, + .secondaryCircleButton { [weak self] component in + component.isHidden = !viewItem.showAdded + component.button.set(image: UIImage(named: "filled_wallet_20")) + component.button.isSelected = true + component.onTap = { + self?.viewModel.onTapAddedToWallet(index: index) + } + }, + .secondaryCircleButton { [weak self] component in + if let explorerUrl = viewItem.explorerUrl { + component.isHidden = false + component.button.set(image: UIImage(named: "globe_20")) component.onTap = { - self?.viewModel.onTapAddedToWallet(index: index) - } - }, - .secondaryCircleButton { [weak self] component in - if let explorerUrl = viewItem.explorerUrl { - component.isHidden = false - component.button.set(image: UIImage(named: "globe_20")) - component.onTap = { - self?.urlManager.open(url: explorerUrl, from: self?.parentNavigationController) - } - } else { - component.isHidden = true + self?.urlManager.open(url: explorerUrl, from: self?.parentNavigationController) } + } else { + component.isHidden = true } - ]), - tableView: tableView, - id: "type-\(index)", - height: .heightDoubleLineCell, - autoDeselect: true, - bind: { cell in - cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) }, - action: action + ]), + tableView: tableView, + id: "type-\(index)", + height: .heightDoubleLineCell, + autoDeselect: true, + bind: { cell in + cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) + }, + action: action ) } private func typesSection(typesViewItem: CoinOverviewViewModel.TypesViewItem) -> SectionProtocol { var rows: [RowProtocol] = [ - tableView.subtitleRow(text: typesViewItem.title) + tableView.subtitleRow(text: typesViewItem.title), ] for (index, viewItem) in typesViewItem.viewItems.enumerated() { rows.append( - typeRow( - viewItem: viewItem, - index: index, - isFirst: index == 0, - isLast: typesViewItem.action != nil ? false : index == typesViewItem.viewItems.count - 1 - ) + typeRow( + viewItem: viewItem, + index: index, + isFirst: index == 0, + isLast: typesViewItem.action != nil ? false : index == typesViewItem.viewItems.count - 1 + ) ) } if let action = typesViewItem.action { rows.append( - CellBuilderNew.row( - rootElement: .textElement(text: .body(action.title), parameters: .centerAlignment), - tableView: tableView, - id: "action", - hash: "\(action.rawValue)", - height: .heightCell48, - autoDeselect: true, - bind: { cell in - cell.set(backgroundStyle: .lawrence, isLast: true) - }, - action: { [weak self] in - self?.viewModel.onTap(typesAction: action) - } - ) + CellBuilderNew.row( + rootElement: .textElement(text: .body(action.title), parameters: .centerAlignment), + tableView: tableView, + id: "action", + hash: "\(action.rawValue)", + height: .heightCell48, + autoDeselect: true, + bind: { cell in + cell.set(backgroundStyle: .lawrence, isLast: true) + }, + action: { [weak self] in + self?.viewModel.onTap(typesAction: action) + } + ) ) } return Section( - id: "types", - headerState: .margin(height: .margin12), - rows: rows + id: "types", + headerState: .margin(height: .margin12), + rows: rows ) } private func marketRow(id: String, title: String, badge: String?, text: String, isFirst: Bool, isLast: Bool) -> RowProtocol { CellBuilderNew.row( - rootElement: .hStack([ - .textElement(text: .subhead2(title), parameters: .highHugging), - .margin8, - .badge { (component: BadgeComponent) in - component.badgeView.set(style: .small) - - if let badge = badge { - component.badgeView.text = badge - component.isHidden = false - } else { - component.isHidden = true - } - }, - .textElement(text: .subhead1(text), parameters: .rightAlignment) - ]), - tableView: tableView, - id: id, - height: .heightCell48, - bind: { cell in - cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) - } + rootElement: .hStack([ + .textElement(text: .subhead2(title), parameters: .highHugging), + .margin8, + .badge { (component: BadgeComponent) in + component.badgeView.set(style: .small) + + if let badge = badge { + component.badgeView.text = badge + component.isHidden = false + } else { + component.isHidden = true + } + }, + .textElement(text: .subhead1(text), parameters: .rightAlignment), + ]), + tableView: tableView, + id: id, + height: .heightCell48, + bind: { cell in + cell.set(backgroundStyle: .lawrence, isFirst: isFirst, isLast: isLast) + } ) } @@ -515,7 +520,7 @@ extension CoinOverviewViewController { }, viewItem.genesisDate.map { (id: "genesis_date", title: "coin_overview.genesis_date".localized, badge: nil, text: $0) - } + }, ].compactMap { $0 } @@ -526,27 +531,24 @@ extension CoinOverviewViewController { let rows = datas.enumerated().map { index, tuple in marketRow( - id: tuple.id, - title: tuple.title, - badge: tuple.badge, - text: tuple.text, - isFirst: index == 0, - isLast: index == datas.count - 1 + id: tuple.id, + title: tuple.title, + badge: tuple.badge, + text: tuple.text, + isFirst: index == 0, + isLast: index == datas.count - 1 ) } - return Section( - id: "market_info_section", - headerState: .margin(height: .margin12), - rows: rows + id: "market_info_section", + headerState: .margin(height: .margin12), + rows: rows ) } - } extension CoinOverviewViewController: SectionsDataSource { - public func buildSections() -> [SectionProtocol] { var sections = [SectionProtocol]() @@ -554,13 +556,13 @@ extension CoinOverviewViewController: SectionsDataSource { sections.append(coinInfoSection(viewItem: viewItem.coinViewItem)) sections.append(chartSection) sections.append( - Section( - id: "chart-configuration", - headerState: .margin(height: .margin12), - rows: [ - chartConfigurationRow - ] - ) + Section( + id: "chart-configuration", + headerState: .margin(height: .margin12), + rows: [ + chartConfigurationRow, + ] + ) ) if let marketInfoSection = marketInfoSection(viewItem: viewItem) { @@ -573,12 +575,8 @@ extension CoinOverviewViewController: SectionsDataSource { sections.append(typesSection(typesViewItem: types)) } - if let categories = viewItem.categories { - sections.append(categoriesSection(categories: categories)) - } - - if !viewItem.description.isEmpty { - sections.append(descriptionSection(description: viewItem.description)) + if !viewItem.description.isEmpty, let descriptionSection = descriptionSection(description: viewItem.description) { + sections.append(descriptionSection) } if viewItem.guideUrl != nil || !viewItem.links.isEmpty { @@ -590,5 +588,4 @@ extension CoinOverviewViewController: SectionsDataSource { return sections } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewItemFactory.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewItemFactory.swift index 5f167eb4aa..68468ea63c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewItemFactory.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewItemFactory.swift @@ -1,9 +1,8 @@ -import Foundation import CurrencyKit +import Foundation import MarketKit class CoinOverviewViewItemFactory { - private func roundedFormat(coinCode: String, value: Decimal?) -> String? { guard let value = value, !value.isZero, let formattedValue = ValueFormatter.instance.formatShort(value: value, decimalCount: 0, symbol: coinCode) else { return nil @@ -57,11 +56,6 @@ class CoinOverviewViewItemFactory { return viewItems } - private func categories(info: MarketInfoOverview) -> [String]? { - let categories = info.categories - return categories.isEmpty ? nil : categories.map { $0.name } - } - private func typesTitle(coinUid: String) -> String { switch coinUid { case "bitcoin", "litecoin": return "coin_overview.bips".localized @@ -85,23 +79,23 @@ class CoinOverviewViewItemFactory { case .native: title = blockchain.name subtitle = "coin_platforms.native".localized - case .derived(let derivation): + case let .derived(derivation): title = derivation.mnemonicDerivation.title subtitle = derivation.mnemonicDerivation.addressType + derivation.mnemonicDerivation.recommended - case .addressType(let type): + case let .addressType(type): title = type.bitcoinCashCoinType.title subtitle = type.bitcoinCashCoinType.description + type.bitcoinCashCoinType.recommended - case .eip20(let address): + case let .eip20(address): title = blockchain.name subtitle = address.shortened reference = address url = blockchain.explorerUrl(reference: address) - case .bep2(let symbol): + case let .bep2(symbol): title = blockchain.name subtitle = symbol reference = symbol url = blockchain.explorerUrl(reference: symbol) - case .spl(let address): + case let .spl(address): title = blockchain.name subtitle = address.shortened reference = address @@ -120,13 +114,13 @@ class CoinOverviewViewItemFactory { } return CoinOverviewViewModel.TypeViewItem( - iconUrl: blockchain.type.imageUrl, - title: title, - subtitle: subtitle, - reference: reference, - explorerUrl: url, - showAdd: showAdd, - showAdded: showAdded + iconUrl: blockchain.type.imageUrl, + title: title, + subtitle: subtitle, + reference: reference, + explorerUrl: url, + showAdd: showAdd, + showAdded: showAdded ) } } @@ -190,17 +184,15 @@ class CoinOverviewViewItemFactory { } return CoinOverviewViewModel.LinkViewItem( - title: linkTitle(type: linkType, url: url), - iconName: linkIconName(type: linkType), - url: linkUrl(type: linkType, url: url) + title: linkTitle(type: linkType, url: url), + iconName: linkIconName(type: linkType), + url: linkUrl(type: linkType, url: url) ) } } - } extension CoinOverviewViewItemFactory { - func viewItem(item: CoinOverviewService.Item, currency: Currency, typesShown: Bool) -> CoinOverviewViewModel.ViewItem { let info = item.info let coin = info.fullCoin.coin @@ -211,35 +203,33 @@ extension CoinOverviewViewItemFactory { if !item.tokens.isEmpty { types = CoinOverviewViewModel.TypesViewItem( - title: typesTitle(coinUid: coin.uid), - viewItems: typeViewItems(tokenItems: item.tokens.count > 4 && !typesShown ? Array(item.tokens.prefix(3)) : item.tokens), - action: item.tokens.count > 4 ? (typesShown ? .showLess : .showMore) : nil + title: typesTitle(coinUid: coin.uid), + viewItems: typeViewItems(tokenItems: item.tokens.count > 4 && !typesShown ? Array(item.tokens.prefix(3)) : item.tokens), + action: item.tokens.count > 4 ? (typesShown ? .showLess : .showMore) : nil ) } return CoinOverviewViewModel.ViewItem( - coinViewItem: CoinOverviewViewModel.CoinViewItem( - name: coin.name, - marketCapRank: marketCapRank, - imageUrl: coin.imageUrl, - imagePlaceholderName: "placeholder_circle_32" - ), - + coinViewItem: CoinOverviewViewModel.CoinViewItem( + name: coin.name, marketCapRank: marketCapRank, - marketCap: info.marketCap.flatMap { ValueFormatter.instance.formatShort(currency: currency, value: $0) }, - totalSupply: roundedFormat(coinCode: coinCode, value: info.totalSupply), - circulatingSupply: roundedFormat(coinCode: coinCode, value: info.circulatingSupply), - volume24h: info.volume24h.flatMap { ValueFormatter.instance.formatShort(currency: currency, value: $0) }, - dilutedMarketCap: info.dilutedMarketCap.flatMap { ValueFormatter.instance.formatShort(currency: currency, value: $0) }, - genesisDate: info.genesisDate.map { DateHelper.instance.formatFullDateOnly(from: $0) }, - - performance: performanceViewItems(info: info), - categories: categories(info: info), - types: types, - description: info.description, - guideUrl: item.guideUrl, - links: links(info: info) + imageUrl: coin.imageUrl, + imagePlaceholderName: "placeholder_circle_32" + ), + + marketCapRank: marketCapRank, + marketCap: info.marketCap.flatMap { ValueFormatter.instance.formatShort(currency: currency, value: $0) }, + totalSupply: roundedFormat(coinCode: coinCode, value: info.totalSupply), + circulatingSupply: roundedFormat(coinCode: coinCode, value: info.circulatingSupply), + volume24h: info.volume24h.flatMap { ValueFormatter.instance.formatShort(currency: currency, value: $0) }, + dilutedMarketCap: info.dilutedMarketCap.flatMap { ValueFormatter.instance.formatShort(currency: currency, value: $0) }, + genesisDate: info.genesisDate.map { DateHelper.instance.formatFullDateOnly(from: $0) }, + + performance: performanceViewItems(info: info), + types: types, + description: info.description, + guideUrl: item.guideUrl, + links: links(info: info) ) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModel.swift index 0dfd4e4318..4f795834fd 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinOverview/CoinOverviewViewModel.swift @@ -1,10 +1,10 @@ +import ComponentKit import Foundation -import RxSwift -import RxRelay -import RxCocoa import MarketKit +import RxCocoa +import RxRelay +import RxSwift import UIKit -import ComponentKit class CoinOverviewViewModel { private let service: CoinOverviewService @@ -32,7 +32,7 @@ class CoinOverviewViewModel { viewItemRelay.accept(nil) loadingRelay.accept(true) syncErrorRelay.accept(false) - case .completed(let item): + case let .completed(item): viewItemRelay.accept(viewItemFactory.viewItem(item: item, currency: service.currency, typesShown: typesShown)) loadingRelay.accept(false) syncErrorRelay.accept(false) @@ -42,11 +42,9 @@ class CoinOverviewViewModel { syncErrorRelay.accept(true) } } - } extension CoinOverviewViewModel { - var viewItemDriver: Driver { viewItemRelay.asDriver() } @@ -75,20 +73,18 @@ extension CoinOverviewViewModel { do { try service.editWallet(index: index, add: true) hudRelay.accept(.addedToWallet) - } catch { - } + } catch {} } func onTapAddedToWallet(index: Int) { do { try service.editWallet(index: index, add: false) hudRelay.accept(.removedFromWallet) - } catch { - } + } catch {} } func onTap(typesAction: TypesAction) { - guard case .completed(let item) = service.state else { + guard case let .completed(item) = service.state else { return } @@ -99,11 +95,9 @@ extension CoinOverviewViewModel { viewItemRelay.accept(viewItemFactory.viewItem(item: item, currency: service.currency, typesShown: typesShown)) } - } extension CoinOverviewViewModel { - struct CoinViewItem { let name: String let marketCapRank: String? @@ -123,7 +117,6 @@ extension CoinOverviewViewModel { let genesisDate: String? let performance: [[PerformanceViewItem]] - let categories: [String]? let types: TypesViewItem? let description: String let guideUrl: URL? @@ -170,5 +163,4 @@ extension CoinOverviewViewModel { let iconName: String let url: String } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageMarkdownParser.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageMarkdownParser.swift index e65b9f633a..f0dcad03a9 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageMarkdownParser.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Coin/CoinPageMarkdownParser.swift @@ -1,20 +1,19 @@ -import UIKit import Down +import UIKit class CoinPageMarkdownParser { - let fonts = StaticFontCollection( - heading1: .title2, - heading2: .title3, - heading3: .subhead1, - body: .subhead2 + heading1: .title3, + heading2: .headline2, + heading3: .subhead1, + body: .subhead2 ) let colors = StaticColorCollection( - heading1: .themeLeah, - heading2: .themeJacob, - heading3: .themeBran, - body: .themeGray + heading1: .themeLeah, + heading2: .themeLeah, + heading3: .themeBran, + body: .themeGray ) let paragraphStyles: StaticParagraphStyleCollection = { @@ -42,14 +41,13 @@ class CoinPageMarkdownParser { let listItemOptions = ListItemOptions(maxPrefixDigits: 1, spacingAfterPrefix: .margin8, spacingAbove: .margin12, spacingBelow: .margin12) let configuration = DownStylerConfiguration( - fonts: fonts, - colors: colors, - paragraphStyles: paragraphStyles, - listItemOptions: listItemOptions + fonts: fonts, + colors: colors, + paragraphStyles: paragraphStyles, + listItemOptions: listItemOptions ) let styler = DownStyler(configuration: configuration) return try down.toAttributedString(styler: styler) } - } diff --git a/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings b/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings index 3193f2733b..516ad7941f 100644 --- a/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings +++ b/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings @@ -740,8 +740,8 @@ Go to Settings - > %@ and allow access to the camera."; "coin_overview.roi.day200" = "6 Month"; "coin_overview.roi.year1" = "1 Year"; -"coin_overview.category" = "Category"; - +"coin_overview.overview" = "Overview"; +"coin_overview.description_warning" = "This is an AI generated description based on the provided reference material for the given cryptocurrency. It may contain errors."; "coin_overview.blockchains" = "Blockchains"; "coin_overview.bips" = "BIPs"; "coin_overview.coin_types" = "Coin Types"; @@ -1372,7 +1372,6 @@ Go to Settings - > %@ and allow access to the camera."; "chart.performance.week_changes" = "Changes (1W)"; "chart.performance.month_changes" = "Changes (1M)"; -"chart.about.header" = "About"; "chart.about.read_more" = "Read More"; "chart.about.read_less" = "Read Less";