// // WalletControllerDashboard.swift // Wallet // // Created by Igor Danich on 30.08.2020. // Copyright © 2020 List. All rights reserved. // import UIKit import struct RealmSwift.Results import class RealmSwift.NotificationToken class P2PControllerDashboard: UIViewController { @IBOutlet var sectionLabels: [UILabel]! @IBOutlet var sectionNotificationButtons: [UIButton]! @IBOutlet var selectedSegmentPositionConsstraint: NSLayoutConstraint! @IBOutlet private weak var tokenView: P2PViewToken! @IBOutlet private weak var historyButton: UIButton! @IBOutlet private weak var cardsBtn: UIButton! @IBOutlet private weak var filtersButton: UIButton! @IBOutlet private weak var filtersBadgeView: UIView! @IBOutlet private weak var balanceLbl: UILabel! @IBOutlet private weak var topView: UIView! @IBOutlet private weak var dealsView: CommonViewEmptyContainer! @IBOutlet private weak var collectionView: UICollectionView! @IBOutlet private weak var pageControl: UIPageControl! @IBOutlet private weak var ordersView: CommonViewEmptyContainer! @IBOutlet private weak var kindCtrl: CommonSwitch! @IBOutlet var tableViewsContainerStackView: UIStackView! @IBOutlet weak var tableViewsContainerScrollView: UIScrollView! @IBOutlet var tableViews: [UITableView]! @IBOutlet private weak var offset: NSLayoutConstraint! private var emptyViews = [CommonViewEmpty(), CommonViewEmpty(), CommonViewEmpty()] private let topViewHeight: CGFloat = 365 private var didOpenChat = false let tablesHeaderView = P2PLockedFundsTopView() var service: P2P.Service.Dashboard! private let refreshs = [UIRefreshControl(), UIRefreshControl(), UIRefreshControl()] private var notificationTokens: [NotificationToken] = [] func setTabWithSroll(index: Int) { selectedSection = index var frame: CGRect = tableViewsContainerScrollView.frame frame.origin.x = frame.size.width * CGFloat(index) frame.origin.y = 0 tableViewsContainerScrollView.scrollRectToVisible(frame, animated: true) } private var selectedSection: Int = 0 { didSet { UIView.animate(withDuration: 0.3) { self.selectedSegmentPositionConsstraint.constant = CGFloat(self.selectedSection) * UIScreen.main.bounds.width / 3.0 self.view.layoutIfNeeded() } for (index, label) in sectionLabels.enumerated() { label.font = index == self.selectedSection ? FontFamily.GolosUI.medium.font(size: 14) : FontFamily.GolosUI.regular.font(size: 14) label.textColor = index == self.selectedSection ? Asset.textDeepWater.color : Asset.textGranite.color } for (index, button) in sectionNotificationButtons.enumerated() { if index == 0 { button.backgroundColor = Asset.brick.color } else { button.backgroundColor = index == self.selectedSection ? Asset.deepWater.color : Asset.pebble.color } } service.isMine = selectedSection == 1 update() } } @IBAction func sectionPressed(_ sender: Any) { setTabWithSroll(index: (sender as? UIButton)?.tag ?? 0) } override func viewDidLoad() { super.viewDidLoad() collectionView.register(cell: P2PCellDeal.self) refreshs.forEach { $0.addTarget(self, action: #selector(onRefresh), for: .valueChanged) } tableViews.forEach({ $0.register(cell: P2pNumberFilterTableViewCell.self) $0.register(cell: P2PCellOrder.self) $0.register(cell: P2PDealTableViewCell.self) $0.backgroundColor = Asset.gypsum.color $0.tableHeaderView = nil if #available(iOS 15.0, *) { $0.sectionHeaderTopPadding = 0.0 } $0.addSubview(refreshs[$0.tag]) }) tablesHeaderView.pressAction = { [weak self] in self?.onWidthdraw(nil) } tablesHeaderView.switchAction = { [weak self] in Loader.show() self?.service.dealsService.runtimeConfiguration.dealsActiveOnly = !$0 self?.service.dealsService.fetch(username: Accounts().current?.username ?? "") { Loader.hide() Alert.notify($0) } } tableViewsContainerStackView.insertArrangedSubview(tablesHeaderView, at: 0) filtersBadgeView.layer.cornerRadius = 4.0 for button in sectionNotificationButtons { button.layer.cornerRadius = 6.0 button.contentEdgeInsets = UIEdgeInsets(top: 2, left: 5, bottom: 3, right: 5) } openRightSection() kindCtrl.lzTextBack = L10n.P2p.Dashboard.Menu.all kindCtrl.lzTextBackSelected = L10n.P2p.Dashboard.Menu.mine kindCtrl.lzTextFront = L10n.P2p.Dashboard.Menu.mine kindCtrl.lzTextFrontSelected = L10n.P2p.Dashboard.Menu.all kindCtrl.isSelected = true offset.constant = 0 notificationTokens.append(contentsOf: [ service.ordersService.orders.observe(on: .main) { [weak self] _ in self?.update() }, service.p2pBalancesService.balances.observe(on: .main) { [weak self] _ in self?.update() }, service.dealsService.deals.observe(on: .main) { [weak self] _ in self?.update() } ]) Notification.subscribe(bag: bag, name: .didUpdateBalances) { [unowned self]_ in update() } Notification.subscribe(name: .didChangeAccount) { [weak self] _ in self?.notificationTokens.removeLast() self?.notificationTokens.removeLast() self?.notificationTokens.append(contentsOf: [ self?.service.dealsService.deals.observe(on: .main) { [weak self] _ in self?.update() }, self?.service.p2pBalancesService.balances.observe(on: .main) { [weak self] _ in self?.update() } ].compactMap({ $0 })) } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationGlobalHack = navigationController navigationController?.setNavigationBarHidden(true, animated: true) if didOpenChat { mainController.barNav.popItem(animated: true) didOpenChat = false } update() refreshAll(showAlert: false) //Fetch orders first time maybe here? but why we should fetch on each apear better sockets updates // P2P.Service.balances.fetch() // service.orders.fetch() // service.deals.fetch() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationGlobalHack = nil } func openRightSection() { if let count = service.activeDeals(symbol: service.token?.symbol ?? ""), count > 0 { setTabWithSroll(index: 0) } else if service.mineOrders.count > 0 { setTabWithSroll(index: 1) } else { setTabWithSroll(index: 2) } } @objc private func onRefresh() { refreshAll(showAlert: true) } private func refreshAll(showAlert: Bool) { if showAlert { // ordersView.isLoading = true } Account.Service.History.shared.current?.fetch() Wallet.Service.Tokens.shared.balances.fetch() service.p2pBalancesService.fetch(username: Accounts().current?.username ?? "", completion: { if showAlert { // self?.ordersView.isLoading = false Alert.notify($0) } }) service.ordersService.fetch(completion: { if showAlert { // self?.ordersView.isLoading = false Alert.notify($0) } }) service.dealsService.fetch(username: Accounts().current?.username ?? "", completion: { if showAlert { // self?.ordersView.isLoading = false Alert.notify($0) } }) service.p2pListsService.fetch(completion: { _ in }) } private func update() { Loader.hide() //Locked Funds and Switch old deals view section let blockedBalance = service.currentBalance?.blockedAmount.decimalValue ?? 0 let freeAmount = service.currentBalance?.amount.decimalValue ?? 0 tablesHeaderView.amountLabel.lzText = (blockedBalance + freeAmount) .toReadebleString(precisionMax: 5, symbol: service.token?.symbol ?? "", group: true) tablesHeaderView.dealsSwitch.isOn = !service.dealsService.runtimeConfiguration.dealsActiveOnly tablesHeaderView.backgroundContainerView.isHidden = (blockedBalance + freeAmount) == 0 tablesHeaderView.switchSection.isHidden = selectedSection != 0 tablesHeaderView.isHidden = tablesHeaderView.backgroundContainerView.isHidden && tablesHeaderView.switchSection.isHidden //End if let count = service.activeDeals(symbol: service.token?.symbol ?? ""), count > 0 { sectionNotificationButtons[0].isHidden = false sectionNotificationButtons[0].lzTitle = "\(count)" } else { sectionNotificationButtons[0].isHidden = true } if service.mineOrders.count > 0 { sectionNotificationButtons[1].isHidden = false sectionNotificationButtons[1].lzTitle = "\(service.mineOrders.count)" } else { sectionNotificationButtons[1].isHidden = true } sectionNotificationButtons[2].isHidden = true // if service.notMineOrders.count > 0 { // sectionNotificationButtons[2].isHidden = false // sectionNotificationButtons[2].lzTitle = "\(service.notMineOrders.count)" // } else { // sectionNotificationButtons[2].isHidden = true // } filtersBadgeView.isHidden = service.isNoFiltersApplied if !kindCtrl.isSelected { ordersView.image = service.kind == .sell ? Asset.p2pOrdersEmptySell.image : Asset.p2pOrdersEmptyBuy.image ordersView.title = L10n.P2p.Dashboard.Orders.Empty.title ordersView.text = service.kind == .sell ? L10n.P2p.Dashboard.Orders.Empty.Sell.mine : L10n.P2p.Dashboard.Orders.Empty.Buy.mine emptyViews.forEach { $0.image = service.kind == .sell ? Asset.p2pOrdersEmptySell.image : Asset.p2pOrdersEmptyBuy.image $0.title = L10n.P2p.Dashboard.Orders.Empty.title $0.text = service.kind == .sell ? L10n.P2p.Dashboard.Orders.Empty.Sell.mine : L10n.P2p.Dashboard.Orders.Empty.Buy.mine } } else { ordersView.image = service.kind == .sell ? Asset.p2pOrdersEmptyBuy.image : Asset.p2pOrdersEmptySell.image ordersView.title = L10n.P2p.Dashboard.Orders.Empty.title ordersView.text = service.kind == .sell ? L10n.P2p.Dashboard.Orders.Empty.Sell.all : L10n.P2p.Dashboard.Orders.Empty.Buy.all emptyViews.forEach { $0.image = service.kind == .sell ? Asset.p2pOrdersEmptyBuy.image : Asset.p2pOrdersEmptySell.image $0.title = L10n.P2p.Dashboard.Orders.Empty.title $0.text = service.kind == .sell ? L10n.P2p.Dashboard.Orders.Empty.Sell.all : L10n.P2p.Dashboard.Orders.Empty.Buy.all } } if service.dealsToShow.count == 0 { historyButton.setImage(Asset.p2pHistoryEmpty.image, for: .normal) } else { if service.dealsService.runtimeConfiguration.dealsActiveOnly { historyButton.setImage(Asset.p2pHistoryOld.image, for: .normal) } else { historyButton.setImage(Asset.p2pHistoryActive.image, for: .normal) } } let collection = P2P.Service.tokens.map({ token -> Common.Model.Menu in var menu = token.toMenu() if let count = service.activeDeals(symbol: token.symbol) { menu.badge = ("\(count)", Asset.brick.color) } // menu.text = menu.text?.components(separatedBy: " ").last ?? "" return menu }) let selectedMenu = service.token.map({ token -> Common.Model.Menu in var menu = token.toMenu() if let count = service.activeDeals(symbol: token.symbol) { menu.badge = ("\(count)", Asset.brick.color) } if !Locale.preferredLanguages[0].lowercased().starts(with: "en") { menu.text = menu.text?.components(separatedBy: " ").last ?? "" } return menu }) tokenView.action = .cards(collection: collection, selectedMenu: selectedMenu) { [weak self] menu in self?.service.token = P2P.Service.tokens.object(menu: menu) ((self?.mainController.content.viewControllers?[2] as? UINavigationController)?.viewControllers.first as? P2PControllerDashboard)?.service.token = P2P.Service.tokens.object(menu: menu) ((self?.mainController.content.viewControllers?[3] as? UINavigationController)?.viewControllers.first as? P2PControllerDashboard)?.service.token = P2P.Service.tokens.object(menu: menu) self?.update() } balanceLbl.attributedText = service.balance.attributed(style: .bold, size: 16, color: Asset.textCoal.color) // dealsView.isLoading = service.dealsToShow.isEmpty ? service.deals.isLoading : false pageControl.numberOfPages = service.dealsToShow.count if service.dealsToShow.isEmpty {//, !service.deals.isLoading { offset.constant = 0 UIView.animate(withDuration: Animation.medium) { self.view.layoutIfNeeded() } } else if offset.constant == 0 { offset.constant = topViewHeight UIView.animate(withDuration: Animation.medium) { self.view.layoutIfNeeded() } } // collectionView.reloadData() // ordersView.isEmpty = (service.orders.isEmpty && selectedSection != 0) // || (selectedSection == 0 && service.dealsToShow.isEmpty) refreshs.forEach({ $0.endRefreshing() }) tableViews.forEach({ $0.reloadSections([1], with: .automatic) }) } @IBAction func onChange(_: AnyObject?) { service.isMine = !kindCtrl.isSelected update() } @IBAction func onWidthdraw(_: AnyObject?) { let symbol = (service.token?.symbol ?? "") let blockedBalance = (service.currentBalance?.blockedAmount.decimalValue.toString(max: 5) ?? "0") let amount = (service.currentBalance?.amount.decimalValue.toString(max: 5) ?? "0") self.showBalances(blocked: [blockedBalance, symbol].joined(separator: " "), amount: [amount, symbol].joined(separator: " ")) } @IBAction func onHistory(_: AnyObject?) { // service.isHistoryDeals = !service.isHistoryDeals collectionView.reloadData() update() } @IBAction func onCards(_: AnyObject?) { self.mainController.content.push(StoryboardScene.Cards.initial.instantiate(), animated: true) } @IBAction func onFilters(_: AnyObject?) { let navigationController = self.navigationController let vc = P2PFilterViewController() vc.selectedToken = service.token let nc = UINavigationController(rootViewController: vc) vc.service = self.service.p2pListsService vc.selectedFilterValues = service.filters // vc.clearButtonAction = { [weak self] in // self?.service.filters = FilterSelectedValues() // self?.update() // } vc.applyButtonAction = { [weak self] in self?.service.filters = $0 self?.update() nc.dismiss(animated: true, completion: nil) } nc.modalPresentationStyle = .overFullScreen navigationController?.present(nc, animated: true) } @IBAction func onExpand(_: AnyObject?) { offset.constant = topViewHeight UIView.animate(withDuration: Animation.medium) { self.view.layoutIfNeeded() } } @IBAction func onCreate(_: AnyObject?) { Popup.show(content: OrderTypeSelectionPopupViewController(kind: service.kind) { [weak service] in guard let service = service else { return } print($0) let ctrl = StoryboardScene.P2P.order.instantiate() ctrl.service = .init(kind: service.kind, contentType: $0, token: service.token) self.mainController.content.push(ctrl, animated: true) }, contentScrollIsEnabled: false) } private var panPos: CGPoint! @IBAction func onPan(_ pan: UIPanGestureRecognizer) { guard let panView = pan.view, service.dealsToShow.count > 0 else { return } switch pan.state { case .began: panPos = pan.location(in: panView) case .changed: let value = pan.location(in: panView).y - panPos.y if offset.constant + value < 60 { offset.constant = 60 } else if offset.constant + value > topViewHeight { offset.constant = topViewHeight } else { offset.constant += value } view.layoutIfNeeded() panPos = pan.location(in: pan.view!) case .ended, .failed, .cancelled: panPos = nil default: break } } func edit(order: P2POrder) { let ctrl = StoryboardScene.P2P.order.instantiate() ctrl.service = .init(kind: order.kind, contentType: order.contentType, token: service.token, order: P2POrder(value: order)) ctrl.service.card = Cards.shared.collection .first(where: { $0.number == order.cardNumber ?? "" }) ?? Cards.shared.default ctrl.service.paymentSystem = order.paymentSystem.map({ (id: $0.id, title: $0.name, description: "") }) ctrl.service.tokenGet = order.currency.map({ (id: $0.id, title: $0.name, description: $0.descriptn ?? "") }) ctrl.service.blockchain = order.blockchain.map({ (id: $0.id, title: $0.name, description: $0.descriptn ?? "") }) ctrl.service.rateToken1 = order.rateIsReversed ? order.rate.decimalValue.toReadebleString(precisionMax: 12) : "1" ctrl.service.rateToken2 = order.rateIsReversed ? "1" : order.rate.decimalValue.toReadebleString(precisionMax: 12) ctrl.service.amount = order.volume.decimalValue.toReadebleString(precisionMax: 2) ctrl.service.limitMin = order.min.decimalValue.toReadebleString(precisionMax: 2) ctrl.service.limitMax = order.max.decimalValue.toReadebleString(precisionMax: 2) ctrl.service.link = order.paymentUrl ?? "" ctrl.service.vcardNumber = order.cardNumber ?? "" ctrl.service.vcardName = order.cardName ?? "" ctrl.service.vcardExpiration = order.cardDate ?? "" ctrl.service.vcardCVV = order.cvv ?? "" ctrl.service.country = order.country.map({ (id: $0.id, title: $0.name, description: "") }) ctrl.service.city = order.city.map({ (id: $0.id, title: $0.name, description: "") }) ctrl.service.bank = order.banks.map({ (id: $0.id, title: $0.name, description: "") }) ctrl.service.account = order.walletNumber ?? order.walletAddress ?? "" ctrl.service.memo = order.memo ?? "" self.mainController.content.push(ctrl, animated: true) } } extension P2PControllerDashboard: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { // print("dfggerg") // guard service.dealsToShow.count > 0 else { return } // if scrollView == tableView { // let value = offset.constant - scrollView.contentOffset.y // if value > 60 && value < 247 { // scrollView.contentOffset = .zero // offset.constant = value // topView.alpha = 0 // } else if value <= 60 { // offset.constant = 60 // topView.alpha = 1 // } else if value >= topViewHeight { // offset.constant = topViewHeight // topView.alpha = 0 // } // view.layoutIfNeeded() // } else if scrollView == collectionView { // pageControl.currentPage = Int( // CGFloat(pageControl.numberOfPages)*(scrollView.contentOffset.x + scrollView.frameWidth/2)/scrollView.contentSize.width // ) // } } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { print(scrollView.contentOffset.x / scrollView.frame.size.width) if scrollView == tableViewsContainerScrollView { selectedSection = Int(scrollView.contentOffset.x / scrollView.frame.size.width) } } } extension P2PControllerDashboard: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return UICollectionViewCell() } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 0 // service.dealsToShow.count } //swiftlint:disable:next function_body_length func tableViewDealCell(_ tableView: UITableView, dealCellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(P2PDealTableViewCell.self, indexPath: indexPath) guard indexPath.row < service.dealsToShow.count else { return cell } cell.p2pDealView.tableView = tableView let deal = service.dealsToShow[indexPath.row] cell.p2pDealView.setup(model: deal) { [unowned self] action in switch action { case .complete: if deal.isExpired && deal.seller == Accounts().current?.username && deal.status != .dispute { AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() self.service.dealsActionsService.complete(privateKey: $0, deal: deal) { (error) in Loader.hide() Alert.notify(error) } } } else if deal.isExpired && deal.buyer == Accounts().current?.username && deal.status != .dispute { Popup.show( title: L10n.P2p.Dashboard.Deal.Confirm.title, views: [ .label( text: L10n.P2p.Dashboard.Deal.Confirm.Complete.Text.expired .attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .warning(text: L10n.P2p.Dashboard.Deal.Confirm.Complete.Warning.expired), .field( title: L10n.P2p.Dashboard.Deal.Confirm.Complete.Amount.expired, text: (deal.volume.decimalValue + deal.insurance.decimalValue).toString(max: 5, symbol: deal.symbol) ), .field( title: L10n.P2p.Dashboard.Deal.Confirm.Complete.Insurance.expired, text: deal.insurance.decimalValue.toString(max: 5, symbol: deal.symbol) ) ], submit: L10n.P2p.Dashboard.Deal.Confirm.submit, in: self ) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show(in: ctrl) self.service.dealsActionsService.complete(privateKey: $0, deal: deal) { (error) in Loader.hide(in: ctrl) ctrl.dismiss(animated: true) Alert.notify(error) } } } } else { Popup.show( title: L10n.P2p.Dashboard.Deal.Confirm.title, views: [ .label( text: L10n.P2p.Dashboard.Deal.Confirm.Complete.text(deal.volume.decimalValue.toString(max: 5, symbol: deal.symbol)) .attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .warning(text: L10n.P2p.Dashboard.Deal.Confirm.Complete.warning), .field( title: L10n.P2p.Dashboard.Deal.Confirm.Complete.amount, text: deal.volume.decimalValue.toString(max: 5, symbol: deal.symbol) ), .field( title: L10n.P2p.Dashboard.Deal.Confirm.Complete.insurance, text: deal.insurance.decimalValue.toString(max: 5, symbol: deal.symbol) ) ], submit: L10n.P2p.Dashboard.Deal.Confirm.submit, in: self ) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show(in: ctrl) self.service.dealsActionsService.complete(privateKey: $0, deal: deal) { (error) in Loader.hide(in: ctrl) ctrl.dismiss(animated: true) Alert.notify(error) } } } } case .dispute: Popup.show( title: L10n.P2p.Dashboard.Deal.Confirm.title, views: [ .label( text: L10n.P2p.Dashboard.Deal.Confirm.Dispute.text1.attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .warning(text: L10n.P2p.Dashboard.Deal.Confirm.Dispute.warning), .label( text: L10n.P2p.Dashboard.Deal.Confirm.Dispute.text2.attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ) ], submit: L10n.P2p.Dashboard.Deal.Confirm.submit, in: self ) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show(in: ctrl) self.service.dealsActionsService.dispute(privateKey: $0, deal: deal) { (error) in Loader.hide(in: ctrl) Alert.notify(error, dismiss: ctrl) } } } case .cancel: if deal.seller == Accounts().current?.username || !deal.isExpired { Popup.show( title: L10n.P2p.Dashboard.Deal.Confirm.title, views: [ .label( text: L10n.P2p.Dashboard.Deal.Confirm.Cancel.text("100").attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .warning(text: L10n.P2p.Dashboard.Deal.Confirm.Cancel.warning), .field( title: L10n.P2p.Dashboard.Deal.Confirm.Cancel.insurance, text: deal.insurance.decimalValue.toString(max: 5, symbol: deal.symbol) ) ], submit: L10n.P2p.Dashboard.Deal.Confirm.submit, in: self ) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show(in: ctrl) self.service.dealsActionsService.cancel(privateKey: $0, deal: deal) { (error) in Loader.hide(in: ctrl) Alert.notify(error, dismiss: ctrl) } } } } else { Popup.show( title: L10n.P2p.Dashboard.Deal.Confirm.title, views: [ .label( text: L10n.P2p.Dashboard.Deal.Confirm.Cancel.text("100").attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .warning(text: L10n.P2p.Dashboard.Deal.Confirm.Cancel.warning), .field( title: L10n.P2p.Dashboard.Deal.Confirm.Cancel.insurance, text: deal.insurance.decimalValue.toString(max: 5, symbol: deal.symbol) ) ], submit: L10n.P2p.Dashboard.Deal.Confirm.submit, in: self ) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show(in: ctrl) self.service.dealsActionsService.cancel(privateKey: $0, deal: deal) { (error) in Loader.hide(in: ctrl) Alert.notify(error, dismiss: ctrl) } } } } case .withdraw: AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() self.service.dealsActionsService.withdraw(privateKey: $0, deal: deal) { (error) in Loader.hide() Alert.notify(error) } } case .pay: Loader.show() service.dealsActionsService.pay(deal: deal) { (error) in Loader.hide() Alert.notify(error) } case .chat: AccountViewAuthorize.showGetPrivateKey(in: self) { guard let user = Accounts().current, let service = try? CryptoChat.Service.MsgsHistory(username: user.username, encryptionKey: $0) else { return } self.didOpenChat = true let account = deal.seller == Accounts().current?.username ? deal.buyer : deal.seller let ctrl = CryptoChatControllerChat() ctrl.username = account ctrl.msgsHistoryService = service ctrl.unreadCount = 0 self.mainController.content.push(ctrl, animated: true) } } } return cell } } extension P2PControllerDashboard: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { 2 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 0 { return 1 } var count: Int = 0 switch tableView.tag { case 0: count = service.dealsToShow.count case 1: count = service.mineOrders.count case 2: count = service.notMineOrders.count default: break } tableView.backgroundView = count == 0 ? emptyViews[tableView.tag] : nil return count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.section == 0 { let cell = tableView.dequeueReusableCell(P2pNumberFilterTableViewCell.self, indexPath: indexPath) cell.searchTextField.delegate = self cell.searchTextField.lzPlaceholder = tableView.tag == 0 ? L10n.P2p.Filter.Filters.searchDeals : L10n.P2p.Filter.Filters.searchOrders cell.searchTextField.keyboardType = .decimalPad if !cell.searchTextField.isFirstResponder { switch selectedSection { case 0: cell.searchTextField.text = service.numberFilterDeals cell.searchTextField.clearButtton.isHidden = service.numberFilterDeals.isEmpty case 1: cell.searchTextField.text = service.numberFilterMineOrders cell.searchTextField.clearButtton.isHidden = service.numberFilterMineOrders.isEmpty case 2: cell.searchTextField.text = service.numberFilter cell.searchTextField.clearButtton.isHidden = service.numberFilter.isEmpty default: break } } return cell } if tableView.tag == 0 { return tableViewDealCell(tableView, dealCellForRowAt: indexPath) } let cell = tableView.dequeueReusableCell(P2PCellOrder.self, indexPath: indexPath) let order: P2POrder switch tableView.tag { case 1: guard indexPath.row < service.mineOrders.count else { return cell } order = service.mineOrders[indexPath.row] case 2: guard indexPath.row < service.notMineOrders.count else { return cell } order = service.notMineOrders[indexPath.row] default: return cell } cell.tableView = tableView cell.setup(model: order) { [unowned self] action in switch action { case .buy, .sell: let ctrl = StoryboardScene.P2P.deal.instantiate() ctrl.service = .init(order: P2POrder(value: order)) self.mainController.content.push(ctrl, animated: true) case .pause: AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() order.pause(privateKey: $0) { (error) in Loader.hide() Alert.notify(error) } } case .resume: AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() order.resume(privateKey: $0) { (error) in Loader.hide() Alert.notify(error) } } case .cancel: AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() order.cancel(privateKey: $0) { (error) in Loader.hide() Alert.notify(error) } } case .chat: AccountViewAuthorize.showGetPrivateKey(in: self) { guard let user = Accounts().current, let service = try? CryptoChat.Service.MsgsHistory(username: user.username, encryptionKey: $0) else { return } self.didOpenChat = true let account = order.owner let ctrl = CryptoChatControllerChat() ctrl.username = account ctrl.msgsHistoryService = service ctrl.unreadCount = 0 self.mainController.content.push(ctrl, animated: true) } case .more: let ctrl = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) ctrl.addAction(.init(title: L10n.P2p.Dashboard.Order.Action.moreEdit, style: .default) { [weak self] _ in guard let self = self else { return } // if order.status == .inactive { // edit(order: order) // return // } Popup.show( title: L10n.P2p.Dashboard.Order.Action.MoreEdit.title, views: [ .label( text: (service.kind == .buy ? L10n.P2p.Dashboard.Order.Action.MoreEdit.descriptionBuy : L10n.P2p.Dashboard.Order.Action.MoreEdit.descriptionSell) .attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .warning(text: L10n.P2p.Dashboard.Order.Action.MoreEdit.warning), ], submit: order.status == .inactive ? L10n.P2p.Dashboard.Order.Action.MoreEdit.edit : L10n.P2p.Dashboard.Order.Action.MoreEdit.stopAndEdit, cancel: L10n.Common.Button.cancel, cancelStyle: .tertiary, cancelSize: .small, in: self ) { ctrl in if order.status == .active { AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() order.pause(privateKey: $0) { (error) in Loader.hide() Popup.hide() Alert.notify(error) edit(order: order) } } } else { Popup.hide() edit(order: order) } } }) ctrl.addAction(.init(title: L10n.P2p.Dashboard.Order.Action.moreCancel, style: .default) { [weak self] _ in guard let self = self else { return } Popup.show( title: L10n.Common.Title.confirmation, views: [ .label( text: (service.kind == .buy ? L10n.P2p.Dashboard.Order.Action.MoreCancel.descriptionBuy : L10n.P2p.Dashboard.Order.Action.MoreCancel.descriptionSell) .attributed(style: .regular, size: 14, color: Asset.textCoal.color), numberOfLines: 0 ), .field( title: L10n.P2p.Dashboard.Order.Action.MoreCancel.get, text: (service.kind == .buy ? order.insurance.decimalValue : order.volume.decimalValue) .toReadebleString(precisionMax: 5, symbol: order.symbol) ), .field( title: L10n.P2p.Dashboard.Order.Action.MoreCancel.deposit, text: (order.volume.decimalValue * 0.1).toReadebleString(precisionMax: 5, symbol: order.symbol) ) ].dropLast(service.kind == .buy ? 1 : 0), submit: L10n.Common.Button.confirm, in: self ) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() order.cancel(privateKey: $0) { (error) in Loader.hide() Popup.hide() Alert.notify(error) } } } }) ctrl.addAction(.init(title: L10n.Common.Button.cancel, style: .cancel, handler: nil)) present(ctrl, animated: true) } } return cell } } extension P2PControllerDashboard { fileprivate func showBalances(blocked: String, amount: String) { Popup.show( title: L10n.P2p.Dashboard.Popup.Balance.title, views: [ .field(title: L10n.P2p.Dashboard.Popup.Balance.blocked, text: blocked), .field(title: L10n.P2p.Dashboard.Popup.Balance.free, text: amount) ], buttons: [ .button(title: L10n.P2p.Dashboard.Popup.Balance.Button.withdraw, isEnabled: service.withdrawBalance > kCommissionMin && service.token?.symbol != nil, action: { } ), .button(style: .tertiary, title: L10n.P2p.Dashboard.Popup.Balance.Button.showLockedFunds, size: .small, action: { }) ], buttonsActions: [ { _ in self.withrawBalance() }, { _ in Popup.hide() self.mainController.content.push(P2PLockedFundsController(), animated: true) } ], in: self ) } private func withrawBalance() { guard service.withdrawBalance > kCommissionMin, let symbol = service.token?.symbol else { return } AccountViewAuthorize.showGetPrivateKey(in: self) { Loader.show() self.service.p2pBalancesService.withdraw(privateKey: $0, symbol: symbol) { (error) in Loader.hide() Popup.hide() Alert.notify(error, pop: self) } } } } extension P2PControllerDashboard: CommonTextFieldSearchDelegate { func textField(_ textField: CommonTextFieldSearch, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let newString = ((textField.text ?? "") as NSString) .replacingCharacters(in: range, with: string) .removingWhitespaceAndNewlines() if newString.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil { switch selectedSection { case 0: service.numberFilterDeals = newString case 1: service.numberFilterMineOrders = newString case 2: service.numberFilter = newString default: break } update() return true } else { return false } } func textFieldDidChange(_ textField: CommonTextFieldSearch) { textField.text = textField.text?.removingWhitespaceAndNewlines() } func textFieldShouldClear(_ textField: CommonTextFieldSearch) -> Bool { switch selectedSection { case 0: service.numberFilterDeals = "" case 1: service.numberFilterMineOrders = "" case 2: service.numberFilter = "" default: break } update() return true } }