482 lines
18 KiB
Swift
482 lines
18 KiB
Swift
//
|
|
// CryptoChatControllerChats.swift
|
|
// PayCash
|
|
//
|
|
// Created by Saveliy Stavitsky on 8/17/20.
|
|
// Copyright © 2020 List. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import IQKeyboardManagerSwift
|
|
import EosioSwift
|
|
import Branch
|
|
|
|
import struct RealmSwift.Results
|
|
|
|
class CryptoChatControllerChats: UIViewController {
|
|
|
|
@IBOutlet private weak var tableView: UITableView!
|
|
@IBOutlet weak var headerContainerView: UIView!
|
|
@IBOutlet weak var addChatButton: UIButton!
|
|
@IBOutlet weak var chatsListLabel: UILabel!
|
|
|
|
var msgsHistoryService: CryptoChat.Service.MsgsHistory?
|
|
|
|
private let refreshControl = UIRefreshControl()
|
|
|
|
let header = CryptoChatViewChatsHeader(width: UIScreen.main.bounds.width, height: 120)
|
|
var chats: Results<CryptoChatModelRealmChat>?
|
|
var msgs: Results<CryptoChatModelRealmMessage>?
|
|
|
|
private lazy var noAccountsView: CommonViewEmpty = {
|
|
CommonViewEmpty(
|
|
title: L10n.CryptoChat.Chats.NoAccounts.title,
|
|
text: L10n.Account.Empty.text,
|
|
image: Asset.accountEmpty.image,
|
|
backgroundColor: Asset.snow.color,
|
|
submit: L10n.Account.Empty.submit
|
|
) { [weak self] in
|
|
guard let self = self else { return }
|
|
AccountController.showPopup(in: self)
|
|
}
|
|
}()
|
|
|
|
private lazy var emptyView: CommonViewEmpty = {
|
|
CommonViewEmpty(
|
|
title: L10n.CryptoChat.Chats.Empty.title,
|
|
text: L10n.CryptoChat.Chats.Empty.text,
|
|
image: Asset.chatsEmpty.image,
|
|
submit: L10n.CryptoChat.Chats.Empty.submit,
|
|
submitRealtion: .top
|
|
) { [weak self] in
|
|
guard let self = self else { return }
|
|
self.showSelectChatAddMethodPopup()
|
|
}
|
|
}()
|
|
|
|
private lazy var encodedView: CommonViewEmpty = {
|
|
CommonViewEmpty(
|
|
title: L10n.CryptoChat.Chats.Encoded.title,
|
|
text: L10n.CryptoChat.Chats.encoded,
|
|
image: Asset.chatsLocked.image,
|
|
submit: L10n.CryptoChat.Chats.encodedButton,
|
|
submitRealtion: .top
|
|
) { [weak self] in
|
|
guard let self = self else { return }
|
|
self.onAccountChange(Notification(name: .didChangeAccount))
|
|
}
|
|
}()
|
|
|
|
var bottomSpace: CGFloat = 0
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
navigationController?.interactivePopGestureRecognizer?.delegate = nil
|
|
|
|
navigationItem.title = L10n.CryptoChat.Chats.title
|
|
|
|
tableView.showsVerticalScrollIndicator = false
|
|
tableView.separatorStyle = .none
|
|
(tableView as UIScrollView).delegate = self
|
|
|
|
tableView.register(cell: CryptoChatCellChat.self)
|
|
|
|
refreshControl.addTarget(self, action: #selector(self.refresh(_:)), for: .valueChanged)
|
|
tableView.addSubview(refreshControl)
|
|
|
|
Notification.subscribe(name: .didUpdateHistory, {
|
|
guard Accounts().current?.username == $0.userInfo?["username"] as? String else { return }
|
|
self.msgsHistoryService?.fetchFromLocalHistory()
|
|
})
|
|
|
|
self.addChatButton.addTarget(self, action: #selector(self.addButtonPressed(_:)), for: .touchUpInside)
|
|
|
|
self.setupView()
|
|
}
|
|
|
|
func setup(username: String) {
|
|
AccountViewAuthorize.showGetPrivateKey(in: self, { [weak self] privateKey in
|
|
self?.msgsHistoryService = try? CryptoChat.Service
|
|
.MsgsHistory(username: username, encryptionKey: privateKey)
|
|
self?.msgsHistoryService?.didUpdate = { [weak self] in
|
|
DispatchQueue.main.async {
|
|
self?.refreshControl.endRefreshing()
|
|
self?.tableView.reloadData()
|
|
}
|
|
}
|
|
self?.chats = self?.msgsHistoryService?.chats
|
|
self?.msgs = self?.msgsHistoryService?.filterMsgs(query: "")
|
|
self?.header.searchTextField.text = ""
|
|
self?.tableView.reloadData()
|
|
Account.Service.History.shared.current?.fetch()
|
|
})
|
|
}
|
|
|
|
@objc
|
|
func addButtonPressed(_ sender: Any) {
|
|
self.showSelectChatAddMethodPopup()
|
|
}
|
|
|
|
@objc func onAccountChange(_ notification: Notification) {
|
|
if let username = Accounts().current?.username {
|
|
msgsHistoryService = nil
|
|
chats = nil
|
|
msgs = nil
|
|
tableView.reloadData()
|
|
|
|
setup(username: username)
|
|
}
|
|
Accounts().exists ? noAccountsView.removeFromSuperview() : view.addSubview(noAccountsView)
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
Accounts().exists ? noAccountsView.removeFromSuperview() : view.addSubview(noAccountsView)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(onAccountChange(_:)), name: .didChangeAccount, object: nil)
|
|
|
|
navigationController?.setNavigationBarHidden(true, animated: animated)
|
|
// IQKeyboardManager.shared.enable = false
|
|
|
|
refresh(self)
|
|
|
|
if ((UIApplication.shared.windows.first!.rootViewController as? MainController)?.barNav.items?.count ?? 0) > 1 {
|
|
(UIApplication.shared.windows.first!.rootViewController as? MainController)?.barNav.popItem(animated: true)
|
|
}
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
|
|
NotificationCenter.default.removeObserver(self, name: .didChangeAccount, object: nil)
|
|
msgsHistoryService = nil
|
|
chats = nil
|
|
msgs = nil
|
|
tableView.reloadData()
|
|
navigationController?.setNavigationBarHidden(false, animated: animated)
|
|
// IQKeyboardManager.shared.enable = true
|
|
}
|
|
|
|
override func viewDidLayoutSubviews() {
|
|
super.viewDidLayoutSubviews()
|
|
if bottomSpace == 0 {
|
|
bottomSpace += (self.tabBarController?.tabBar.frameHeight ?? 0) /*tab bar*/
|
|
+ 40 /*button height*/ + 24 /*button top space*/
|
|
}
|
|
noAccountsView.frame = tableView.frame
|
|
emptyView.frame = tableView.frame
|
|
encodedView.frame = tableView.frame
|
|
}
|
|
|
|
private func setupView() {
|
|
self.addChatButton.layer.borderColor = Asset.deepWater.color.withAlphaComponent(0.2).cgColor
|
|
self.addChatButton.layer.borderWidth = 1.0
|
|
self.addChatButton.layer.cornerRadius = 8.0
|
|
}
|
|
|
|
@objc func refresh(_ sender: AnyObject) {
|
|
guard let username = Accounts().current?.username else { return }
|
|
if msgsHistoryService == nil {
|
|
setup(username: username)
|
|
} else {
|
|
Account.Service.History.shared.current?.fetch()
|
|
}
|
|
}
|
|
|
|
func openChat(username: String, unreadCount: Int) {
|
|
guard username.count > 0, msgsHistoryService != nil else { return }
|
|
let chatViewController = CryptoChatControllerChat()
|
|
chatViewController.username = username
|
|
chatViewController.msgsHistoryService = msgsHistoryService
|
|
chatViewController.unreadCount = unreadCount
|
|
|
|
mainController.content.push(chatViewController, animated: true)
|
|
}
|
|
|
|
var query = ""
|
|
}
|
|
|
|
extension CryptoChatControllerChats: UITableViewDelegate, UITableViewDataSource {
|
|
|
|
func numberOfSections(in tableView: UITableView) -> Int {
|
|
1 + ((msgs?.count ?? 0) > 0 ? 1 : 0)
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
if Accounts().current != nil {
|
|
if msgsHistoryService == nil {
|
|
emptyView.removeFromSuperview()
|
|
Loader.hide(in: tableView)
|
|
view.addSubview(encodedView)
|
|
} else {
|
|
if !UserDefaults.standard.bool(forKey: "\(Accounts().current!.username)_firstMsgsSyncDone") {
|
|
Loader.show(in: tableView)
|
|
} else {
|
|
Loader.hide(in: tableView)
|
|
(chats?.count ?? 0) == 0 && query.isEmpty ? view.addSubview(emptyView) : emptyView.removeFromSuperview()
|
|
}
|
|
encodedView.removeFromSuperview()
|
|
}
|
|
[
|
|
self.addChatButton,
|
|
self.header.searchTextField
|
|
]
|
|
.forEach {
|
|
$0?.isHidden = (msgsHistoryService == nil)
|
|
|| !UserDefaults.standard.bool(forKey: "\(Accounts().current!.username)_firstMsgsSyncDone")
|
|
|| ((chats?.count ?? 0) == 0 && query.isEmpty)
|
|
}
|
|
} else {
|
|
Loader.hide(in: tableView)
|
|
emptyView.removeFromSuperview()
|
|
encodedView.removeFromSuperview()
|
|
}
|
|
return section == 0 ? (chats?.count ?? 0) : (msgs?.count ?? 0)
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell = tableView.dequeueReusableCell(CryptoChatCellChat.self, indexPath: indexPath)
|
|
|
|
if indexPath.section == 0 {
|
|
guard let chat = chats?[indexPath.row] else { return cell }
|
|
|
|
cell.titleLabel.text = chat.chatName
|
|
cell.titleShortLabel.text = String(chat.chatName.prefix(3))
|
|
cell.descriptionLabel.text = chat.textDescription
|
|
cell.timeLabel.text = chat.time
|
|
cell.setUnread(count: chat.unreadCount)
|
|
} else {
|
|
guard let msg = msgs?[indexPath.row] else { return cell }
|
|
|
|
cell.titleLabel.text = msg.chatName
|
|
cell.titleShortLabel.text = String(msg.chatName.prefix(3))
|
|
cell.descriptionLabel.text = msg.textDescription
|
|
cell.timeLabel.text = msg.time
|
|
cell.setUnread(count: nil)
|
|
}
|
|
|
|
return cell
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
|
if section == 0 {
|
|
header.parent = self
|
|
header.searchTextField.delegate = self
|
|
return header
|
|
} else {
|
|
let button = CommonButtonAction(width: UIScreen.main.bounds.width, height: 40)
|
|
button.style = .secondary
|
|
button.backgroundColor = Asset.deepWater.color.withAlphaComponent(0.1)
|
|
button.isUserInteractionEnabled = false
|
|
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 24)
|
|
button.cornerRadius = 0
|
|
button.setTitle(L10n.CryptoChat.Chats.Search.msgsSection, for: .normal)
|
|
button.setTitleColor(Asset.textDeepWater.color, for: .normal)
|
|
button.titleLabel?.font = Font.font(style: .bold, size: 12)
|
|
button.contentHorizontalAlignment = .left
|
|
return UIStackView(subviews: [UIView(height: 16, color: .white), button], axis: .vertical, distribution: .fill, alignment: .fill, spacing: 0)
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
|
section == 0 ? 65 : 56
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
|
|
if indexPath.section == 0 {
|
|
guard let chat = chats?[indexPath.row] else { return }
|
|
openChat(username: chat.chatName, unreadCount: chat.unreadCount)
|
|
} else {
|
|
guard let msg = msgs?[indexPath.row] else { return }
|
|
openChat(username: msg.chatName, unreadCount: 0)
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
|
if (msgs?.count ?? 0) > 0 {
|
|
return section == 1 ? UIView(height: 56, color: .white) : UIView(height: 0)
|
|
} else {
|
|
return UIView(height: 56, color: .white)
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
|
section == 1 ? 56 : 0
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
|
|
|
|
if !query.isEmpty { return nil }
|
|
guard let chat = chats?[indexPath.row] else { return nil }
|
|
|
|
let delete = UITableViewRowAction(style: .destructive, title: L10n.Common.Button.delete) { [weak self] (action, indexPath) in
|
|
self?.msgsHistoryService?.hideChatMsgs(chatName: chat.chatName)
|
|
}
|
|
|
|
return [delete]
|
|
}
|
|
}
|
|
|
|
extension CryptoChatControllerChats: CommonTextFieldSearchDelegate {
|
|
|
|
func textField(_ textField: CommonTextFieldSearch, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
|
if let text = textField.text {
|
|
let updatedText = (text as NSString).replacingCharacters(in: range, with: string)
|
|
if updatedText.isEmpty {
|
|
chats = msgsHistoryService?.chats
|
|
msgs = msgsHistoryService?.filterMsgs(query: "")
|
|
query = ""
|
|
} else {
|
|
chats = msgsHistoryService?.filterChats(query: updatedText)
|
|
msgs = msgsHistoryService?.filterMsgs(query: updatedText)
|
|
query = updatedText
|
|
}
|
|
} else {
|
|
chats = msgsHistoryService?.chats
|
|
msgs = msgsHistoryService?.filterMsgs(query: "")
|
|
query = ""
|
|
}
|
|
|
|
tableView.reloadData()
|
|
|
|
return true
|
|
}
|
|
|
|
func textFieldShouldClear(_ textField: CommonTextFieldSearch) -> Bool {
|
|
chats = msgsHistoryService?.chats
|
|
msgs = msgsHistoryService?.filterMsgs(query: "")
|
|
query = ""
|
|
tableView.reloadData()
|
|
return true
|
|
}
|
|
}
|
|
|
|
// MARK: - Opening QR to select New Chat Add Method
|
|
extension CryptoChatControllerChats: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
func showSelectChatAddMethodPopup() {
|
|
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
|
|
|
let openScannerAction: ((UIAlertAction) -> Void) = { [weak self] _ in
|
|
self?.openScannerQrViewController { [weak self] in
|
|
self?.dismiss(animated: true)
|
|
let username = self?.injectUsername(string: $0) ?? .init()
|
|
guard !username.isEmpty else { return }
|
|
|
|
switch username {
|
|
case let username where URL(string: username)?.absoluteString.contains("app.link") ?? false:
|
|
Branch.getInstance().application(UIApplication.shared, open: URL(string: username), options: nil)
|
|
default:
|
|
self?.validateEosAccount(username: username)
|
|
}
|
|
}
|
|
}
|
|
|
|
let cryptoChatCreateAction: ((UIAlertAction) -> Void) = { [weak self] _ in
|
|
self?.openCryptoChatCreatePopup()
|
|
}
|
|
|
|
let imagePickerAction: ((UIAlertAction) -> Void) = { [weak self] _ in
|
|
self?.openImagePickerViewController()
|
|
}
|
|
|
|
alert.addAction(.init(title: L10n.CryptoChat.Chats.Popup.findAccount, style: .default, handler: cryptoChatCreateAction))
|
|
|
|
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
|
|
alert.addAction(.init(title: L10n.CryptoChat.Chats.Popup.qrLibrary, style: .default, handler: imagePickerAction))
|
|
}
|
|
|
|
if UIImagePickerController.isSourceTypeAvailable(.camera) {
|
|
alert.addAction(.init(title: L10n.CryptoChat.Chats.Popup.qrCamera, style: .default, handler: openScannerAction))
|
|
}
|
|
|
|
alert.addAction(.init(title: L10n.Common.Button.cancel, style: .cancel, handler: nil))
|
|
self.present(alert, animated: true)
|
|
}
|
|
|
|
private func injectUsername(string: String) -> String? {
|
|
guard let stringData = string.data(using: .utf8) else { return string }
|
|
let addressDecoder = try? JSONDecoder().decode(WalletTansferQr.self, from: stringData)
|
|
|
|
return addressDecoder?.address ?? string
|
|
}
|
|
|
|
private func validateEosAccount(username: String) {
|
|
EosioRpcProvider(endpoint: URL(string: Network.servers.current.node)!).getAccount(requestParameters: EosioRpcAccountRequest(accountName: username), completion: { [weak self] in
|
|
switch $0 {
|
|
case .success:
|
|
self?.openChat(username: username, unreadCount: 0)
|
|
case .failure:
|
|
Alert.error(text: L10n.CryptoChat.Chats.createTextFieldError)
|
|
}
|
|
})
|
|
}
|
|
|
|
// MARK: UIImagePickerControllerDelegate & UINavigationControllerDelegate delegate methods
|
|
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
|
self.dismiss(animated: true, completion: nil)
|
|
|
|
if let selectedImage = info[.originalImage] as? UIImage,
|
|
let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]),
|
|
let ciImage = CIImage(image: selectedImage) {
|
|
|
|
let qrCodeString =
|
|
(detector.features(in: ciImage) as? [CIQRCodeFeature])?
|
|
.compactMap(\.messageString)
|
|
.joined()
|
|
?? String.init()
|
|
|
|
guard let username = self.injectUsername(string: qrCodeString) else {
|
|
return
|
|
}
|
|
|
|
guard !username.isEmpty else {
|
|
Alert.error(text: L10n.CryptoChat.Chats.noQr, delay: 1)
|
|
return
|
|
}
|
|
|
|
switch username {
|
|
case let username where URL(string: username)?.absoluteString.contains("app.link") ?? false:
|
|
Branch.getInstance().application(UIApplication.shared, open: URL(string: username), options: nil)
|
|
default:
|
|
self.validateEosAccount(username: username)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Routing
|
|
extension CryptoChatControllerChats {
|
|
fileprivate func openImagePickerViewController() {
|
|
let imagePicker = UIImagePickerController()
|
|
imagePicker.delegate = self
|
|
imagePicker.sourceType = .photoLibrary
|
|
self.present(imagePicker, animated: true, completion: nil)
|
|
}
|
|
|
|
fileprivate func openCryptoChatCreatePopup() {
|
|
let view = CryptoChatViewCreateChat()
|
|
view.parent = self
|
|
Popup.show(content: CommonViewControllerViewPopUp(
|
|
title: L10n.CryptoChat.Chats.craateDescription,
|
|
image: Asset.chatCreate.image,
|
|
view: view
|
|
))
|
|
}
|
|
|
|
fileprivate func openScannerQrViewController(onAction: @escaping (String) -> Void) {
|
|
let scanner = ScannerViewController()
|
|
|
|
scanner.navigationItem.leftBarButtonItem = .close { [weak self] in
|
|
self?.dismiss(animated: true)
|
|
}
|
|
|
|
scanner.didCapture = onAction
|
|
|
|
let navCtrl = UINavigationController(rootViewController: scanner)
|
|
navCtrl.modalPresentationStyle = .fullScreen
|
|
self.navigationController?.tabBarController?.present(navCtrl, animated: true)
|
|
}
|
|
}
|