1058 lines
46 KiB
Swift
1058 lines
46 KiB
Swift
//
|
|
// WalletControllerDashboard.swift
|
|
// Wallet
|
|
//
|
|
// Created by Igor Danich on 30.08.2020.
|
|
// Copyright © 2020 List. All rights reserved.
|
|
//
|
|
|
|
// swiftlint:disable file_length
|
|
|
|
import UIKit
|
|
import Combine
|
|
import WalletFoundation
|
|
import WalletUIComponents
|
|
import struct RealmSwift.Results
|
|
import class RealmSwift.NotificationToken
|
|
|
|
final 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
|
|
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
let tablesHeaderView = P2PLockedFundsTopView()
|
|
|
|
var service: P2P.Service.Dashboard!
|
|
|
|
private var isRefreshing = false
|
|
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)
|
|
}
|
|
|
|
func setToken(token: Network.Model.Token?) {
|
|
if let token {
|
|
service.token = token
|
|
update()
|
|
}
|
|
}
|
|
|
|
private var selectedSection: Int = 1 {
|
|
didSet {
|
|
self.tableViews.forEach {
|
|
if let cell = $0.cellForRow(at: IndexPath(row: 0, section: 0)) as? P2pNumberFilterTableViewCell,
|
|
$0.tag == oldValue {
|
|
cell.searchTextField?.viewModel.isFirstResponder = false
|
|
}
|
|
}
|
|
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(self.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.refreshControl = 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?.name ?? "") {
|
|
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()
|
|
|
|
self.setupEmptyView()
|
|
|
|
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) { [weak self] _ in self?.update() }
|
|
|
|
Accounts().activePublisher
|
|
.sink { [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 })
|
|
}
|
|
.store(in: &self.cancellables)
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
navigationGlobalHack = self.navigationController
|
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
|
if self.didOpenChat {
|
|
self.mainController.barNav.popItem(animated: true)
|
|
self.didOpenChat = false
|
|
}
|
|
|
|
self.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)
|
|
if navigationGlobalHack == self.navigationController {
|
|
navigationGlobalHack = nil
|
|
}
|
|
}
|
|
|
|
func openRightSection() {
|
|
if let count = self.service.activeDeals(symbol: self.service.token?.symbol ?? ""),
|
|
count > 0 {
|
|
self.setTabWithSroll(index: 0)
|
|
} else if service.mineOrders.count > 0 {
|
|
self.setTabWithSroll(index: 1)
|
|
} else {
|
|
self.setTabWithSroll(index: 2)
|
|
}
|
|
}
|
|
|
|
@objc private func onRefresh() {
|
|
self.refreshAll(showAlert: true)
|
|
}
|
|
|
|
private func textFieldDidChange(_ text: String?) {
|
|
let newString = (text ?? "").removingWhitespaceAndNewlines()
|
|
if newString.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil {
|
|
self.changeServiceFilteredResults(by: newString)
|
|
}
|
|
}
|
|
|
|
private func textFieldShouldClear() {
|
|
self.changeServiceFilteredResults(by: "")
|
|
}
|
|
|
|
private func changeServiceFilteredResults(by text: String) {
|
|
switch self.selectedSection {
|
|
case 0:
|
|
self.service.numberFilterDeals = text
|
|
case 1:
|
|
self.service.numberFilterMineOrders = text
|
|
case 2:
|
|
self.service.numberFilter = text
|
|
default:
|
|
break
|
|
}
|
|
self.update()
|
|
}
|
|
|
|
private func refreshAll(showAlert: Bool) {
|
|
|
|
self.isRefreshing = true
|
|
Accounts().messageShelf.activeBook?.fetch()
|
|
Wallet.Service.Tokens.shared.balances.fetch()
|
|
|
|
let name = Accounts().current?.name ?? ""
|
|
|
|
let dispatchGroup = DispatchGroup()
|
|
var error: Error?
|
|
dispatchGroup.enter()
|
|
self.service.p2pBalancesService.fetch(username: name, completion: {
|
|
if let closureError = $0 {
|
|
error = closureError
|
|
}
|
|
dispatchGroup.leave()
|
|
})
|
|
dispatchGroup.enter()
|
|
self.service.ordersService.fetch(username: name, completion: {
|
|
if let closureError = $0 {
|
|
error = closureError
|
|
}
|
|
dispatchGroup.leave()
|
|
})
|
|
dispatchGroup.enter()
|
|
self.service.dealsService.fetch(username: name, completion: {
|
|
if let closureError = $0 {
|
|
error = closureError
|
|
}
|
|
dispatchGroup.leave()
|
|
})
|
|
dispatchGroup.notify(queue: DispatchQueue.main) {
|
|
self.isRefreshing = false
|
|
self.endRefreshing()
|
|
if showAlert {
|
|
Alert.notify(error)
|
|
}
|
|
}
|
|
self.service.p2pListsService.fetch(completion: { _ in })
|
|
}
|
|
|
|
var mineOrders: Results<P2POrder>!
|
|
var notMineOrders: Results<P2POrder>!
|
|
|
|
private func update() {
|
|
Loader.hide()
|
|
// MARK: - TO DO, look for better solution for refresh
|
|
|
|
self.startRefreshing()
|
|
|
|
// Locked Funds and Switch old deals view section
|
|
let blockedBalance = self.service.currentBalance?.blockedAmount.decimalValue ?? 0
|
|
let freeAmount = self.service.currentBalance?.amount.decimalValue ?? 0
|
|
|
|
self.tablesHeaderView.amountLabel.lzText = (blockedBalance + freeAmount)
|
|
.toReadebleString(precisionMax: self.service.token?.precision ?? 0,
|
|
symbol: self.service.token?.symbol ?? "",
|
|
group: true)
|
|
self.tablesHeaderView.dealsSwitch.isOn = !self.service.dealsService.runtimeConfiguration.dealsActiveOnly
|
|
|
|
self.tablesHeaderView.backgroundContainerView.isHidden = (blockedBalance + freeAmount) == 0
|
|
self.tablesHeaderView.switchSection.isHidden = self.selectedSection != 0
|
|
self.tablesHeaderView.isHidden = self.tablesHeaderView.backgroundContainerView.isHidden && self.tablesHeaderView.switchSection.isHidden
|
|
// End
|
|
|
|
if let sectionNotificationButtons {
|
|
if let count = service.activeDeals(symbol: self.service.token?.symbol ?? ""),
|
|
count > 0 {
|
|
self.sectionNotificationButtons[0].isHidden = false
|
|
self.sectionNotificationButtons[0].lzTitle = "\(count)"
|
|
} else {
|
|
self.sectionNotificationButtons[0].isHidden = true
|
|
}
|
|
|
|
if self.service.mineOrders.count > 0 {
|
|
self.sectionNotificationButtons[1].isHidden = false
|
|
self.sectionNotificationButtons[1].lzTitle = "\(self.service.mineOrders.count)"
|
|
} else {
|
|
self.sectionNotificationButtons[1].isHidden = true
|
|
}
|
|
|
|
self.sectionNotificationButtons[2].isHidden = true
|
|
}
|
|
|
|
self.filtersBadgeView?.isHidden = self.service.isNoFiltersApplied
|
|
|
|
if let kindCtrl = self.kindCtrl, !kindCtrl.isSelected {
|
|
self.ordersView?.image = self.service.kind == .sell
|
|
? Asset.p2pOrdersEmptySell.image
|
|
: Asset.p2pOrdersEmptyBuy.image
|
|
|
|
self.ordersView?.title = L10n.P2p.Dashboard.Orders.Empty.title
|
|
|
|
self.ordersView?.text = self.service.kind == .sell
|
|
? L10n.P2p.Dashboard.Orders.Empty.Sell.mine
|
|
: L10n.P2p.Dashboard.Orders.Empty.Buy.mine
|
|
|
|
self.emptyViews.forEach {
|
|
$0.image = self.service.kind == .sell ? Asset.p2pOrdersEmptySell.image : Asset.p2pOrdersEmptyBuy.image
|
|
|
|
$0.title = L10n.P2p.Dashboard.Orders.Empty.title
|
|
|
|
$0.text = self.service.kind == .sell
|
|
? L10n.P2p.Dashboard.Orders.Empty.Sell.mine
|
|
: L10n.P2p.Dashboard.Orders.Empty.Buy.mine
|
|
}
|
|
} else {
|
|
self.ordersView?.image = service.kind == .sell ? Asset.p2pOrdersEmptyBuy.image : Asset.p2pOrdersEmptySell.image
|
|
self.ordersView?.title = L10n.P2p.Dashboard.Orders.Empty.title
|
|
self.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 let offset, 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)
|
|
guard !self.isRefreshing else { return }
|
|
self.endRefreshing()
|
|
}
|
|
|
|
private func startRefreshing() {
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.refreshs.forEach { $0.beginRefreshing() }
|
|
}
|
|
}
|
|
|
|
private func endRefreshing() {
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.refreshs.forEach { $0.endRefreshing() }
|
|
self?.tableViews.forEach { $0.reloadSections([1], with: .automatic) }
|
|
}
|
|
}
|
|
|
|
private func setupEmptyView() {
|
|
self.tableViews.forEach {
|
|
guard let view = self.emptyViews[safe: $0.tag] else { return }
|
|
view.isHidden = true
|
|
view.frame = CGRect(x: 0, y: 50, width: $0.frame.width, height: $0.frame.height / 1.5)
|
|
$0.addSubview(view)
|
|
}
|
|
}
|
|
|
|
@IBAction func onChange(_: AnyObject?) {
|
|
self.service.isMine = !kindCtrl.isSelected
|
|
self.update()
|
|
}
|
|
|
|
@IBAction func onWidthdraw(_: AnyObject?) {
|
|
let symbol = (self.service.token?.symbol ?? "")
|
|
let blockedBalance = (self.service.currentBalance?.blockedAmount.decimalValue.toString(max: 5) ?? "0")
|
|
let amount = (self.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
|
|
self.collectionView.reloadData()
|
|
self.update()
|
|
}
|
|
|
|
@IBAction func onCards(_: AnyObject?) {
|
|
self.mainController.content.push(StoryboardScene.Cards.initial.instantiate(), animated: true)
|
|
}
|
|
|
|
@IBAction func onFilters(_: AnyObject?) {
|
|
guard let navigationController = self.navigationController else { return }
|
|
|
|
let vc = P2PFilterViewController()
|
|
vc.selectedToken = self.service.token
|
|
let nc = UINavigationController(rootViewController: vc)
|
|
|
|
vc.service = self.service.p2pListsService
|
|
vc.selectedFilterValues = self.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?) {
|
|
self.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: panView)
|
|
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) { [weak self] action in
|
|
guard let self else { return }
|
|
switch action {
|
|
case .complete:
|
|
if deal.isExpired && deal.seller == Accounts().current?.name && 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?.name && 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: deal.precision, symbol: deal.symbol)
|
|
),
|
|
.field(
|
|
title: L10n.P2p.Dashboard.Deal.Confirm.Complete.Insurance.expired,
|
|
text: deal.insurance.decimalValue.toString(max: deal.precision, 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: deal.precision, 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: deal.precision, symbol: deal.symbol)
|
|
),
|
|
.field(
|
|
title: L10n.P2p.Dashboard.Deal.Confirm.Complete.insurance,
|
|
text: deal.insurance.decimalValue.toString(max: deal.precision, 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?.name || !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: deal.precision, 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: deal.precision, 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()
|
|
self.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.name, encryptionKey: $0)
|
|
else { return }
|
|
self.didOpenChat = true
|
|
let account = deal.seller == Accounts().current?.name ? deal.buyer : deal.seller
|
|
let ctrl = CryptoChatControllerChat(username: account, unreadCount: 0, service: service)
|
|
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 }
|
|
mineOrders = service.mineOrders.freeze()
|
|
notMineOrders = service.notMineOrders.freeze()
|
|
|
|
var count: Int = 0
|
|
switch tableView.tag {
|
|
case 0: count = service.dealsToShow.count
|
|
case 1: count = mineOrders.count
|
|
case 2: count = notMineOrders.count
|
|
default: break
|
|
}
|
|
if count == 0 {
|
|
emptyViews[tableView.tag].isHidden = false
|
|
} else {
|
|
emptyViews[tableView.tag].isHidden = true
|
|
}
|
|
return count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
if indexPath.section == 0 {
|
|
let cell = tableView.dequeueReusableCell(P2pNumberFilterTableViewCell.self, indexPath: indexPath) as? P2pNumberFilterTableViewCell ?? P2pNumberFilterTableViewCell()
|
|
let placeholder = tableView.tag == 0
|
|
? L10n.P2p.Filter.Filters.searchDeals
|
|
: L10n.P2p.Filter.Filters.searchOrders
|
|
|
|
let viewModel = TextFieldViewModel(placeholder: placeholder, keyboardType: .numberPad)
|
|
viewModel.objectWillChange
|
|
.receive(on: DispatchQueue.main)
|
|
.sink { [weak self, weak viewModel] in
|
|
guard let self,
|
|
let viewModel else {
|
|
return
|
|
}
|
|
self.textFieldDidChange(viewModel.text)
|
|
if viewModel.shouldClear { self.textFieldShouldClear() }
|
|
}
|
|
.store(in: &self.cancellables)
|
|
|
|
cell.configureView(with: viewModel)
|
|
|
|
guard let searchTextField = cell.searchTextField, !searchTextField.viewModel.isFirstResponder else { return cell }
|
|
|
|
switch self.selectedSection {
|
|
case 0:
|
|
searchTextField.viewModel.text = service.numberFilterDeals
|
|
case 1:
|
|
searchTextField.viewModel.text = service.numberFilterMineOrders
|
|
case 2:
|
|
searchTextField.viewModel.text = service.numberFilter
|
|
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 < mineOrders.count else { return cell }
|
|
order = mineOrders[indexPath.row]
|
|
case 2:
|
|
guard indexPath.row < notMineOrders.count else { return cell }
|
|
order = notMineOrders[indexPath.row]
|
|
default:
|
|
return cell
|
|
}
|
|
|
|
cell.tableView = tableView
|
|
cell.setup(model: order) { [weak self] action in
|
|
guard let self else { return }
|
|
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.name, encryptionKey: $0)
|
|
else { return }
|
|
self.didOpenChat = true
|
|
let account = order.owner
|
|
let ctrl = CryptoChatControllerChat(username: account, unreadCount: 0, service: service)
|
|
|
|
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 else { return }
|
|
|
|
// if order.status == .inactive {
|
|
// edit(order: order)
|
|
// return
|
|
// }
|
|
|
|
Popup.show(
|
|
title: L10n.P2p.Dashboard.Order.Action.MoreEdit.title,
|
|
views: [
|
|
.label(
|
|
text: (self.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
|
|
) { [weak self] ctrl in
|
|
|
|
guard let self else {
|
|
Popup.hide()
|
|
return
|
|
}
|
|
|
|
if order.status == .active {
|
|
AccountViewAuthorize.showGetPrivateKey(in: self) {
|
|
Loader.show()
|
|
order.pause(privateKey: $0) { (error) in
|
|
Loader.hide()
|
|
Popup.hide()
|
|
Alert.notify(error)
|
|
self.edit(order: order)
|
|
}
|
|
}
|
|
} else {
|
|
Popup.hide()
|
|
self.edit(order: order)
|
|
}
|
|
}
|
|
})
|
|
|
|
ctrl.addAction(.init(title: L10n.P2p.Dashboard.Order.Action.moreCancel, style: .default) { [weak self] _ in
|
|
guard let self else { return }
|
|
|
|
Popup.show(
|
|
title: L10n.Common.Title.confirmation,
|
|
views: [
|
|
.label(
|
|
text: (self.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: (self.service.kind == .buy ? order.insurance.decimalValue : order.volume.decimalValue)
|
|
.toReadebleString(precisionMax: order.precision, symbol: order.symbol)
|
|
),
|
|
.field(
|
|
title: L10n.P2p.Dashboard.Order.Action.MoreCancel.deposit,
|
|
text: (order.volume.decimalValue * 0.1).toReadebleString(precisionMax: order.precision, symbol: order.symbol)
|
|
)
|
|
].dropLast(self.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))
|
|
self.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 > 0 && 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 > 0, let symbol = service.token?.symbol, let precision = service.token?.precision else { return }
|
|
AccountViewAuthorize.showGetPrivateKey(in: self) {
|
|
Loader.show()
|
|
self.service.p2pBalancesService.withdraw(privateKey: $0, symbol: symbol, precision: precision) { (error) in
|
|
Loader.hide()
|
|
Popup.hide()
|
|
Alert.notify(error, pop: self)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|