From 35204e88b6f4fff4955194a03a587f9fbddd7180 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 20 May 2022 19:24:04 +0300 Subject: [PATCH 01/12] New chatrooms api --- Adamant.xcodeproj/project.pbxproj | 12 +++ Adamant/Helpers/UITableView+adamant.swift | 57 ++++++++++++ Adamant/ServiceProtocols/ApiService.swift | 5 ++ .../DataProviders/ChatsProvider.swift | 5 ++ .../ApiService/AdamantApi+Chats.swift | 58 ++++++++++++- .../DataProviders/AdamantChatsProvider.swift | 87 ++++++++++++++++++- .../Account/AccountViewController.swift | 3 + .../Chats/ChatListViewController.swift | 13 +++ .../Stories/Chats/ChatViewController.swift | 37 ++++++++ AdamantShared/Models/ChatRooms.swift | 32 +++++++ AdamantShared/Models/ChatRoomsChats.swift | 24 +++++ AdamantShared/Models/Transaction.swift | 2 +- 12 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 Adamant/Helpers/UITableView+adamant.swift create mode 100644 AdamantShared/Models/ChatRooms.swift create mode 100644 AdamantShared/Models/ChatRoomsChats.swift diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index dfae408fc..3833a9293 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -14,6 +14,9 @@ 3A8875EF27BBF38D00436195 /* Parchment in Frameworks */ = {isa = PBXBuildFile; productRef = 3A8875EE27BBF38D00436195 /* Parchment */; }; 3AA2D5F7280EADE3000ED971 /* SocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F6280EADE3000ED971 /* SocketService.swift */; }; 3AA2D5FA280EAF5D000ED971 /* AdamantSocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */; }; + 3AE89DD72837F42D0051D3A9 /* UITableView+adamant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */; }; + 3AE89DD92837F5BF0051D3A9 /* ChatRooms.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */; }; + 3AE89DDB2837F5D30051D3A9 /* ChatRoomsChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */; }; 3C06931576393125C61FB8F6 /* Pods_Adamant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33975C0D891698AA7E74EBCC /* Pods_Adamant.framework */; }; 553B1284281C6EE100FFF24C /* GCDUtilites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553B1283281C6EE100FFF24C /* GCDUtilites.swift */; }; 6403F5DB2272389800D58779 /* (null) in Sources */ = {isa = PBXBuildFile; }; @@ -612,6 +615,9 @@ 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantSecret.swift; sourceTree = ""; }; 3AA2D5F6280EADE3000ED971 /* SocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketService.swift; sourceTree = ""; }; 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantSocketService.swift; sourceTree = ""; }; + 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+adamant.swift"; sourceTree = ""; }; + 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRooms.swift; sourceTree = ""; }; + 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomsChats.swift; sourceTree = ""; }; 4A4D67BD3DC89C07D1351248 /* Pods-AdmCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdmCore.release.xcconfig"; path = "Target Support Files/Pods-AdmCore/Pods-AdmCore.release.xcconfig"; sourceTree = ""; }; 553B1283281C6EE100FFF24C /* GCDUtilites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCDUtilites.swift; sourceTree = ""; }; 6403F5DD22723C6800D58779 /* DashMainnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashMainnet.swift; sourceTree = ""; }; @@ -1354,6 +1360,7 @@ E9256F5E2034C21100DE86E9 /* String+localized.swift */, E98FC34320F920BD00032D65 /* UIFont+adamant.swift */, 645AE06521E67D3300AD3623 /* UITextField+adamant.swift */, + 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */, ); path = Helpers; sourceTree = ""; @@ -1742,6 +1749,8 @@ E93EB0A220DA4CCA001F9601 /* Node.swift */, E965A53320B833A00041A3EA /* StateAsset.swift */, E965A53120B82C850041A3EA /* StateType.swift */, + 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */, + 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */, E9E7CDC920040CC200DFC4DB /* Transaction.swift */, E965A53520B8370C0041A3EA /* TransactionAsset.swift */, E9E7CDCB20040FDC00DFC4DB /* TransactionType.swift */, @@ -2536,6 +2545,7 @@ E9AA8BFA212C166600F9249F /* EthWalletService+Send.swift in Sources */, 6449BA68235CA0930033B936 /* ERC20WalletService.swift in Sources */, 644793C32166314A00FC4CF5 /* OnboardPage.swift in Sources */, + 3AE89DDB2837F5D30051D3A9 /* ChatRoomsChats.swift in Sources */, 64E1C831222E9617006C4DA7 /* DogeWalletService.swift in Sources */, E91947B22000246A001362F8 /* AdamantError.swift in Sources */, 3AA2D5F7280EADE3000ED971 /* SocketService.swift in Sources */, @@ -2590,6 +2600,7 @@ E91947AC20001A9A001362F8 /* ApiService.swift in Sources */, E96D64B72295BED700CA5587 /* DelegateVote.swift in Sources */, E993302221354BC300CD5200 /* EthWalletRoutes.swift in Sources */, + 3AE89DD92837F5BF0051D3A9 /* ChatRooms.swift in Sources */, E90055F520EBF5DA00D0CB2D /* AboutViewController.swift in Sources */, E965A53020B594120041A3EA /* AdamantApi+States.swift in Sources */, E96D64BB2295BED700CA5587 /* VotesAsset.swift in Sources */, @@ -2670,6 +2681,7 @@ 64EE46B220FE0C8D00194DDA /* LskTransactionsViewController.swift in Sources */, E94008832114EE4700CD2D67 /* LskWallet.swift in Sources */, 64E1C833222EA0F0006C4DA7 /* DogeWalletViewController.swift in Sources */, + 3AE89DD72837F42D0051D3A9 /* UITableView+adamant.swift in Sources */, E96D64BA2295BED700CA5587 /* TransactionType.swift in Sources */, E93EB09F20DA3FA4001F9601 /* NodesEditorRoutes.swift in Sources */, 6489794F24CE00C000C33A68 /* SwiftyOnboardOverlay.swift in Sources */, diff --git a/Adamant/Helpers/UITableView+adamant.swift b/Adamant/Helpers/UITableView+adamant.swift new file mode 100644 index 000000000..cd939687a --- /dev/null +++ b/Adamant/Helpers/UITableView+adamant.swift @@ -0,0 +1,57 @@ +// +// UITableView+adamant.swift +// Adamant +// +// Created by Stanislav Jelezoglo on 20.05.2022. +// Copyright © 2022 Adamant. All rights reserved. +// + +import Foundation +import UIKit + +extension UITableView { + func indicatorView() -> UIActivityIndicatorView{ + var activityIndicatorView = UIActivityIndicatorView() + if self.tableFooterView == nil { + let indicatorFrame = CGRect(x: 0, y: 0, width: self.bounds.width, height: 60) + activityIndicatorView = UIActivityIndicatorView(frame: indicatorFrame) + activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin] + + if #available(iOS 13.0, *) { + activityIndicatorView.style = .medium + } else { + activityIndicatorView.style = .gray + } + + activityIndicatorView.color = .lightGray + activityIndicatorView.hidesWhenStopped = true + + self.tableFooterView = activityIndicatorView + return activityIndicatorView + } + else { + return activityIndicatorView + } + } + + func addLoading(_ indexPath:IndexPath, closure: @escaping (() -> Void)){ + if let lastVisibleIndexPath = self.indexPathsForVisibleRows?.last { + if indexPath == lastVisibleIndexPath && indexPath.row == self.numberOfRows(inSection: 0) - 1 { + DispatchQueue.main.async() { + self.indicatorView().startAnimating() + closure() + } + } + } + } + + func stopLoading() { + if self.tableFooterView != nil { + self.indicatorView().stopAnimating() + self.tableFooterView = nil + } + else { + self.tableFooterView = nil + } + } +} diff --git a/Adamant/ServiceProtocols/ApiService.swift b/Adamant/ServiceProtocols/ApiService.swift index 8e8695a6e..37c477114 100644 --- a/Adamant/ServiceProtocols/ApiService.swift +++ b/Adamant/ServiceProtocols/ApiService.swift @@ -147,6 +147,11 @@ protocol ApiService: AnyObject { func getTransactions(forAccount: String, type: TransactionType, fromHeight: Int64?, offset: Int?, limit: Int?, completion: @escaping (ApiServiceResult<[Transaction]>) -> Void) + // MARK: - Chats Rooms + + func getChatRooms(address: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) + func getChatMessages(address: String, addressRecipient: String, offset: Int?, completion: @escaping (ApiServiceResult<[Transaction]?>) -> Void) + // MARK: - Funds func transferFunds(sender: String, recipient: String, amount: Decimal, keypair: Keypair, completion: @escaping (ApiServiceResult) -> Void) diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index bcd5dbf7d..40916264d 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -174,9 +174,14 @@ protocol ChatsProvider: DataProvider { var chatPositon: [String: Double] { get set } var blackList: [String] { get } + var roomsMaxCount: Int? { get } + var roomsLoadedCount: Int? { get } + // MARK: - Getting chats and messages func getChatroomsController() -> NSFetchedResultsController func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController + func getChatRooms(offset: Int?, completion: (() ->())?) + func getChatMessages(with addressRecipient: String, offset: Int?, completion: (() ->())?) /// Unread messages controller. Sections by chatroom. func getUnreadMessagesController() -> NSFetchedResultsController diff --git a/Adamant/Services/ApiService/AdamantApi+Chats.swift b/Adamant/Services/ApiService/AdamantApi+Chats.swift index c9cfd29ca..fa90ee273 100644 --- a/Adamant/Services/ApiService/AdamantApi+Chats.swift +++ b/Adamant/Services/ApiService/AdamantApi+Chats.swift @@ -14,7 +14,8 @@ extension AdamantApiService.ApiCommands { root: "/api/chats", get: "/api/chats/get", normalizeTransaction: "/api/chats/normalize", - processTransaction: "/api/chats/process" + processTransaction: "/api/chats/process", + getChatRooms: "/api/chatrooms" ) } @@ -133,4 +134,59 @@ extension AdamantApiService { } } } + + // new api + func getChatRooms(address: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) { + // MARK: 1. Prepare params + var queryItems: [URLQueryItem] = [] + if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } + + // MARK: 2. Build endpoint + let endpoint: URL + do { + endpoint = try buildUrl(path: ApiCommands.Chats.getChatRooms + "/\(address)", queryItems: queryItems) + } catch { + let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) + completion(.failure(err)) + return + } + + // MARK: 3. Send + sendRequest(url: endpoint) { (serverResponse: ApiServiceResult) in + switch serverResponse { + case .success(let response): + completion(.success(response)) + + case .failure(let error): + completion(.failure(.networkError(error: error))) + } + } + } + + func getChatMessages(address: String, addressRecipient: String, offset: Int?, completion: @escaping (ApiServiceResult<[Transaction]?>) -> Void) { + // MARK: 1. Prepare params + var queryItems: [URLQueryItem] = [] + if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } + + // MARK: 2. Build endpoint + let endpoint: URL + do { + endpoint = try buildUrl(path: ApiCommands.Chats.getChatRooms + "/\(address)/\(addressRecipient)", queryItems: queryItems) + } catch { + let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) + completion(.failure(err)) + return + } + + // MARK: 3. Send + sendRequest(url: endpoint) { (serverResponse: ApiServiceResult) in + switch serverResponse { + case .success(let response): + completion(.success(response.messages)) + + case .failure(let error): + completion(.failure(.networkError(error: error))) + } + } + } } diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 57bace107..90d47125f 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -54,6 +54,9 @@ class AdamantChatsProvider: ChatsProvider { private var previousAppState: UIApplication.State? + private(set) var roomsMaxCount: Int? + private(set) var roomsLoadedCount: Int? + // MARK: Lifecycle init() { NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedIn, object: nil, queue: nil) { [weak self] notification in @@ -79,7 +82,8 @@ class AdamantChatsProvider: ChatsProvider { self?.dropStateData() store.set(loggedAddress, for: StoreKey.chatProvider.address) } - self?.update() + + self?.getChatRooms(offset: nil, completion: nil) self?.connectToSocket() } @@ -121,7 +125,9 @@ class AdamantChatsProvider: ChatsProvider { } NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] notification in - self?.previousAppState = .background + if self?.isInitiallySynced ?? false { + self?.previousAppState = .background + } } } @@ -196,6 +202,83 @@ extension AdamantChatsProvider { setState(.empty, previous: prevState, notify: notify) } + func getChatRooms(offset: Int?, completion: (() ->())?) { + guard let address = accountService.account?.address, + let privateKey = accountService.keypair?.privateKey else { + return + } + + let cms = DispatchSemaphore(value: 1) + // MARK: 3. Get transactions + let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + privateContext.parent = self.stack.container.viewContext + apiService.getChatRooms(address: address, offset: offset) { [weak self] result in + switch result { + case .success(let trans): + self?.roomsMaxCount = trans.count + if let roomsLoadedCount = self?.roomsLoadedCount { + self?.roomsLoadedCount = roomsLoadedCount + (trans.chats?.count ?? 0) + } else { + self?.roomsLoadedCount = trans.chats?.count ?? 0 + } + + var array = Array() + trans.chats?.forEach({ room in + if let last = room.lastTransaction { + array.append(last) + } + }) + + self?.processingQueue.async { + self?.process(messageTransactions: array, + senderId: address, + privateKey: privateKey, + context: privateContext, + contextMutatingSemaphore: cms) + } + + if let synced = self?.isInitiallySynced, !synced { + self?.processingQueue.asyncAfter(deadline: .now() + 1) { + self?.isInitiallySynced = true + } + } + completion?() + case .failure(_): + completion?() + } + } + } + + func getChatMessages(with addressRecipient: String, offset: Int?, completion: (() ->())?) { + guard let address = accountService.account?.address, + let privateKey = accountService.keypair?.privateKey else { + return + } + + let cms = DispatchSemaphore(value: 1) + // MARK: 3. Get transactions + let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + privateContext.parent = self.stack.container.viewContext + + apiService.getChatMessages(address: address, addressRecipient: addressRecipient, offset: offset) { [weak self] result in + switch result { + case .success(let transactions): + if let transactions = transactions { + self?.processingQueue.async { + self?.process(messageTransactions: transactions, + senderId: address, + privateKey: privateKey, + context: privateContext, + contextMutatingSemaphore: cms) + } + } + completion?() + case .failure(_): + completion?() + } + } + } + func update() { self.update(completion: nil) } diff --git a/Adamant/Stories/Account/AccountViewController.swift b/Adamant/Stories/Account/AccountViewController.swift index a299672f8..2d0ca635f 100644 --- a/Adamant/Stories/Account/AccountViewController.swift +++ b/Adamant/Stories/Account/AccountViewController.swift @@ -680,6 +680,9 @@ class AccountViewController: FormViewController { if UIScreen.main.traitCollection.userInterfaceIdiom == .pad, !initiated { layoutTableHeaderView() layoutTableFooterView() + if !initiated { + initiated = true + } } pagingViewController?.indicatorColor = UIColor.adamant.primary diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index b4660d0ed..238f7c9a4 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -402,6 +402,19 @@ extension ChatListViewController { if let cell = cell as? ChatTableViewCell, let chat = chatsController?.object(at: indexPath) { configureCell(cell, for: chat) } + + if let roomsLoadedCount = chatsProvider.roomsLoadedCount, + let roomsMaxCount = chatsProvider.roomsMaxCount, + roomsLoadedCount < roomsMaxCount, + tableView.numberOfRows(inSection: 0) >= roomsLoadedCount { + tableView.addLoading(indexPath) { [weak self] in + self?.chatsProvider.getChatRooms(offset: roomsLoadedCount, completion: { + DispatchQueue.main.async { + tableView.stopLoading() + } + }) + } + } } private func configureCell(_ cell: ChatTableViewCell, for chatroom: Chatroom) { diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index a06b95a39..dcb6f4d9c 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -196,6 +196,11 @@ class ChatViewController: MessagesViewController { return queue }() + // MARK: Busy indicator + + private var busyBackgroundView: UIView? + private var spinner = UIActivityIndicatorView(style: .whiteLarge) + // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() @@ -422,6 +427,16 @@ class ChatViewController: MessagesViewController { UIMenuController.shared.menuItems = [ UIMenuItem(title: String.adamantLocalized.chat.remove, action: NSSelectorFromString("remove:")), UIMenuItem(title: String.adamantLocalized.chat.report, action: NSSelectorFromString("report:"))] + + setBusyIndicator(state: true) + if let address = chatroom.partner?.address { + chatsProvider.getChatMessages(with: address, offset: 0) { + print("loaded") + DispatchQueue.main.async { + self.setBusyIndicator(state: false) + } + } + } } override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool { @@ -1024,3 +1039,25 @@ private class StatusUpdateProcedure: Procedure { } } } + +// MARK: - Busy Indicator View +extension ChatViewController { + func setBusyIndicator(state: Bool) { + if busyBackgroundView == nil { + busyBackgroundView = UIView() + busyBackgroundView?.backgroundColor = UIColor(white: 0, alpha: 0.2) + busyBackgroundView?.frame = view.frame + view.addSubview(busyBackgroundView!) + + spinner.translatesAutoresizingMaskIntoConstraints = false + spinner.startAnimating() + busyBackgroundView?.addSubview(spinner) + spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + } + + if !state { + busyBackgroundView?.removeFromSuperview() + } + } +} diff --git a/AdamantShared/Models/ChatRooms.swift b/AdamantShared/Models/ChatRooms.swift new file mode 100644 index 000000000..fcd712c36 --- /dev/null +++ b/AdamantShared/Models/ChatRooms.swift @@ -0,0 +1,32 @@ +// +// ChatRooms.swift +// Adamant +// +// Created by Stanislav Jelezoglo on 20.05.2022. +// Copyright © 2022 Adamant. All rights reserved. +// + +import Foundation +struct ChatRooms : Codable { + let chats : [ChatRoomsChats]? + let messages : [Transaction]? + let count : Int? + + enum CodingKeys: String, CodingKey { + case chats = "chats" + case count = "count" + case messages = "messages" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + chats = try values.decodeIfPresent([ChatRoomsChats].self, forKey: .chats) + count = Int(try (values.decodeIfPresent(String.self, forKey: .count) ?? "0")) ?? 0 + messages = try values.decodeIfPresent([Transaction].self, forKey: .messages) + } + +} + +extension ChatRooms: WrappableCollection { + static let CollectionKey = "chatRooms" +} diff --git a/AdamantShared/Models/ChatRoomsChats.swift b/AdamantShared/Models/ChatRoomsChats.swift new file mode 100644 index 000000000..42b4e53b0 --- /dev/null +++ b/AdamantShared/Models/ChatRoomsChats.swift @@ -0,0 +1,24 @@ +// +// ChatRoomsChat.swift +// Adamant +// +// Created by Stanislav Jelezoglo on 20.05.2022. +// Copyright © 2022 Adamant. All rights reserved. +// + +import Foundation + +struct ChatRoomsChats : Codable { + + let lastTransaction : Transaction? + + enum CodingKeys: String, CodingKey { + case lastTransaction = "lastTransaction" + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + lastTransaction = try values.decodeIfPresent(Transaction.self, forKey: .lastTransaction) + } + +} diff --git a/AdamantShared/Models/Transaction.swift b/AdamantShared/Models/Transaction.swift index 9243013e4..0cc7fc11c 100644 --- a/AdamantShared/Models/Transaction.swift +++ b/AdamantShared/Models/Transaction.swift @@ -63,7 +63,7 @@ extension Transaction: Codable { self.senderId = try container.decode(String.self, forKey: .senderId) self.recipientId = (try? container.decode(String.self, forKey: .recipientId)) ?? "" self.recipientPublicKey = try? container.decode(String.self, forKey: .recipientPublicKey) - self.signature = try container.decode(String.self, forKey: .signature) + self.signature = (try? container.decode(String.self, forKey: .signature)) ?? "" self.confirmations = (try? container.decode(Int64.self, forKey: .confirmations)) ?? 0 self.requesterPublicKey = try? container.decode(String.self, forKey: .requesterPublicKey) self.signSignature = try? container.decode(String.self, forKey: .signSignature) From 25858e28ca3860f9ad6bd5fe727329bf7d3234e6 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 27 May 2022 18:15:40 +0300 Subject: [PATCH 02/12] Load moore message from scroll & improved UI smoothness --- Adamant.xcodeproj/project.pbxproj | 36 ++++--- .../DataProviders/ChatsProvider.swift | 2 +- .../ApiService/AdamantApi+Chats.swift | 1 + .../DataProviders/AdamantChatsProvider.swift | 32 +++--- .../AdamantTransfersProvider.swift | 2 +- .../Chats/ChatViewController+MessageKit.swift | 16 +++ .../Stories/Chats/ChatViewController.swift | 98 ++++++++++++++++--- .../Stories/Chats/HeaderReusableView.swift | 54 ++++++++++ .../Stories/Shared/WelcomeViewController.xib | 17 ++-- 9 files changed, 200 insertions(+), 58 deletions(-) create mode 100644 Adamant/Stories/Chats/HeaderReusableView.swift diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 3833a9293..55328c420 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 3A8875EF27BBF38D00436195 /* Parchment in Frameworks */ = {isa = PBXBuildFile; productRef = 3A8875EE27BBF38D00436195 /* Parchment */; }; 3AA2D5F7280EADE3000ED971 /* SocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F6280EADE3000ED971 /* SocketService.swift */; }; 3AA2D5FA280EAF5D000ED971 /* AdamantSocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */; }; + 3AD95B96284121E100767396 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD95B95284121E100767396 /* HeaderReusableView.swift */; }; 3AE89DD72837F42D0051D3A9 /* UITableView+adamant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */; }; 3AE89DD92837F5BF0051D3A9 /* ChatRooms.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */; }; 3AE89DDB2837F5D30051D3A9 /* ChatRoomsChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */; }; @@ -615,6 +616,7 @@ 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantSecret.swift; sourceTree = ""; }; 3AA2D5F6280EADE3000ED971 /* SocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketService.swift; sourceTree = ""; }; 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantSocketService.swift; sourceTree = ""; }; + 3AD95B95284121E100767396 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+adamant.swift"; sourceTree = ""; }; 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRooms.swift; sourceTree = ""; }; 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomsChats.swift; sourceTree = ""; }; @@ -1629,6 +1631,7 @@ E95F85C5200A9B070070534A /* ChatTableViewCell.swift */, E95F85C6200A9B070070534A /* ChatTableViewCell.xib */, E974D167215D033C003AD7E8 /* ChatCell.swift */, + 3AD95B95284121E100767396 /* HeaderReusableView.swift */, E9240BF8215D813A00187B09 /* CustomCellDeleage.swift */, 649D6BF121C27D5C009E727B /* SearchResultsViewController.swift */, 6406D74821C7F06000196713 /* SearchResultsViewController.xib */, @@ -2615,6 +2618,7 @@ E940086B2114A70600CD2D67 /* LskAccount.swift in Sources */, 6416B19D21AD7B92006089AC /* LskWalletRoutes.swift in Sources */, E9B3D3A1201FA26B0019EB36 /* AdamantAccountsProvider.swift in Sources */, + 3AD95B96284121E100767396 /* HeaderReusableView.swift in Sources */, E9FAE5DA203DBFEF008D3A6B /* Comparable+clamped.swift in Sources */, E94008802114EE2000CD2D67 /* AdmWallet.swift in Sources */, E9A03FDA20DC0B14007653A1 /* NodesSource.swift in Sources */, @@ -2913,7 +2917,7 @@ CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -2922,7 +2926,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev.MessageNotificationContentExtension"; @@ -2942,7 +2946,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -2951,7 +2955,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger.MessageNotificationContentExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3089,7 +3093,7 @@ CODE_SIGN_ENTITLEMENTS = Adamant/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3099,7 +3103,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "e4233bbf-3705-44fe-95b0-e77475672c60"; @@ -3118,7 +3122,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3128,7 +3132,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "bedd1b75-2f23-4a85-a0b2-14c424fcff42"; @@ -3146,7 +3150,7 @@ CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3155,7 +3159,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev.TransferNotificationContentExtension"; @@ -3175,7 +3179,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3184,7 +3188,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger.TransferNotificationContentExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3204,7 +3208,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3213,7 +3217,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev.NotificationServiceExtension"; @@ -3233,7 +3237,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 180; + CURRENT_PROJECT_VERSION = 183; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3242,7 +3246,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.4.0; + MARKETING_VERSION = 2.5.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger.NotificationServiceExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index 40916264d..7206cbf63 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -181,7 +181,7 @@ protocol ChatsProvider: DataProvider { func getChatroomsController() -> NSFetchedResultsController func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController func getChatRooms(offset: Int?, completion: (() ->())?) - func getChatMessages(with addressRecipient: String, offset: Int?, completion: (() ->())?) + func getChatMessages(with addressRecipient: String, offset: Int?, completion: ((Int) ->())?) /// Unread messages controller. Sections by chatroom. func getUnreadMessagesController() -> NSFetchedResultsController diff --git a/Adamant/Services/ApiService/AdamantApi+Chats.swift b/Adamant/Services/ApiService/AdamantApi+Chats.swift index fa90ee273..8dadfcb4b 100644 --- a/Adamant/Services/ApiService/AdamantApi+Chats.swift +++ b/Adamant/Services/ApiService/AdamantApi+Chats.swift @@ -167,6 +167,7 @@ extension AdamantApiService { // MARK: 1. Prepare params var queryItems: [URLQueryItem] = [] if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } + queryItems.append(URLQueryItem(name: "limit", value: "50")) // MARK: 2. Build endpoint let endpoint: URL diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 90d47125f..b01b00bcf 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -221,7 +221,6 @@ extension AdamantChatsProvider { } else { self?.roomsLoadedCount = trans.chats?.count ?? 0 } - var array = Array() trans.chats?.forEach({ room in if let last = room.lastTransaction { @@ -234,22 +233,21 @@ extension AdamantChatsProvider { senderId: address, privateKey: privateKey, context: privateContext, - contextMutatingSemaphore: cms) - } - - if let synced = self?.isInitiallySynced, !synced { - self?.processingQueue.asyncAfter(deadline: .now() + 1) { - self?.isInitiallySynced = true - } + contextMutatingSemaphore: cms, + completion: { + if let synced = self?.isInitiallySynced, !synced { + self?.isInitiallySynced = true + } + completion?() + }) } - completion?() case .failure(_): completion?() } } } - func getChatMessages(with addressRecipient: String, offset: Int?, completion: (() ->())?) { + func getChatMessages(with addressRecipient: String, offset: Int?, completion: ((Int) ->())?) { guard let address = accountService.account?.address, let privateKey = accountService.keypair?.privateKey else { return @@ -269,12 +267,14 @@ extension AdamantChatsProvider { senderId: address, privateKey: privateKey, context: privateContext, - contextMutatingSemaphore: cms) + contextMutatingSemaphore: cms, + completion: { + completion?(transactions.count) + }) } } - completion?() case .failure(_): - completion?() + completion?(0) } } } @@ -872,8 +872,6 @@ extension AdamantChatsProvider { return controller } -} - // MARK: - Processing extension AdamantChatsProvider { @@ -951,7 +949,8 @@ extension AdamantChatsProvider { senderId: String, privateKey: String, context: NSManagedObjectContext, - contextMutatingSemaphore: DispatchSemaphore) { + contextMutatingSemaphore: DispatchSemaphore, + completion: (() -> ())? = nil) { struct DirectionedTransaction { let transaction: Transaction let isOut: Bool @@ -1177,6 +1176,7 @@ extension AdamantChatsProvider { receivedLastHeight = height } highSemaphore.signal() + completion?() } } diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index e454b3136..d2b2db01e 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -110,7 +110,7 @@ class AdamantTransfersProvider: TransfersProvider { extension AdamantTransfersProvider { func reload() { reset(notify: false) - + update() } diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index 78e6ebc20..91fdf90b3 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -288,6 +288,14 @@ extension ChatViewController: MessagesDisplayDelegate { let timeIntervalSinceLastMessage = message.sentDate.timeIntervalSince(previousMessage.sentDate) return timeIntervalSinceLastMessage >= self.showsDateHeaderAfterTimeInterval } + + func messageHeaderView(for indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageReusableView { + let header = messagesCollectionView.dequeueReusableHeaderView(HeaderReusableView.self, for: indexPath) + if (indexPath.section == 0 && isBusy) { + header.setupLoadAnimating() + } + return header + } } extension ChatViewController: MessageCellDelegate { @@ -550,6 +558,14 @@ extension ChatViewController: MessagesLayoutDelegate { return (messagesCollectionView.collectionViewLayout as! MessagesCollectionViewFlowLayout).textMessageSizeCalculator } } + + func headerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize { + if (section == 0) { + return CGSize(width: messagesCollectionView.bounds.width, height: HeaderReusableView.height) + } else { + return .zero + } + } } diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index dcb6f4d9c..0c2f51709 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -200,12 +200,23 @@ class ChatViewController: MessagesViewController { private var busyBackgroundView: UIView? private var spinner = UIActivityIndicatorView(style: .whiteLarge) + private var loadedMessages = 0 + var isBusy = true + + //MARK: Background UI + private let amadantLogoImageView: UIImageView = { + let iv = UIImageView() + iv.image = UIImage(named: "Adamant-logo") + return iv + }() // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = UIBarButtonItem(title: "•••", style: .plain, target: self, action: #selector(properties)) + setBackgroundUI() + guard let chatroom = chatroom else { return } @@ -224,6 +235,8 @@ class ChatViewController: MessagesViewController { messagesCollectionView.messageCellDelegate = self maintainPositionOnKeyboardFrameChanged = true + messagesCollectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader) + if let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout { for messageSizeCalculator in layout.messageSizeCalculators() { messageSizeCalculator.outgoingAvatarSize = .zero @@ -429,11 +442,20 @@ class ChatViewController: MessagesViewController { UIMenuItem(title: String.adamantLocalized.chat.report, action: NSSelectorFromString("report:"))] setBusyIndicator(state: true) + messagesCollectionView.alpha = 0.0 + messageInputBar.isUserInteractionEnabled = false if let address = chatroom.partner?.address { - chatsProvider.getChatMessages(with: address, offset: 0) { - print("loaded") + chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] _ in DispatchQueue.main.async { - self.setBusyIndicator(state: false) + self?.messagesCollectionView.reloadData() + self?.messagesCollectionView.scrollToBottom(animated: false) + self?.setBusyIndicator(state: false) + self?.messagesCollectionView.reloadSections(IndexSet(integer: 0)) + UIView.animate(withDuration: 0.25) { + self?.messagesCollectionView.alpha = 1.0 + self?.amadantLogoImageView.alpha = 0.0 + } + self?.messageInputBar.isUserInteractionEnabled = true } } } @@ -568,16 +590,17 @@ class ChatViewController: MessagesViewController { } if self.messageToShow == nil { - if let offset = self.chatsProvider.chatPositon[address] { - self.chatPositionOffset = CGFloat(offset) - self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata - let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) - - messagesCollectionView.performBatchUpdates(nil) { _ in self.messagesCollectionView.scrollRectToVisible(CGRect(x: 0.0, y: collectionViewContentHeight - 1.0, width: 1.0, height: 1.0), animated: false) - } - } else { - messagesCollectionView.scrollToBottom(animated: false) - } +// if let offset = self.chatsProvider.chatPositon[address] { +// self.chatPositionOffset = CGFloat(offset) +// self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata +// let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) +// +// messagesCollectionView.performBatchUpdates(nil) { _ in self.messagesCollectionView.scrollRectToVisible(CGRect(x: 0.0, y: collectionViewContentHeight - 1.0, width: 1.0, height: 1.0), animated: false) +// } +// } else { +// messagesCollectionView.scrollToBottom(animated: false) +// } + messagesCollectionView.scrollToBottom(animated: false) } else { self.chatsProvider.chatPositon.removeValue(forKey: address) } @@ -806,7 +829,9 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { } func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - performBatchChanges(controllerChanges) + if !isBusy { + performBatchChanges(controllerChanges) + } } func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { @@ -846,6 +871,7 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { chat.performBatchUpdates({ for change in changes { + print(change.type) switch change.type { case .insert: guard let newIndexPath = change.newIndexPath else { @@ -1043,9 +1069,10 @@ private class StatusUpdateProcedure: Procedure { // MARK: - Busy Indicator View extension ChatViewController { func setBusyIndicator(state: Bool) { + isBusy = state if busyBackgroundView == nil { busyBackgroundView = UIView() - busyBackgroundView?.backgroundColor = UIColor(white: 0, alpha: 0.2) + busyBackgroundView?.backgroundColor = UIColor(white: 0, alpha: 0.1) busyBackgroundView?.frame = view.frame view.addSubview(busyBackgroundView!) @@ -1057,7 +1084,46 @@ extension ChatViewController { } if !state { - busyBackgroundView?.removeFromSuperview() + UIView.animate(withDuration: 0.25) { [weak self] in + self?.busyBackgroundView?.backgroundColor = .clear + } completion: { [weak self] _ in + self?.busyBackgroundView?.removeFromSuperview() + } } } } + +//MARK: Load moore message +extension ChatViewController { + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + try? chatController?.performFetch() + if indexPath.section < 2, + let count = chatController?.fetchedObjects?.count, + let address = chatroom?.partner?.address, + !isBusy, + loadedMessages != count { + isBusy = true + loadedMessages = count + chatsProvider.getChatMessages(with: address, offset: count) { [weak self] _count in + DispatchQueue.main.async { + self?.messagesCollectionView.reloadDataAndKeepOffset() + self?.isBusy = false + self?.messagesCollectionView.reloadSections(IndexSet(integer: 0)) + } + } + } + } +} + +// MARK: - Background UI +extension ChatViewController { + func setBackgroundUI() { + amadantLogoImageView.translatesAutoresizingMaskIntoConstraints = false + // view.insertSubview(amadantLogoImageView, at: 0) + view.addSubview(amadantLogoImageView) + amadantLogoImageView.heightAnchor.constraint(equalToConstant: 100).isActive = true + amadantLogoImageView.widthAnchor.constraint(equalToConstant: 100).isActive = true + amadantLogoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + amadantLogoImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + } +} diff --git a/Adamant/Stories/Chats/HeaderReusableView.swift b/Adamant/Stories/Chats/HeaderReusableView.swift new file mode 100644 index 000000000..d655931ad --- /dev/null +++ b/Adamant/Stories/Chats/HeaderReusableView.swift @@ -0,0 +1,54 @@ +// +// HeaderReusableView.swift +// Adamant +// +// Created by Stanislav Jelezoglo on 27.05.2022. +// Copyright © 2022 Adamant. All rights reserved. +// + +import Foundation + +class HeaderReusableView: MessageReusableView { + // MARK: - Private Properties + static private let insets = UIEdgeInsets(top: 12, left: 80, bottom: 12, right: 80) + + private var spinner = UIActivityIndicatorView(style: .gray) + + // MARK: - Public Methods + static var height: CGFloat { + return insets.top + insets.bottom + 27 + } + + override init(frame: CGRect) { + super.init(frame: frame) + createUI() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + createUI() + } + + /// Setup the receiver with text. + /// + /// - Parameter text: The text to be displayed. + func setupLoadAnimating() { + spinner.startAnimating() + } + + func stopLoadAnimating() { + spinner.stopAnimating() + } + + override func prepareForReuse() { + spinner.stopAnimating() + } + + // MARK: - Private Methods + private func createUI() { + spinner.translatesAutoresizingMaskIntoConstraints = false + addSubview(spinner) + spinner.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + spinner.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true + } +} diff --git a/Adamant/Stories/Shared/WelcomeViewController.xib b/Adamant/Stories/Shared/WelcomeViewController.xib index 4d0f64e5f..a9d154b98 100644 --- a/Adamant/Stories/Shared/WelcomeViewController.xib +++ b/Adamant/Stories/Shared/WelcomeViewController.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -22,7 +20,7 @@ - + @@ -31,7 +29,8 @@ - + + @@ -41,11 +40,13 @@ - + + + From 7b6fb263c20768c460d84f9d86163a27218f35df Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 3 Jun 2022 17:23:51 +0300 Subject: [PATCH 03/12] fixed reopen chat download, scroll position and some others bugs --- Adamant.xcodeproj/project.pbxproj | 36 ++++----- .../DataProviders/ChatsProvider.swift | 2 + .../DataProviders/AdamantChatsProvider.swift | 14 +++- .../AdamantTransfersProvider.swift | 1 + .../Stories/Chats/ChatViewController.swift | 76 ++++++++++++------- .../Chats/ComplexTransferViewController.swift | 4 +- .../Stories/Chats/HeaderReusableView.swift | 1 + ...TransactionDetailsViewControllerBase.swift | 6 +- Podfile.lock | 2 +- 9 files changed, 87 insertions(+), 55 deletions(-) diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 55328c420..4b6cc6d29 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -7,10 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 3A64FAA627BA67BF007D5588 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */; }; - 3A64FAA727BA67BF007D5588 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */; }; - 3A64FAA827BA67BF007D5588 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */; }; - 3A64FAA927BA67BF007D5588 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */; }; 3A8875EF27BBF38D00436195 /* Parchment in Frameworks */ = {isa = PBXBuildFile; productRef = 3A8875EE27BBF38D00436195 /* Parchment */; }; 3AA2D5F7280EADE3000ED971 /* SocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F6280EADE3000ED971 /* SocketService.swift */; }; 3AA2D5FA280EAF5D000ED971 /* AdamantSocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */; }; @@ -19,6 +15,10 @@ 3AE89DD92837F5BF0051D3A9 /* ChatRooms.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */; }; 3AE89DDB2837F5D30051D3A9 /* ChatRoomsChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */; }; 3C06931576393125C61FB8F6 /* Pods_Adamant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33975C0D891698AA7E74EBCC /* Pods_Adamant.framework */; }; + 41E857A42847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; + 41E857A52847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; + 41E857A62847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; + 41E857A72847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; 553B1284281C6EE100FFF24C /* GCDUtilites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553B1283281C6EE100FFF24C /* GCDUtilites.swift */; }; 6403F5DB2272389800D58779 /* (null) in Sources */ = {isa = PBXBuildFile; }; 6403F5DE22723C6800D58779 /* DashMainnet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6403F5DD22723C6800D58779 /* DashMainnet.swift */; }; @@ -613,13 +613,13 @@ /* Begin PBXFileReference section */ 33975C0D891698AA7E74EBCC /* Pods_Adamant.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Adamant.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36AB8CE9537B3B873972548B /* Pods_AdmCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AdmCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantSecret.swift; sourceTree = ""; }; 3AA2D5F6280EADE3000ED971 /* SocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketService.swift; sourceTree = ""; }; 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantSocketService.swift; sourceTree = ""; }; 3AD95B95284121E100767396 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+adamant.swift"; sourceTree = ""; }; 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRooms.swift; sourceTree = ""; }; 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomsChats.swift; sourceTree = ""; }; + 41E857A32847ADA200BC0783 /* AdamantSecret.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantSecret.swift; sourceTree = ""; }; 4A4D67BD3DC89C07D1351248 /* Pods-AdmCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdmCore.release.xcconfig"; path = "Target Support Files/Pods-AdmCore/Pods-AdmCore.release.xcconfig"; sourceTree = ""; }; 553B1283281C6EE100FFF24C /* GCDUtilites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCDUtilites.swift; sourceTree = ""; }; 6403F5DD22723C6800D58779 /* DashMainnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashMainnet.swift; sourceTree = ""; }; @@ -1725,7 +1725,7 @@ E9D664C222A0037600733F8A /* View */, E957E123229B0C330019732A /* AdamantNotificationKeys.swift */, E9771D802299602D0099AAC7 /* AdamantResources.swift */, - 3A64FAA527BA67BF007D5588 /* AdamantSecret.swift */, + 41E857A32847ADA200BC0783 /* AdamantSecret.swift */, E957E170229C56EE0019732A /* Shared.xcassets */, ); path = AdamantShared; @@ -2371,7 +2371,7 @@ E9079A92229DF1B80022CA0D /* UIColor+hex.swift in Sources */, E9079A8D229DF19A0022CA0D /* KeychainStore.swift in Sources */, E9079AA4229DF6850022CA0D /* AdamantUtilities.swift in Sources */, - 3A64FAA927BA67BF007D5588 /* AdamantSecret.swift in Sources */, + 41E857A72847ADA200BC0783 /* AdamantSecret.swift in Sources */, E951466C22A691EF007564D7 /* String+utilites.swift in Sources */, E9079A8F229DF1A40022CA0D /* Crypto.swift in Sources */, E9079A90229DF1A40022CA0D /* Mnemonic.swift in Sources */, @@ -2411,6 +2411,7 @@ E9256F5F2034C21100DE86E9 /* String+localized.swift in Sources */, 6414C18E217DF43100373FA6 /* String+adamant.swift in Sources */, E908472A2196FEA80095825D /* RichMessageTransaction+CoreDataClass.swift in Sources */, + 41E857A42847ADA200BC0783 /* AdamantSecret.swift in Sources */, 64A223D620F760BB005157CB /* Localization.swift in Sources */, 64E1C82F222E95F6006C4DA7 /* DogeWallet.swift in Sources */, E9484B7D2285BAD9008E10F0 /* PrivateKeyGenerator.swift in Sources */, @@ -2521,7 +2522,6 @@ E9A03FD220DBC0F2007653A1 /* NodeEditorViewController.swift in Sources */, E96D64B82295BED700CA5587 /* ChatType.swift in Sources */, E9393FAA2055D03300EE6F30 /* AdamantMessage.swift in Sources */, - 3A64FAA627BA67BF007D5588 /* AdamantSecret.swift in Sources */, E96D64D62295C9CB00CA5587 /* NativeAdamantCore.swift in Sources */, E90A494D204DA932009F6A65 /* LocalAuthentication.swift in Sources */, E96D64C62295C3ED00CA5587 /* Mnemonic+extended.swift in Sources */, @@ -2754,7 +2754,7 @@ 64AE84622300012400F38FBD /* DashProvider.swift in Sources */, E957E16C229C53980019732A /* LskProvider.swift in Sources */, E9D664D022A009AB00733F8A /* AdamantLocalized.swift in Sources */, - 3A64FAA827BA67BF007D5588 /* AdamantSecret.swift in Sources */, + 41E857A62847ADA200BC0783 /* AdamantSecret.swift in Sources */, E957E14D229C3E530019732A /* Node.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2797,7 +2797,7 @@ E957E0ED229982FF0019732A /* Keypair.swift in Sources */, 6423038324B26CAF004CF3FE /* ERC20Token.swift in Sources */, E957E0FB229AB9310019732A /* AdamantProvider.swift in Sources */, - 3A64FAA727BA67BF007D5588 /* AdamantSecret.swift in Sources */, + 41E857A52847ADA200BC0783 /* AdamantSecret.swift in Sources */, E957E157229C3F840019732A /* ExtensionsApi.swift in Sources */, E957E0F722999BD50019732A /* TransferBaseProvider.swift in Sources */, E9771D9622996FEB0099AAC7 /* StateType.swift in Sources */, @@ -2917,7 +2917,7 @@ CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -2946,7 +2946,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3093,7 +3093,7 @@ CODE_SIGN_ENTITLEMENTS = Adamant/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3122,7 +3122,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3150,7 +3150,7 @@ CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3179,7 +3179,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3208,7 +3208,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3237,7 +3237,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 183; + CURRENT_PROJECT_VERSION = 185; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index 7206cbf63..12cf342f2 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -177,6 +177,8 @@ protocol ChatsProvider: DataProvider { var roomsMaxCount: Int? { get } var roomsLoadedCount: Int? { get } + var isChatLoaded: [String: Bool] { get } + // MARK: - Getting chats and messages func getChatroomsController() -> NSFetchedResultsController func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index b01b00bcf..af92198a0 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -38,6 +38,8 @@ class AdamantChatsProvider: ChatsProvider { private(set) var blackList: [String] = [] private(set) var removedMessages: [String] = [] + public var isChatLoaded: [String : Bool] = [:] + private(set) var isInitiallySynced: Bool = false { didSet { NotificationCenter.default.post(name: Notification.Name.AdamantChatsProvider.initiallySyncedChanged, object: self, userInfo: [AdamantUserInfoKey.ChatProvider.initiallySynced : isInitiallySynced]) @@ -205,8 +207,9 @@ extension AdamantChatsProvider { func getChatRooms(offset: Int?, completion: (() ->())?) { guard let address = accountService.account?.address, let privateKey = accountService.keypair?.privateKey else { - return - } + completion?() + return + } let cms = DispatchSemaphore(value: 1) // MARK: 3. Get transactions @@ -250,8 +253,9 @@ extension AdamantChatsProvider { func getChatMessages(with addressRecipient: String, offset: Int?, completion: ((Int) ->())?) { guard let address = accountService.account?.address, let privateKey = accountService.keypair?.privateKey else { - return - } + completion?(0) + return + } let cms = DispatchSemaphore(value: 1) // MARK: 3. Get transactions @@ -262,6 +266,7 @@ extension AdamantChatsProvider { switch result { case .success(let transactions): if let transactions = transactions { + self?.isChatLoaded[addressRecipient] = true self?.processingQueue.async { self?.process(messageTransactions: transactions, senderId: address, @@ -872,6 +877,7 @@ extension AdamantChatsProvider { return controller } +} // MARK: - Processing extension AdamantChatsProvider { diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index d2b2db01e..c89002842 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -660,6 +660,7 @@ extension AdamantTransfersProvider { } trsfr.confirmations = transaction.confirmations + trsfr.blockId = transaction.blockId do { try context.save() diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 0c2f51709..43ef99e6d 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -441,21 +441,29 @@ class ChatViewController: MessagesViewController { UIMenuItem(title: String.adamantLocalized.chat.remove, action: NSSelectorFromString("remove:")), UIMenuItem(title: String.adamantLocalized.chat.report, action: NSSelectorFromString("report:"))] - setBusyIndicator(state: true) - messagesCollectionView.alpha = 0.0 - messageInputBar.isUserInteractionEnabled = false if let address = chatroom.partner?.address { + if let isLoaded = chatsProvider.isChatLoaded[address], + isLoaded { + setBusyIndicator(state: false) + return + } + if address == AdamantContacts.adamantBountyWallet.name { + setBusyIndicator(state: false) + return + } + setBusyIndicator(state: true) + chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] _ in DispatchQueue.main.async { - self?.messagesCollectionView.reloadData() - self?.messagesCollectionView.scrollToBottom(animated: false) - self?.setBusyIndicator(state: false) - self?.messagesCollectionView.reloadSections(IndexSet(integer: 0)) - UIView.animate(withDuration: 0.25) { - self?.messagesCollectionView.alpha = 1.0 - self?.amadantLogoImageView.alpha = 0.0 - } - self?.messageInputBar.isUserInteractionEnabled = true + self?.messagesCollectionView.reloadDataAndKeepOffset() + self?.messagesCollectionView.performBatchUpdates(nil, completion: { + (result) in + DispatchQueue.main.async { + print("scroll to scrollToBottom ViewdidLoad") + self?.setBusyIndicator(state: false) + self?.reloadTopScetionIfNeeded() + } + }) } } } @@ -544,7 +552,7 @@ class ChatViewController: MessagesViewController { scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata scrollToBottomBtnOffetConstraint?.constant = -20 - self.messageInputBar.bounds.height - if forceScrollToBottom ?? false { + if forceScrollToBottom ?? false && !scrollToBottomBtn.isHidden { scrollDown() } } @@ -590,17 +598,16 @@ class ChatViewController: MessagesViewController { } if self.messageToShow == nil { -// if let offset = self.chatsProvider.chatPositon[address] { -// self.chatPositionOffset = CGFloat(offset) -// self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata -// let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) -// -// messagesCollectionView.performBatchUpdates(nil) { _ in self.messagesCollectionView.scrollRectToVisible(CGRect(x: 0.0, y: collectionViewContentHeight - 1.0, width: 1.0, height: 1.0), animated: false) -// } -// } else { -// messagesCollectionView.scrollToBottom(animated: false) -// } - messagesCollectionView.scrollToBottom(animated: false) + if let offset = self.chatsProvider.chatPositon[address] { + self.chatPositionOffset = CGFloat(offset) + self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata + let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) + + messagesCollectionView.performBatchUpdates(nil) { _ in self.messagesCollectionView.scrollRectToVisible(CGRect(x: 0.0, y: collectionViewContentHeight - 1.0, width: 1.0, height: 1.0), animated: false) + } + } else { + messagesCollectionView.scrollToBottom(animated: false) + } } else { self.chatsProvider.chatPositon.removeValue(forKey: address) } @@ -871,7 +878,6 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { chat.performBatchUpdates({ for change in changes { - print(change.type) switch change.type { case .insert: guard let newIndexPath = change.newIndexPath else { @@ -987,6 +993,8 @@ extension ChatViewController { var offset = scrollView.contentSize.height - scrollView.bounds.height - scrollView.contentOffset.y + messageInputBar.bounds.height offset += self.keyboardHeight + if self.messageToShow != nil && offset < 0 { offset *= -1 } + if offset > chatPositionDelata { chatPositionOffset = offset } else { @@ -1081,11 +1089,17 @@ extension ChatViewController { busyBackgroundView?.addSubview(spinner) spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + + messagesCollectionView.alpha = 0.0 + messageInputBar.isUserInteractionEnabled = false } if !state { + messageInputBar.isUserInteractionEnabled = true UIView.animate(withDuration: 0.25) { [weak self] in self?.busyBackgroundView?.backgroundColor = .clear + self?.messagesCollectionView.alpha = 1.0 + self?.amadantLogoImageView.alpha = 0.0 } completion: { [weak self] _ in self?.busyBackgroundView?.removeFromSuperview() } @@ -1102,24 +1116,32 @@ extension ChatViewController { let address = chatroom?.partner?.address, !isBusy, loadedMessages != count { + if address == AdamantContacts.adamantBountyWallet.name { return } isBusy = true loadedMessages = count chatsProvider.getChatMessages(with: address, offset: count) { [weak self] _count in DispatchQueue.main.async { self?.messagesCollectionView.reloadDataAndKeepOffset() self?.isBusy = false - self?.messagesCollectionView.reloadSections(IndexSet(integer: 0)) + self?.reloadTopScetionIfNeeded() } } } } + + func reloadTopScetionIfNeeded() { + try? chatController?.performFetch() + if let count = chatController?.fetchedObjects?.count, + count >= 1 { + self.messagesCollectionView.reloadSections(IndexSet(integer: 0)) + } + } } // MARK: - Background UI extension ChatViewController { func setBackgroundUI() { amadantLogoImageView.translatesAutoresizingMaskIntoConstraints = false - // view.insertSubview(amadantLogoImageView, at: 0) view.addSubview(amadantLogoImageView) amadantLogoImageView.heightAnchor.constraint(equalToConstant: 100).isActive = true amadantLogoImageView.widthAnchor.constraint(equalToConstant: 100).isActive = true diff --git a/Adamant/Stories/Chats/ComplexTransferViewController.swift b/Adamant/Stories/Chats/ComplexTransferViewController.swift index c29f73812..e3226886b 100644 --- a/Adamant/Stories/Chats/ComplexTransferViewController.swift +++ b/Adamant/Stories/Chats/ComplexTransferViewController.swift @@ -37,7 +37,7 @@ class ComplexTransferViewController: UIViewController { view.backgroundColor = UIColor.white if let partner = partner { - navigationItem.title = partner.name ?? partner.address + navigationItem.title = partner.name?.checkAndReplaceSystemWallets() ?? partner.address } navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel)) @@ -88,7 +88,7 @@ extension ComplexTransferViewController: PagingViewControllerDataSource { let vc = service.transferViewController() if let v = vc as? TransferViewControllerBase { if let address = partner?.address { - let name = partner?.name + let name = partner?.name?.checkAndReplaceSystemWallets() v.admReportRecipient = address v.recipientIsReadonly = true v.commentsEnabled = service.commentsEnabledForRichMessages diff --git a/Adamant/Stories/Chats/HeaderReusableView.swift b/Adamant/Stories/Chats/HeaderReusableView.swift index d655931ad..886a4ca1e 100644 --- a/Adamant/Stories/Chats/HeaderReusableView.swift +++ b/Adamant/Stories/Chats/HeaderReusableView.swift @@ -7,6 +7,7 @@ // import Foundation +import MessageKit class HeaderReusableView: MessageReusableView { // MARK: - Private Properties diff --git a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift index 96aa3709f..ffb204f20 100644 --- a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift +++ b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift @@ -204,7 +204,7 @@ class TransactionDetailsViewControllerBase: FormViewController { if let transaction = transaction { if transaction.senderAddress.count == 0 { $0.value = DoubleDetail(first: TransactionDetailsViewControllerBase.awaitingValueString, second: nil) - } else if let senderName = self?.senderName { + } else if let senderName = self?.senderName?.checkAndReplaceSystemWallets() { $0.value = DoubleDetail(first: senderName, second: transaction.senderAddress) } else { $0.value = DoubleDetail(first: transaction.senderAddress, second: nil) @@ -236,7 +236,7 @@ class TransactionDetailsViewControllerBase: FormViewController { if let transaction = self?.transaction { if transaction.senderAddress.count == 0 { row.value = DoubleDetail(first: TransactionDetailsViewControllerBase.awaitingValueString, second: nil) - } else if let senderName = self?.senderName { + } else if let senderName = self?.senderName?.checkAndReplaceSystemWallets() { row.value = DoubleDetail(first: senderName, second: transaction.senderAddress) } else { row.value = DoubleDetail(first: transaction.senderAddress, second: nil) @@ -255,7 +255,7 @@ class TransactionDetailsViewControllerBase: FormViewController { $0.cell.titleLabel.text = Rows.to.localized if let transaction = transaction { - if let recipientName = self?.recipientName { + if let recipientName = self?.recipientName?.checkAndReplaceSystemWallets() { $0.value = DoubleDetail(first: recipientName, second: transaction.recipientAddress) } else { $0.value = DoubleDetail(first: transaction.recipientAddress, second: nil) diff --git a/Podfile.lock b/Podfile.lock index 0c3b61078..da3ae8778 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -47,4 +47,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 1d081ac466709ab7ca1f43d0d3bf418f86bd7d6d -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 From 24360caf39f66bfbd1e84c39124d839d5be36761 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 10 Jun 2022 17:49:28 +0300 Subject: [PATCH 04/12] Improved charooms work & fix design bugs --- Adamant.xcodeproj/project.pbxproj | 16 +-- Adamant/ServiceProtocols/ApiService.swift | 2 +- .../DataProviders/AccountsProvider.swift | 5 + .../DataProviders/ChatsProvider.swift | 2 + .../ApiService/AdamantApi+Chats.swift | 99 +++++++-------- .../AdamantAccountsProvider.swift | 115 +++++++++++++++++- .../DataProviders/AdamantChatsProvider.swift | 33 +++-- .../Chats/ChatListViewController.swift | 87 +++++++++++-- .../Chats/ChatViewController+MessageKit.swift | 4 +- .../Stories/Chats/ChatViewController.swift | 73 +++++++---- .../Stories/Chats/HeaderReusableView.swift | 52 +++++++- 11 files changed, 382 insertions(+), 106 deletions(-) diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 4b6cc6d29..b3864b580 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -2917,7 +2917,7 @@ CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -2946,7 +2946,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3093,7 +3093,7 @@ CODE_SIGN_ENTITLEMENTS = Adamant/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3122,7 +3122,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3150,7 +3150,7 @@ CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3179,7 +3179,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3208,7 +3208,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3237,7 +3237,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 185; + CURRENT_PROJECT_VERSION = 187; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; diff --git a/Adamant/ServiceProtocols/ApiService.swift b/Adamant/ServiceProtocols/ApiService.swift index 37c477114..222887ec2 100644 --- a/Adamant/ServiceProtocols/ApiService.swift +++ b/Adamant/ServiceProtocols/ApiService.swift @@ -150,7 +150,7 @@ protocol ApiService: AnyObject { // MARK: - Chats Rooms func getChatRooms(address: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) - func getChatMessages(address: String, addressRecipient: String, offset: Int?, completion: @escaping (ApiServiceResult<[Transaction]?>) -> Void) + func getChatMessages(address: String, addressRecipient: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) // MARK: - Funds diff --git a/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift b/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift index c9cd3fdd3..b7c31bc7b 100644 --- a/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift @@ -55,6 +55,11 @@ protocol AccountsProvider { /// - Returns: Account, if found, created in main viewContext func getAccount(byAddress address: String, completion: @escaping (AccountsProviderResult) -> Void) + /// Search for fetched account, if not found try to create or asks server for account. + /// + /// - Returns: Account, if found, created in main viewContext + func getAccount(byAddress address: String, publicKey: String, completion: @escaping (AccountsProviderResult) -> Void) + /* That one bugged. Will be fixed later. Maybe. */ /// Search for fetched account, if not found, asks server for account. /// diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index 12cf342f2..f6d1b57d6 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -178,6 +178,8 @@ protocol ChatsProvider: DataProvider { var roomsLoadedCount: Int? { get } var isChatLoaded: [String: Bool] { get } + var chatMaxMessages: [String: Int] { get } + var chatLoadedMessages: [String: Int] { get } // MARK: - Getting chats and messages func getChatroomsController() -> NSFetchedResultsController diff --git a/Adamant/Services/ApiService/AdamantApi+Chats.swift b/Adamant/Services/ApiService/AdamantApi+Chats.swift index 8dadfcb4b..d6fb98553 100644 --- a/Adamant/Services/ApiService/AdamantApi+Chats.swift +++ b/Adamant/Services/ApiService/AdamantApi+Chats.swift @@ -136,58 +136,59 @@ extension AdamantApiService { } // new api - func getChatRooms(address: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) { - // MARK: 1. Prepare params - var queryItems: [URLQueryItem] = [] - if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } - - // MARK: 2. Build endpoint - let endpoint: URL - do { - endpoint = try buildUrl(path: ApiCommands.Chats.getChatRooms + "/\(address)", queryItems: queryItems) - } catch { - let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) - completion(.failure(err)) - return - } - - // MARK: 3. Send - sendRequest(url: endpoint) { (serverResponse: ApiServiceResult) in - switch serverResponse { - case .success(let response): - completion(.success(response)) - - case .failure(let error): - completion(.failure(.networkError(error: error))) - } - } + func getChatRooms(address: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) { + // MARK: 1. Prepare params + var queryItems: [URLQueryItem] = [] + if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } + queryItems.append(URLQueryItem(name: "limit", value: "20")) + + // MARK: 2. Build endpoint + let endpoint: URL + do { + endpoint = try buildUrl(path: ApiCommands.Chats.getChatRooms + "/\(address)", queryItems: queryItems) + } catch { + let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) + completion(.failure(err)) + return } - func getChatMessages(address: String, addressRecipient: String, offset: Int?, completion: @escaping (ApiServiceResult<[Transaction]?>) -> Void) { - // MARK: 1. Prepare params - var queryItems: [URLQueryItem] = [] - if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } - queryItems.append(URLQueryItem(name: "limit", value: "50")) - - // MARK: 2. Build endpoint - let endpoint: URL - do { - endpoint = try buildUrl(path: ApiCommands.Chats.getChatRooms + "/\(address)/\(addressRecipient)", queryItems: queryItems) - } catch { - let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) - completion(.failure(err)) - return + // MARK: 3. Send + sendRequest(url: endpoint) { (serverResponse: ApiServiceResult) in + switch serverResponse { + case .success(let response): + completion(.success(response)) + + case .failure(let error): + completion(.failure(.networkError(error: error))) } - - // MARK: 3. Send - sendRequest(url: endpoint) { (serverResponse: ApiServiceResult) in - switch serverResponse { - case .success(let response): - completion(.success(response.messages)) - - case .failure(let error): - completion(.failure(.networkError(error: error))) - } + } + } + + func getChatMessages(address: String, addressRecipient: String, offset: Int?, completion: @escaping (ApiServiceResult) -> Void) { + // MARK: 1. Prepare params + var queryItems: [URLQueryItem] = [] + if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) } + queryItems.append(URLQueryItem(name: "limit", value: "50")) + + // MARK: 2. Build endpoint + let endpoint: URL + do { + endpoint = try buildUrl(path: ApiCommands.Chats.getChatRooms + "/\(address)/\(addressRecipient)", queryItems: queryItems) + } catch { + let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) + completion(.failure(err)) + return + } + + // MARK: 3. Send + sendRequest(url: endpoint) { (serverResponse: ApiServiceResult) in + switch serverResponse { + case .success(let response): + completion(.success(response)) + + case .failure(let error): + completion(.failure(.networkError(error: error))) } } + } } diff --git a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift index 4e84c0307..8c1e2d908 100644 --- a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift @@ -215,7 +215,7 @@ extension AdamantAccountsProvider { } } - /// Get account info from servier. + /// Get account info from server. /// /// - Parameters: /// - address: address of an account @@ -227,7 +227,7 @@ extension AdamantAccountsProvider { return } - let context = stack.container.viewContext + //let context = stack.container.viewContext // Go background, to not to hang threads (especially main) on semaphores and dispatch groups queue.async { @@ -281,6 +281,7 @@ extension AdamantAccountsProvider { var coreAccount: CoreDataAccount! = nil DispatchQueue.main.sync { + let context = self.stack.container.viewContext coreAccount = self.createCoreDataAccount(from: account, context: context) if let dummy = dummy { @@ -344,7 +345,102 @@ extension AdamantAccountsProvider { } } - + /// Get account info from server or create instantly + /// + /// - Parameters: + /// - address: address of an account + /// - publicKey: publicKey of an account + /// - completion: returns Account created in viewContext + func getAccount(byAddress address: String, publicKey: String, completion: @escaping (AccountsProviderResult) -> Void) { + let validation = AdamantUtilities.validateAdamantAddress(address: address) + if validation == .invalid { + completion(.invalidAddress(address: address)) + return + } + + if publicKey.isEmpty { + getAccount(byAddress: address) { _account in + completion(_account) + } + return + } + let context = stack.container.viewContext + + // Go background, to not to hang threads (especially main) on semaphores and dispatch groups + queue.async { + self.groupsSemaphore.wait() // 1 + + // If there is already request for a this address, wait + if let group = self.requestGroups[address] { + self.groupsSemaphore.signal() // 1 + group.wait() + self.groupsSemaphore.wait() // 2 + } + + // Check if there is an account, that we are looking for + let dummy: DummyAccount? + switch self.getAccount(byPredicate: NSPredicate(format: "address == %@", address)) { + case .core(let account): + self.groupsSemaphore.signal() // 1 or 2 + completion(.success(account)) + return + + case .dummy(let account): + dummy = account + + case .notFound: + dummy = nil + } + + // No, we need to get one + let group = DispatchGroup() + self.requestGroups[address] = group + group.enter() + + self.groupsSemaphore.signal() // 1 or 2 + + switch validation { + case .valid: + var coreAccount: CoreDataAccount! = nil + DispatchQueue.main.sync { + coreAccount = self.createCoreDataAccount(with: address, publicKey: publicKey, contextt: context) + if let dummy = dummy { + coreAccount.name = dummy.name + + if let transfers = dummy.transfers { + dummy.removeFromTransfers(transfers) + coreAccount.addToTransfers(transfers) + + if let chatroom = coreAccount.chatroom { + chatroom.addToTransactions(transfers) + chatroom.updateLastTransaction() + } + } + context.delete(dummy) + } + + try? context.save() + } + self.removeSafeFromRequests(address) + group.leave() + + completion(.success(coreAccount)) + + case .system: + let coreAccount = self.createCoreDataAccount(with: address, publicKey: "") + self.removeSafeFromRequests(address) + group.leave() + + completion(.success(coreAccount)) + + case .invalid: + self.removeSafeFromRequests(address) + group.leave() + + completion(.invalidAddress(address: address)) + } + } + } /* @@ -409,6 +505,19 @@ extension AdamantAccountsProvider { */ + private func createCoreDataAccount(with address: String, publicKey: String, contextt: NSManagedObjectContext) -> CoreDataAccount { + var coreAccount: CoreDataAccount! + + DispatchQueue.onMainSync { + coreAccount = createCoreDataAccount( + with: address, + publicKey: publicKey, + context: contextt + ) + } + return coreAccount + } + private func createCoreDataAccount(with address: String, publicKey: String) -> CoreDataAccount { var coreAccount: CoreDataAccount! diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index af92198a0..478ade2c2 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -39,6 +39,8 @@ class AdamantChatsProvider: ChatsProvider { private(set) var removedMessages: [String] = [] public var isChatLoaded: [String : Bool] = [:] + public var chatMaxMessages: [String : Int] = [:] + public var chatLoadedMessages: [String : Int] = [:] private(set) var isInitiallySynced: Bool = false { didSet { @@ -180,6 +182,11 @@ extension AdamantChatsProvider { // Drop props receivedLastHeight = nil readedLastHeight = nil + roomsMaxCount = nil + roomsLoadedCount = nil + isChatLoaded.removeAll() + chatMaxMessages.removeAll() + chatLoadedMessages.removeAll() // Drop store securedStore.remove(StoreKey.chatProvider.address) @@ -211,21 +218,24 @@ extension AdamantChatsProvider { return } + let prevState = state + state = .updating + let cms = DispatchSemaphore(value: 1) // MARK: 3. Get transactions let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateContext.parent = self.stack.container.viewContext apiService.getChatRooms(address: address, offset: offset) { [weak self] result in switch result { - case .success(let trans): - self?.roomsMaxCount = trans.count + case .success(let chatrooms): + self?.roomsMaxCount = chatrooms.count if let roomsLoadedCount = self?.roomsLoadedCount { - self?.roomsLoadedCount = roomsLoadedCount + (trans.chats?.count ?? 0) + self?.roomsLoadedCount = roomsLoadedCount + (chatrooms.chats?.count ?? 0) } else { - self?.roomsLoadedCount = trans.chats?.count ?? 0 + self?.roomsLoadedCount = chatrooms.chats?.count ?? 0 } var array = Array() - trans.chats?.forEach({ room in + chatrooms.chats?.forEach({ room in if let last = room.lastTransaction { array.append(last) } @@ -241,6 +251,7 @@ extension AdamantChatsProvider { if let synced = self?.isInitiallySynced, !synced { self?.isInitiallySynced = true } + self?.setState(.upToDate, previous: prevState) completion?() }) } @@ -264,9 +275,13 @@ extension AdamantChatsProvider { apiService.getChatMessages(address: address, addressRecipient: addressRecipient, offset: offset) { [weak self] result in switch result { - case .success(let transactions): + case .success(let chatroom): + let transactions = chatroom.messages if let transactions = transactions { self?.isChatLoaded[addressRecipient] = true + self?.chatMaxMessages[addressRecipient] = chatroom.count ?? 0 + let loadedCount = self?.chatLoadedMessages[addressRecipient] ?? 0 + self?.chatLoadedMessages[addressRecipient] = loadedCount + (chatroom.messages?.count ?? 0) self?.processingQueue.async { self?.process(messageTransactions: transactions, senderId: address, @@ -996,7 +1011,11 @@ extension AdamantChatsProvider { for address in notFound { keysGroup.enter() // Enter 1 - accountsProvider.getAccount(byAddress: address) { result in + let transaction = grouppedTransactions[address]?.first + let isOut = transaction?.isOut ?? false + let publicKey = isOut ? transaction?.transaction.recipientPublicKey : transaction?.transaction.senderPublicKey + + accountsProvider.getAccount(byAddress: address, publicKey: publicKey ?? "") { result in defer { keysGroup.leave() // Exit 1 } diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index 238f7c9a4..367a0f788 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -25,6 +25,7 @@ extension String.adamantLocalized { class ChatListViewController: UIViewController { let cellIdentifier = "cell" + let loadingCellIdentifier = "loadingCell" let cellHeight: CGFloat = 76.0 // MARK: Dependencies @@ -77,6 +78,7 @@ class ChatListViewController: UIViewController { @IBOutlet weak var busyIndicatorLabel: UILabel! private(set) var isBusy: Bool = true + private var lastSystemChatPositionRow: Int? // MARK: Keyboard // SplitView sends double notifications about keyboard. @@ -104,6 +106,7 @@ class ChatListViewController: UIViewController { tableView.dataSource = self tableView.delegate = self tableView.register(UINib(nibName: "ChatTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier) + tableView.register(LoadingTableViewCell.self, forCellReuseIdentifier: loadingCellIdentifier) tableView.refreshControl = refreshControl if self.accountService.account != nil { @@ -157,6 +160,7 @@ class ChatListViewController: UIViewController { if let synced = notification.userInfo?[AdamantUserInfoKey.ChatProvider.initiallySynced] as? Bool { self?.didLoadedMessages?() self?.setIsBusy(!synced) + self?.tableView.reloadData() } else if let synced = self?.chatsProvider.isInitiallySynced { self?.setIsBusy(!synced) } else { @@ -349,6 +353,9 @@ class ChatListViewController: UIViewController { extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let f = chatsController?.fetchedObjects { + if f.count > 0 { + return isBusy ? f.count + 1 : f.count + } return f.count } else { return 0 @@ -359,6 +366,10 @@ extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { return cellHeight } + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { // fix jump bug in ios 12 + return cellHeight + } + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { return UIView() } @@ -384,6 +395,12 @@ extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { // MARK: - UITableView Cells extension ChatListViewController { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + if isBusy && indexPath.row == lastSystemChatPositionRow { + let cell = tableView.dequeueReusableCell(withIdentifier: loadingCellIdentifier, for: indexPath) as! LoadingTableViewCell + cell.startLoadAnimating() + return cell + } + let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ChatTableViewCell cell.accessoryType = .disclosureIndicator @@ -399,24 +416,61 @@ extension ChatListViewController { } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - if let cell = cell as? ChatTableViewCell, let chat = chatsController?.object(at: indexPath) { - configureCell(cell, for: chat) + if isBusy, + indexPath.row == lastSystemChatPositionRow, + let cell = cell as? LoadingTableViewCell { + configureCell(cell) + } else if let cell = cell as? ChatTableViewCell { + let nIndexPath = isBusy && indexPath.row >= (lastSystemChatPositionRow ?? 0) ? IndexPath(row: indexPath.row - 1, section: 0) : indexPath + if let chat = chatsController?.object(at: nIndexPath) { + configureCell(cell, for: chat) + } } if let roomsLoadedCount = chatsProvider.roomsLoadedCount, let roomsMaxCount = chatsProvider.roomsMaxCount, roomsLoadedCount < roomsMaxCount, - tableView.numberOfRows(inSection: 0) >= roomsLoadedCount { - tableView.addLoading(indexPath) { [weak self] in - self?.chatsProvider.getChatRooms(offset: roomsLoadedCount, completion: { - DispatchQueue.main.async { - tableView.stopLoading() + roomsMaxCount > 0, + !isBusy, + tableView.numberOfRows(inSection: 0) >= roomsLoadedCount, + let lastVisibleIndexPath = tableView.indexPathsForVisibleRows?.last, + indexPath == lastVisibleIndexPath, + indexPath.row == tableView.numberOfRows(inSection: 0) - 1 { + isBusy = true + + lastSystemChatPositionRow = getBottomSystemChatIndex() + DispatchQueue.main.async { [weak self] in + if #available(iOS 11.0, *) { + tableView.performBatchUpdates { + tableView.insertRows(at: [IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0)], with: .none) } - }) + } else { + tableView.beginUpdates() + tableView.insertRows(at: [IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0)], with: .none) + tableView.endUpdates() + } } + + chatsProvider.getChatRooms(offset: roomsLoadedCount, completion: { [weak self] in + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { + self?.isBusy = false + if #available(iOS 13.0, *) { + tableView.reloadData() + } else { + let oldTableViewHeight = tableView.contentSize.height; + tableView.reloadData() + let newTableViewHeight = tableView.contentSize.height; + tableView.contentOffset = CGPoint(x: 0, y: newTableViewHeight - oldTableViewHeight); + } + }) + }) } } + private func configureCell(_ cell: LoadingTableViewCell) { + cell.startLoadAnimating() + } + private func configureCell(_ cell: ChatTableViewCell, for chatroom: Chatroom) { if let partner = chatroom.partner { if let title = chatroom.title { @@ -471,12 +525,14 @@ extension ChatListViewController { // MARK: - NSFetchedResultsControllerDelegate extension ChatListViewController: NSFetchedResultsControllerDelegate { func controllerWillChangeContent(_ controller: NSFetchedResultsController) { + if isBusy { return } if controller == chatsController { tableView.beginUpdates() } } func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + if isBusy { return } switch controller { case let c where c == chatsController: tableView.endUpdates() @@ -490,6 +546,7 @@ extension ChatListViewController: NSFetchedResultsControllerDelegate { } func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { + if isBusy { return } switch controller { // MARK: Chats controller case let c where c == chatsController: @@ -897,6 +954,20 @@ extension ChatListViewController { return vc.chatroom } + + /// First system botoom chat index + func getBottomSystemChatIndex() -> Int { + var index = 0 + try? chatsController?.performFetch() + chatsController?.fetchedObjects?.enumerated().forEach({ (i, room) in + if index == 0, + let date = room.updatedAt as? Date, + date == Date.adamantNullDate { + index = i + } + }) + return index + } } // MARK: Search diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index 91fdf90b3..d41ba2228 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -293,6 +293,8 @@ extension ChatViewController: MessagesDisplayDelegate { let header = messagesCollectionView.dequeueReusableHeaderView(HeaderReusableView.self, for: indexPath) if (indexPath.section == 0 && isBusy) { header.setupLoadAnimating() + } else { + header.stopLoadAnimating() } return header } @@ -560,7 +562,7 @@ extension ChatViewController: MessagesLayoutDelegate { } func headerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize { - if (section == 0) { + if (section == 0 && isNeedToLoadMoore()) { return CGSize(width: messagesCollectionView.bounds.width, height: HeaderReusableView.height) } else { return .zero diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 43ef99e6d..3d57b73e6 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -200,9 +200,8 @@ class ChatViewController: MessagesViewController { private var busyBackgroundView: UIView? private var spinner = UIActivityIndicatorView(style: .whiteLarge) - private var loadedMessages = 0 - var isBusy = true - + var isBusy = false + //MARK: Background UI private let amadantLogoImageView: UIImageView = { let iv = UIImageView() @@ -215,8 +214,6 @@ class ChatViewController: MessagesViewController { super.viewDidLoad() navigationItem.rightBarButtonItem = UIBarButtonItem(title: "•••", style: .plain, target: self, action: #selector(properties)) - setBackgroundUI() - guard let chatroom = chatroom else { return } @@ -416,7 +413,7 @@ class ChatViewController: MessagesViewController { scrollToBottomBtn.frame = CGRect.zero scrollToBottomBtn.translatesAutoresizingMaskIntoConstraints = false scrollToBottomBtn.addTarget(self, action: #selector(scrollDown), for: .touchUpInside) - + scrollToBottomBtn.isHidden = true self.view.addSubview(scrollToBottomBtn) keyboardManager.on(event: .willChangeFrame) { [weak self] (notification) in @@ -447,23 +444,31 @@ class ChatViewController: MessagesViewController { setBusyIndicator(state: false) return } + if address == AdamantContacts.adamantBountyWallet.name { setBusyIndicator(state: false) return } + setBusyIndicator(state: true) - chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] _ in + chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] count in DispatchQueue.main.async { - self?.messagesCollectionView.reloadDataAndKeepOffset() - self?.messagesCollectionView.performBatchUpdates(nil, completion: { - (result) in - DispatchQueue.main.async { - print("scroll to scrollToBottom ViewdidLoad") - self?.setBusyIndicator(state: false) - self?.reloadTopScetionIfNeeded() + if #available(iOS 13.0, *) { + self?.messagesCollectionView.reloadDataAndKeepOffset() + } else { + self?.messagesCollectionView.reloadData() + self?.messagesCollectionView.reloadDataAndKeepOffset() + } + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + if #available(iOS 13.0, *) { + + } else { + self?.messagesCollectionView.reloadDataAndKeepOffset() } - }) + self?.setBusyIndicator(state: false) + //self?.reloadTopScetionIfNeeded() + } } } } @@ -872,7 +877,7 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { var scrollToBottom = changes.first { $0.type == .insert } != nil - if !isFirstLayout { + if !isFirstLayout && changes.first?.type != nil { scrollToBottom = scrollToBottomBtn.isHidden } @@ -1078,7 +1083,8 @@ private class StatusUpdateProcedure: Procedure { extension ChatViewController { func setBusyIndicator(state: Bool) { isBusy = state - if busyBackgroundView == nil { + if busyBackgroundView == nil && state { + setBackgroundUI() busyBackgroundView = UIView() busyBackgroundView?.backgroundColor = UIColor(white: 0, alpha: 0.1) busyBackgroundView?.frame = view.frame @@ -1089,14 +1095,23 @@ extension ChatViewController { busyBackgroundView?.addSubview(spinner) spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true - + messagesCollectionView.alpha = 0.0 - messageInputBar.isUserInteractionEnabled = false + messageInputBar.sendButton.isEnabled = false + messageInputBar.inputTextView.isEditable = false + messageInputBar.leftStackView.isUserInteractionEnabled = false } if !state { - messageInputBar.isUserInteractionEnabled = true - UIView.animate(withDuration: 0.25) { [weak self] in + if busyBackgroundView != nil { + reloadTopScetionIfNeeded() + } + + messageInputBar.sendButton.isEnabled = true + messageInputBar.inputTextView.isEditable = true + messageInputBar.leftStackView.isUserInteractionEnabled = true + + UIView.animate(withDuration: 0.25, delay: 0.25) { [weak self] in self?.busyBackgroundView?.backgroundColor = .clear self?.messagesCollectionView.alpha = 1.0 self?.amadantLogoImageView.alpha = 0.0 @@ -1110,16 +1125,14 @@ extension ChatViewController { //MARK: Load moore message extension ChatViewController { func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - try? chatController?.performFetch() if indexPath.section < 2, - let count = chatController?.fetchedObjects?.count, let address = chatroom?.partner?.address, !isBusy, - loadedMessages != count { + isNeedToLoadMoore() { if address == AdamantContacts.adamantBountyWallet.name { return } isBusy = true - loadedMessages = count - chatsProvider.getChatMessages(with: address, offset: count) { [weak self] _count in + let mcount = chatsProvider.chatLoadedMessages[address] ?? 0 + chatsProvider.getChatMessages(with: address, offset: mcount) { [weak self] _count in DispatchQueue.main.async { self?.messagesCollectionView.reloadDataAndKeepOffset() self?.isBusy = false @@ -1136,6 +1149,14 @@ extension ChatViewController { self.messagesCollectionView.reloadSections(IndexSet(integer: 0)) } } + + func isNeedToLoadMoore() -> Bool { + if let address = chatroom?.partner?.address, + chatsProvider.chatLoadedMessages[address] ?? 0 < chatsProvider.chatMaxMessages[address] ?? 0 { + return true + } + return false + } } // MARK: - Background UI diff --git a/Adamant/Stories/Chats/HeaderReusableView.swift b/Adamant/Stories/Chats/HeaderReusableView.swift index 886a4ca1e..11520dd88 100644 --- a/Adamant/Stories/Chats/HeaderReusableView.swift +++ b/Adamant/Stories/Chats/HeaderReusableView.swift @@ -30,15 +30,61 @@ class HeaderReusableView: MessageReusableView { createUI() } - /// Setup the receiver with text. - /// - /// - Parameter text: The text to be displayed. + /// Start animation. func setupLoadAnimating() { spinner.startAnimating() + spinner.isHidden = false } + /// Stop animation. func stopLoadAnimating() { spinner.stopAnimating() + spinner.isHidden = true + } + + override func prepareForReuse() { + spinner.stopAnimating() + } + + // MARK: - Private Methods + private func createUI() { + spinner.translatesAutoresizingMaskIntoConstraints = false + addSubview(spinner) + spinner.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + spinner.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true + } +} + +class LoadingTableViewCell: UITableViewCell { + // MARK: - Private Properties + static private let insets = UIEdgeInsets(top: 12, left: 80, bottom: 12, right: 80) + + private var spinner = UIActivityIndicatorView(style: .gray) + + // MARK: - Public Methods + static var height: CGFloat { + return insets.top + insets.bottom + 27 + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + createUI() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + /// Start animation. + func startLoadAnimating() { + spinner.startAnimating() + spinner.isHidden = false + } + + /// Stop animation. + func stopLoadAnimating() { + spinner.stopAnimating() + spinner.isHidden = true } override func prepareForReuse() { From fba1477727fe007e38deccf94e9c821772f74c90 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 17 Jun 2022 17:48:59 +0300 Subject: [PATCH 05/12] Fixed Chatrooms bugs: lost chats, transfers, design --- Adamant.xcodeproj/project.pbxproj | 16 ++-- .../Adamant.xcdatamodel/contents | 4 +- .../DataProviders/AdamantChatsProvider.swift | 73 ++++++++++++++++--- .../AdamantTransfersProvider.swift | 20 ++--- .../Chats/ChatListViewController.swift | 37 ++++++++-- Adamant/Stories/Chats/ChatTableViewCell.xib | 26 ++++--- .../Stories/Chats/ChatViewController.swift | 38 +++++----- 7 files changed, 145 insertions(+), 69 deletions(-) diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index b3864b580..84bc4a124 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -2917,7 +2917,7 @@ CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -2946,7 +2946,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3093,7 +3093,7 @@ CODE_SIGN_ENTITLEMENTS = Adamant/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3122,7 +3122,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3150,7 +3150,7 @@ CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3179,7 +3179,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3208,7 +3208,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -3237,7 +3237,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 187; + CURRENT_PROJECT_VERSION = 189; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; diff --git a/Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents b/Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents index 63a430530..40bf6ce99 100644 --- a/Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents +++ b/Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -62,7 +62,7 @@ - + diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 478ade2c2..8fe51839e 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -254,6 +254,11 @@ extension AdamantChatsProvider { self?.setState(.upToDate, previous: prevState) completion?() }) + if let synced = self?.isInitiallySynced, !synced { + self?.isInitiallySynced = true + } + self?.setState(.upToDate, previous: prevState) + completion?() } case .failure(_): completion?() @@ -892,6 +897,23 @@ extension AdamantChatsProvider { return controller } + + /// Search transaction in local storage + /// + /// - Parameter id: Transacton ID + /// - Returns: Transaction, if found + func getTransfer(id: String, context: NSManagedObjectContext) -> TransferTransaction? { + let request = NSFetchRequest(entityName: TransferTransaction.entityName) + request.predicate = NSPredicate(format: "transactionId == %@", String(id)) + request.fetchLimit = 1 + + do { + let result = try context.fetch(request) + return result.first + } catch { + return nil + } + } } // MARK: - Processing @@ -1084,12 +1106,12 @@ extension AdamantChatsProvider { height = chatTransaction.height } - let trans = privateChatroom.transactions?.first(where: { message in + let transactionExist = privateChatroom.transactions?.first(where: { message in return (message as? ChatTransaction)?.txId == chatTransaction.txId }) as? ChatTransaction if !trs.isOut { - if trans == nil { + if transactionExist == nil { newMessageTransactions.append(chatTransaction) } @@ -1116,16 +1138,16 @@ extension AdamantChatsProvider { } } - if trans == nil { + if transactionExist == nil { if (chatTransaction.blockId?.isEmpty ?? true) && (chatTransaction.amountValue ?? 0.0 > 0.0) { chatTransaction.statusEnum = .pending } messages.insert(chatTransaction) } else { - trans?.height = chatTransaction.height - trans?.blockId = chatTransaction.blockId - trans?.confirmations = chatTransaction.confirmations - trans?.statusEnum = .delivered + transactionExist?.height = chatTransaction.height + transactionExist?.blockId = chatTransaction.blockId + transactionExist?.confirmations = chatTransaction.confirmations + transactionExist?.statusEnum = .delivered } } } @@ -1256,11 +1278,36 @@ extension AdamantChatsProvider { /// - context: context to insert parsed transaction to /// - Returns: New parsed transaction private func chatTransaction(from transaction: Transaction, isOutgoing: Bool, publicKey: String, privateKey: String, partner: BaseAccount, context: NSManagedObjectContext) -> ChatTransaction? { + let messageTransaction: ChatTransaction guard let chat = transaction.asset.chat else { + if transaction.type == .send { + if let trs = getTransfer(id: String(transaction.id), context: context) { + messageTransaction = trs + } else { + messageTransaction = TransferTransaction(context: context) + } + messageTransaction.amount = transaction.amount as NSDecimalNumber + messageTransaction.date = transaction.date as NSDate + messageTransaction.recipientId = transaction.recipientId + messageTransaction.senderId = transaction.senderId + messageTransaction.transactionId = String(transaction.id) + messageTransaction.type = Int16(TransactionType.chatMessage.rawValue) + messageTransaction.showsChatroom = true + messageTransaction.height = Int64(transaction.height) + messageTransaction.isConfirmed = true + messageTransaction.isOutgoing = isOutgoing + messageTransaction.blockId = transaction.blockId + messageTransaction.confirmations = transaction.confirmations + messageTransaction.chatMessageId = UUID().uuidString + messageTransaction.fee = transaction.fee as NSDecimalNumber + messageTransaction.statusEnum = MessageStatus.delivered + messageTransaction.partner = partner + return messageTransaction + } return nil } - let messageTransaction: ChatTransaction + // MARK: Decode message, message must contain data if let decodedMessage = adamantCore.decodeMessage(rawMessage: chat.message, rawNonce: chat.ownMessage, senderPublicKey: publicKey, privateKey: privateKey)?.trimmingCharacters(in: .whitespacesAndNewlines) { if (decodedMessage.isEmpty && transaction.amount > 0) || !decodedMessage.isEmpty { @@ -1268,9 +1315,13 @@ extension AdamantChatsProvider { // MARK: Text message case .message, .messageOld, .signal, .unknown: if transaction.amount > 0 { - let trs = TransferTransaction(entity: TransferTransaction.entity(), insertInto: context) - trs.comment = decodedMessage - messageTransaction = trs + if let trs = getTransfer(id: String(transaction.id), context: context) { + messageTransaction = trs + } else { + let trs = TransferTransaction(entity: TransferTransaction.entity(), insertInto: context) + trs.comment = decodedMessage + messageTransaction = trs + } } else { let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) trs.message = decodedMessage diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index c89002842..adf5bee19 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -873,7 +873,7 @@ extension AdamantTransfersProvider { } else { unconfirmedsSemaphore.signal() } - + print("transfer=", String(t.id)) let transfer = TransferTransaction(context: context) transfer.amount = t.amount as NSDecimalNumber transfer.date = t.date as NSDate @@ -896,9 +896,9 @@ extension AdamantTransfersProvider { if let partner = partners[partnerId] { transfer.partner = partner - if let chatroom = (partner as? CoreDataAccount)?.chatroom { - transfer.chatroom = chatroom - } +// if let chatroom = (partner as? CoreDataAccount)?.chatroom { +// transfer.chatroom = chatroom +// } } if t.height > height { @@ -940,12 +940,12 @@ extension AdamantTransfersProvider { if context.hasChanges { do { try context.save() - - // MARK: 7. Update lastTransactions - let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom } - DispatchQueue.main.async { - viewContextChatrooms.forEach { $0.updateLastTransaction() } - } + +// // MARK: 7. Update lastTransactions +// let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom } +// DispatchQueue.main.async { +// viewContextChatrooms.forEach { $0.updateLastTransaction() } +// } } catch { print("TransferProvider: Failed to save changes to CoreData: \(error.localizedDescription)") } diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index 367a0f788..933b220ee 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -71,6 +71,8 @@ class ChatListViewController: UIViewController { return parser }() + private var defaultSeparatorInstets: UIEdgeInsets? + // MARK: Busy indicator @IBOutlet weak var busyBackgroundView: UIView! @@ -375,7 +377,15 @@ extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if let chatroom = chatsController?.object(at: indexPath) { + if isBusy, + indexPath.row == lastSystemChatPositionRow, + let cell = tableView.cellForRow(at: indexPath), + let _ = cell as? LoadingTableViewCell { + tableView.deselectRow(at: indexPath, animated: true) + return + } + let nIndexPath = isBusy && indexPath.row >= (lastSystemChatPositionRow ?? 0) ? IndexPath(row: indexPath.row - 1, section: 0) : indexPath + if let chatroom = chatsController?.object(at: nIndexPath) { let vc = chatViewController(for: chatroom) if let split = self.splitViewController { @@ -425,6 +435,16 @@ extension ChatListViewController { if let chat = chatsController?.object(at: nIndexPath) { configureCell(cell, for: chat) } + if isBusy, + indexPath.row == (lastSystemChatPositionRow ?? 0) - 1 { + cell.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0) + } else { + if let defaultSeparatorInstets = defaultSeparatorInstets { + cell.separatorInset = defaultSeparatorInstets + } else { + defaultSeparatorInstets = cell.separatorInset + } + } } if let roomsLoadedCount = chatsProvider.roomsLoadedCount, @@ -433,20 +453,22 @@ extension ChatListViewController { roomsMaxCount > 0, !isBusy, tableView.numberOfRows(inSection: 0) >= roomsLoadedCount, - let lastVisibleIndexPath = tableView.indexPathsForVisibleRows?.last, - indexPath == lastVisibleIndexPath, - indexPath.row == tableView.numberOfRows(inSection: 0) - 1 { + let lastVisibleIndexPath = tableView.indexPathsForVisibleRows, + lastVisibleIndexPath.contains(IndexPath(row: tableView.numberOfRows(inSection: 0) - 3, section: 0)) { isBusy = true - lastSystemChatPositionRow = getBottomSystemChatIndex() DispatchQueue.main.async { [weak self] in if #available(iOS 11.0, *) { tableView.performBatchUpdates { - tableView.insertRows(at: [IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0)], with: .none) + tableView.insertRows(at: [ + IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0) + ], with: .none) + tableView.reloadRows(at: [IndexPath(row: (self?.lastSystemChatPositionRow ?? 0) - 1, section: 0)], with: .none) } } else { tableView.beginUpdates() tableView.insertRows(at: [IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0)], with: .none) + tableView.reloadRows(at: [IndexPath(row: (self?.lastSystemChatPositionRow ?? 0) - 1, section: 0)], with: .none) tableView.endUpdates() } } @@ -477,6 +499,9 @@ extension ChatListViewController { cell.accountLabel.text = title } else if let name = partner.name { cell.accountLabel.text = name + } else if let address = partner.address, + let name = self.addressBook.addressBook[address] { + cell.accountLabel.text = name.checkAndReplaceSystemWallets() } else { cell.accountLabel.text = partner.address } diff --git a/Adamant/Stories/Chats/ChatTableViewCell.xib b/Adamant/Stories/Chats/ChatTableViewCell.xib index 253280fde..4bcd46133 100644 --- a/Adamant/Stories/Chats/ChatTableViewCell.xib +++ b/Adamant/Stories/Chats/ChatTableViewCell.xib @@ -1,12 +1,9 @@ - - - - + + - - + @@ -17,37 +14,37 @@ - + - + - + @@ -83,6 +80,11 @@ + + + + + diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 3d57b73e6..4f5f5a59f 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -105,6 +105,7 @@ class ChatViewController: MessagesViewController { internal var showsDateHeaderAfterTimeInterval: TimeInterval = 3600 private var isFirstLayout = true + private var didLoaded = false // Content insets are broken after modal view dissapears private var fixKeyboardInsets = false @@ -444,30 +445,24 @@ class ChatViewController: MessagesViewController { setBusyIndicator(state: false) return } - + if address == AdamantContacts.adamantBountyWallet.name { setBusyIndicator(state: false) return } - + setBusyIndicator(state: true) chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] count in DispatchQueue.main.async { - if #available(iOS 13.0, *) { - self?.messagesCollectionView.reloadDataAndKeepOffset() - } else { - self?.messagesCollectionView.reloadData() - self?.messagesCollectionView.reloadDataAndKeepOffset() - } + self?.messagesCollectionView.reloadDataAndKeepOffset() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { if #available(iOS 13.0, *) { - + } else { - self?.messagesCollectionView.reloadDataAndKeepOffset() + self?.messagesCollectionView.scrollToItem(at: IndexPath(row: 0, section: count - 1), at: .top, animated: false) } self?.setBusyIndicator(state: false) - //self?.reloadTopScetionIfNeeded() } } } @@ -560,6 +555,8 @@ class ChatViewController: MessagesViewController { if forceScrollToBottom ?? false && !scrollToBottomBtn.isHidden { scrollDown() } + + didLoaded = true } override func viewDidDisappear(_ animated: Bool) { @@ -580,7 +577,6 @@ class ChatViewController: MessagesViewController { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - guard let address = chatroom?.partner?.address else { return } // MARK: 4.2 Scroll to message @@ -605,8 +601,9 @@ class ChatViewController: MessagesViewController { if self.messageToShow == nil { if let offset = self.chatsProvider.chatPositon[address] { self.chatPositionOffset = CGFloat(offset) + print("chatPositionOffset=", offset) self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata - let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) + let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) + 38 messagesCollectionView.performBatchUpdates(nil) { _ in self.messagesCollectionView.scrollRectToVisible(CGRect(x: 0.0, y: collectionViewContentHeight - 1.0, width: 1.0, height: 1.0), animated: false) } @@ -910,8 +907,10 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { guard let section = change.indexPath?.row else { continue } - - chat.reloadItems(at: [IndexPath(row: 0, section: section)]) + if chat.indexPathsForVisibleItems.contains(IndexPath(row: 0, section: section)) { + chat.reloadItems(at: [IndexPath(row: 0, section: section)]) + } + scrollToBottom = false @unknown default: break } @@ -998,8 +997,6 @@ extension ChatViewController { var offset = scrollView.contentSize.height - scrollView.bounds.height - scrollView.contentOffset.y + messageInputBar.bounds.height offset += self.keyboardHeight - if self.messageToShow != nil && offset < 0 { offset *= -1 } - if offset > chatPositionDelata { chatPositionOffset = offset } else { @@ -1128,11 +1125,12 @@ extension ChatViewController { if indexPath.section < 2, let address = chatroom?.partner?.address, !isBusy, - isNeedToLoadMoore() { + isNeedToLoadMoore(), + didLoaded { if address == AdamantContacts.adamantBountyWallet.name { return } isBusy = true - let mcount = chatsProvider.chatLoadedMessages[address] ?? 0 - chatsProvider.getChatMessages(with: address, offset: mcount) { [weak self] _count in + let offset = chatsProvider.chatLoadedMessages[address] ?? 0 + chatsProvider.getChatMessages(with: address, offset: offset) { [weak self] _count in DispatchQueue.main.async { self?.messagesCollectionView.reloadDataAndKeepOffset() self?.isBusy = false From 3bf482dfa6ee08b38600f180310686e306296a30 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 24 Jun 2022 20:01:29 +0300 Subject: [PATCH 06/12] Infinity loading messages if no connection & improvements notifications work & bug fixes --- Adamant.xcodeproj/project.pbxproj | 32 ++-- Adamant/AppDelegate.swift | 32 +++- .../Assets/l18n/de.lproj/Localizable.strings | 3 + .../Assets/l18n/en.lproj/Localizable.strings | 3 + .../Assets/l18n/ru.lproj/Localizable.strings | 3 + .../DataProviders/AccountsProvider.swift | 20 +- Adamant/Services/AdamantReachability.swift | 59 +++--- .../AdamantAccountsProvider.swift | 6 +- .../DataProviders/AdamantChatsProvider.swift | 172 +++++++++++++----- .../AdamantTransfersProvider.swift | 26 ++- .../Chats/ChatListViewController.swift | 1 - .../Stories/Chats/ChatViewController.swift | 36 +++- .../Wallets/WalletViewControllerBase.swift | 8 +- AdamantShared/AdamantResources.swift | 2 + 14 files changed, 275 insertions(+), 128 deletions(-) diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 84bc4a124..09d77b8b7 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -2917,10 +2917,10 @@ CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2946,10 +2946,10 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3093,12 +3093,12 @@ CODE_SIGN_ENTITLEMENTS = Adamant/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; INFOPLIST_FILE = Adamant/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3122,12 +3122,12 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; INFOPLIST_FILE = Adamant/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3150,10 +3150,10 @@ CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3179,10 +3179,10 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3208,10 +3208,10 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3237,10 +3237,10 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 189; + CURRENT_PROJECT_VERSION = 190; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index 097d3b185..25f21b037 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -170,11 +170,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { switch connection { case .cellular, .wifi: - self?.dialogService.dissmisNoConnectionNotification() + DispatchQueue.onMainSync { + self?.dialogService.dissmisNoConnectionNotification() + } repeater.resumeAll() case .none: - self?.dialogService.showNoConnectionNotification() + DispatchQueue.onMainSync { + self?.dialogService.showNoConnectionNotification() + } repeater.pauseAll() } } @@ -341,7 +345,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { //MARK: Open Chat From Notification func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - if let recipientAddress = userInfo[AdamantNotificationUserInfoKeys.pushRecipient] as? String + if let transactionID = userInfo[AdamantNotificationUserInfoKeys.transactionId] as? String, + let recipientAddress = userInfo[AdamantNotificationUserInfoKeys.pushRecipient] as? String { if application.applicationState != .background && application.applicationState != .inactive { completionHandler(.noData) @@ -355,7 +360,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { switch list.accountService.state { case .loggedIn: - self.openDialog(chatList: chatList, tabbar: tabbar, list: list, recipientAddress: recipientAddress) + self.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, recipientAddress: recipientAddress) case .notLogged: break case .isLoggingIn: @@ -367,7 +372,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { // if not logged in list.didLoadedMessages = { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - self?.openDialog(chatList: chatList, tabbar: tabbar, list: list, recipientAddress: recipientAddress) + self?.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, recipientAddress: recipientAddress) } } } @@ -377,9 +382,19 @@ extension AppDelegate: UNUserNotificationCenterDelegate { } } - func openDialog(chatList: UINavigationController, tabbar: UITabBarController, list: ChatListViewController, recipientAddress: String) { + func openDialog(chatList: UINavigationController, tabbar: UITabBarController, list: ChatListViewController, transactionID: String, recipientAddress: String) { + if let chatVCNav = chatList.viewControllers.last as? UINavigationController, + let chatVC = chatVCNav.viewControllers.first as? ChatViewController, + chatVC.chatroom?.partner?.address == recipientAddress { + chatVC.scrollDown() + return + } + guard let chatroom = list.chatsController?.fetchedObjects?.first(where: { room in - return room.lastTransaction?.recipientAddress == recipientAddress + let transactionExist = room.transactions?.first(where: { message in + return (message as? ChatTransaction)?.txId == transactionID + }) + return transactionExist != nil }) else { return } chatList.popToRootViewController(animated: false) @@ -480,7 +495,6 @@ extension AppDelegate { } else { unread = true } - if let exchenge = AdamantContacts.adamantExchange.messages["chats.welcome_message"] { chatProvider.fakeReceived(message: exchenge.message, senderId: AdamantContacts.adamantExchange.address, @@ -531,7 +545,7 @@ extension AppDelegate { if let welcome = AdamantContacts.adamantBountyWallet.messages["chats.welcome_message"] { chatProvider.fakeReceived(message: welcome.message, - senderId: AdamantContacts.adamantBountyWallet.name, + senderId: AdamantContacts.adamantWelcomeWallet.name, date: Date.adamantNullDate, unread: unread, silent: welcome.silentNotification, diff --git a/Adamant/Assets/l18n/de.lproj/Localizable.strings b/Adamant/Assets/l18n/de.lproj/Localizable.strings index 7088e686d..80e9bf6d9 100755 --- a/Adamant/Assets/l18n/de.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/de.lproj/Localizable.strings @@ -73,6 +73,9 @@ /* System accounts: ADAMANT Tokens */ "Accounts.AdamantTokens" = "Willkommen in ADAMANT"; +/* System accounts: ADAMANT Bounty */ +"Accounts.AdamantBounty" = "ADAMANT Bounty Wallet"; + /* System accounts: ADAMANT iOS Support */ "Accounts.iOSSupport" = "ADAMANT iOS Support"; diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings index dbc9da48d..f2b862a63 100755 --- a/Adamant/Assets/l18n/en.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings @@ -76,6 +76,9 @@ /* System accounts: ADAMANT Tokens */ "Accounts.AdamantTokens" = "Welcome to ADAMANT"; +/* System accounts: ADAMANT Bounty */ +"Accounts.AdamantBounty" = "ADAMANT Bounty Wallet"; + /* System accounts: ADAMANT iOS Support */ "Accounts.iOSSupport" = "ADAMANT iOS Support"; diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings index 53601c937..52cb2cac0 100644 --- a/Adamant/Assets/l18n/ru.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.strings @@ -73,6 +73,9 @@ /* System accounts: ADAMANT Tokens */ "Accounts.AdamantTokens" = "Приветствие АДАМАНТа"; +/* System accounts: ADAMANT Bounty */ +"Accounts.AdamantBounty" = "ADAMANT Bounty Wallet"; + /* System accounts: ADAMANT iOS Support */ "Accounts.iOSSupport" = "Техподдержка iOS ADAMANT"; diff --git a/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift b/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift index b7c31bc7b..12aa9a883 100644 --- a/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift @@ -88,9 +88,10 @@ enum AdamantContacts { case adamantExchange case betOnBitcoin case donate + case adamantWelcomeWallet static let systemAddresses: [String] = { - return [AdamantContacts.adamantExchange.name, AdamantContacts.betOnBitcoin.name, AdamantContacts.adamantIco.name, AdamantContacts.adamantBountyWallet.name, AdamantContacts.adamantNewBountyWallet.name, AdamantContacts.donate.name] + return [AdamantContacts.adamantExchange.name, AdamantContacts.betOnBitcoin.name, AdamantContacts.adamantIco.name, AdamantContacts.adamantBountyWallet.name, AdamantContacts.adamantNewBountyWallet.name, AdamantContacts.donate.name, AdamantContacts.adamantWelcomeWallet.name] }() static func messagesFor(address: String) -> [String:SystemMessage]? { @@ -117,13 +118,14 @@ enum AdamantContacts { var name: String { switch self { - case .adamantBountyWallet, .adamantNewBountyWallet: + case .adamantWelcomeWallet: return NSLocalizedString("Accounts.AdamantTokens", comment: "System accounts: ADAMANT Tokens") + case .adamantBountyWallet, .adamantNewBountyWallet: + return NSLocalizedString("Accounts.AdamantBounty", comment: "System accounts: ADAMANT Bounty") case .adamantIco: return "Adamant ICO" case .iosSupport: return NSLocalizedString("Accounts.iOSSupport", comment: "System accounts: ADAMANT iOS Support") - case .adamantExchange: return NSLocalizedString("Accounts.AdamantExchange", comment: "System accounts: ADAMANT Exchange") case .betOnBitcoin: @@ -151,6 +153,7 @@ enum AdamantContacts { case .adamantExchange: return AdamantResources.contacts.adamantExchange case .betOnBitcoin: return AdamantResources.contacts.betOnBitcoin case .donate: return AdamantResources.contacts.donateWallet + case .adamantWelcomeWallet: return AdamantResources.contacts.adamantWelcomeWallet } } @@ -163,12 +166,13 @@ enum AdamantContacts { case .iosSupport: return AdamantResources.contacts.iosSupportPK case .adamantIco: return AdamantResources.contacts.adamantIcoPK case .donate: return AdamantResources.contacts.donateWalletPK + case .adamantWelcomeWallet: return AdamantResources.contacts.adamantBountyWalletPK } } var isReadonly: Bool { switch self { - case .adamantBountyWallet, .adamantNewBountyWallet, .adamantIco: return true + case .adamantBountyWallet, .adamantNewBountyWallet, .adamantIco, .adamantWelcomeWallet: return true case .iosSupport, .adamantExchange, .betOnBitcoin, .donate: return false } } @@ -176,13 +180,13 @@ enum AdamantContacts { var isHidden: Bool { switch self { case .adamantBountyWallet, .adamantNewBountyWallet: return true - case .adamantIco, .iosSupport, .adamantExchange, .betOnBitcoin, .donate: return false + case .adamantIco, .iosSupport, .adamantExchange, .betOnBitcoin, .donate, .adamantWelcomeWallet: return false } } var avatar: String { switch self { - case .adamantExchange, .betOnBitcoin, .donate: + case .adamantExchange, .betOnBitcoin, .donate, .adamantBountyWallet, .adamantNewBountyWallet: return "" default: return "avatar_bots" @@ -194,7 +198,9 @@ enum AdamantContacts { case .adamantBountyWallet, .adamantNewBountyWallet: return ["chats.welcome_message": SystemMessage(message: AdamantMessage.markdownText(NSLocalizedString("Chats.WelcomeMessage", comment: "Known contacts: Adamant welcome message. Markdown supported.")), silentNotification: true)] - + case .adamantWelcomeWallet: + return ["chats.welcome_message": SystemMessage(message: AdamantMessage.markdownText(NSLocalizedString("Chats.WelcomeMessage", comment: "Known contacts: Adamant welcome message. Markdown supported.")), + silentNotification: true)] case .adamantIco: return [ "chats.preico_message": SystemMessage(message: AdamantMessage.text(NSLocalizedString("Chats.PreIcoMessage", comment: "Known contacts: Adamant pre ICO message")), diff --git a/Adamant/Services/AdamantReachability.swift b/Adamant/Services/AdamantReachability.swift index a7a5b3e1d..5f1ec511a 100644 --- a/Adamant/Services/AdamantReachability.swift +++ b/Adamant/Services/AdamantReachability.swift @@ -8,6 +8,7 @@ import Foundation import Reachability +import Network // MAKR: - Convinients extension Reachability.Connection { @@ -27,51 +28,39 @@ extension Reachability.Connection { // MARK: - AdamantReachability wrapper class AdamantReachability: ReachabilityMonitor { - let reachability: Reachability - - private(set) var isActive = false - + let monitorForWifi = NWPathMonitor(requiredInterfaceType: .wifi) + let monitorForCellular = NWPathMonitor(requiredInterfaceType: .cellular) + private var wifiStatus: NWPath.Status = .satisfied + private var cellularStatus: NWPath.Status = .satisfied + var connection: AdamantConnection { - return reachability.connection.adamantConnection + if wifiStatus == .satisfied { return AdamantConnection.wifi } + if cellularStatus == .satisfied { return AdamantConnection.cellular } + return AdamantConnection.none } - init() { - reachability = try! Reachability() // TODO: remove force try and make safe init - reachability.whenReachable = { [weak self] reachability in - let userInfo: [String:Any] = [AdamantUserInfoKey.ReachabilityMonitor.connection:reachability.connection.adamantConnection] + func start() { + monitorForWifi.pathUpdateHandler = { [weak self] path in + self?.wifiStatus = path.status + let status = path.status == .satisfied ? AdamantConnection.wifi : AdamantConnection.none + let userInfo: [String:Any] = [AdamantUserInfoKey.ReachabilityMonitor.connection: status] NotificationCenter.default.post(name: Notification.Name.AdamantReachabilityMonitor.reachabilityChanged, object: self, userInfo: userInfo) } - - reachability.whenUnreachable = { [weak self] reachability in - let userInfo: [String:Any] = [AdamantUserInfoKey.ReachabilityMonitor.connection:reachability.connection.adamantConnection] + monitorForCellular.pathUpdateHandler = { [weak self] path in + self?.cellularStatus = path.status + let status = path.status == .satisfied ? AdamantConnection.cellular : AdamantConnection.none + let userInfo: [String:Any] = [AdamantUserInfoKey.ReachabilityMonitor.connection: status] NotificationCenter.default.post(name: Notification.Name.AdamantReachabilityMonitor.reachabilityChanged, object: self, userInfo: userInfo) } - } - - deinit { - stop() - } - func start() { - guard !isActive else { - return - } - - do { - try reachability.startNotifier() - isActive = true - } catch { - isActive = false - } + let queue = DispatchQueue(label: "NetworkMonitor") + monitorForCellular.start(queue: queue) + monitorForWifi.start(queue: queue) } func stop() { - guard isActive else { - return - } - - NotificationCenter.default.removeObserver(self) - reachability.stopNotifier() - isActive = false + monitorForWifi.cancel() + monitorForCellular.cancel() } + } diff --git a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift index 8c1e2d908..a9638ce76 100644 --- a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift @@ -52,6 +52,7 @@ class AdamantAccountsProvider: AccountsProvider { init() { let ico = KnownContact(contact: AdamantContacts.adamantIco) let bounty = KnownContact(contact: AdamantContacts.adamantBountyWallet) + let welcome = KnownContact(contact: AdamantContacts.adamantWelcomeWallet) let newBounty = KnownContact(contact: AdamantContacts.adamantNewBountyWallet) let iosSupport = KnownContact(contact: AdamantContacts.iosSupport) @@ -74,7 +75,10 @@ class AdamantAccountsProvider: AccountsProvider { AdamantContacts.betOnBitcoin.address: betOnBitcoin, AdamantContacts.betOnBitcoin.name: betOnBitcoin, - AdamantContacts.donate.address: donate + AdamantContacts.donate.address: donate, + + AdamantContacts.adamantWelcomeWallet.address: welcome, + AdamantContacts.adamantWelcomeWallet.name: welcome, ] NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAddressBookService.addressBookUpdated, object: nil, queue: nil) { [weak self] notification in diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 8fe51839e..881da28ac 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -41,6 +41,9 @@ class AdamantChatsProvider: ChatsProvider { public var isChatLoaded: [String : Bool] = [:] public var chatMaxMessages: [String : Int] = [:] public var chatLoadedMessages: [String : Int] = [:] + private var connection: AdamantConnection? + private var isConnectedToTheInthernet = true + private var isRestoredConnectionToTheInthernet: (() ->())? private(set) var isInitiallySynced: Bool = false { didSet { @@ -133,6 +136,28 @@ class AdamantChatsProvider: ChatsProvider { self?.previousAppState = .background } } + + NotificationCenter.default.addObserver( + forName: Notification.Name.AdamantReachabilityMonitor.reachabilityChanged, + object: nil, + queue: nil + ) { [weak self] notification in + guard let connection = notification + .userInfo?[AdamantUserInfoKey.ReachabilityMonitor.connection] as? AdamantConnection + else { + return + } + self?.connection = connection + switch self?.connection { + case .some(.cellular), .some(.wifi): + if self?.isConnectedToTheInthernet == false { + self?.isRestoredConnectionToTheInthernet?() + } + self?.isConnectedToTheInthernet = true + case nil, .some(.none): + self?.isConnectedToTheInthernet = false + } + } } deinit { @@ -225,43 +250,71 @@ extension AdamantChatsProvider { // MARK: 3. Get transactions let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateContext.parent = self.stack.container.viewContext - apiService.getChatRooms(address: address, offset: offset) { [weak self] result in - switch result { - case .success(let chatrooms): - self?.roomsMaxCount = chatrooms.count - if let roomsLoadedCount = self?.roomsLoadedCount { - self?.roomsLoadedCount = roomsLoadedCount + (chatrooms.chats?.count ?? 0) - } else { - self?.roomsLoadedCount = chatrooms.chats?.count ?? 0 + + apiGetChatrooms(address: address, offset: offset) { [weak self] chatrooms in + guard let chatrooms = chatrooms else { + if let synced = self?.isInitiallySynced, !synced { + self?.isInitiallySynced = true } - var array = Array() - chatrooms.chats?.forEach({ room in - if let last = room.lastTransaction { - array.append(last) - } - }) - - self?.processingQueue.async { - self?.process(messageTransactions: array, - senderId: address, - privateKey: privateKey, - context: privateContext, - contextMutatingSemaphore: cms, - completion: { - if let synced = self?.isInitiallySynced, !synced { - self?.isInitiallySynced = true - } - self?.setState(.upToDate, previous: prevState) - completion?() - }) + self?.setState(.upToDate, previous: prevState) + completion?() + return + } + + self?.roomsMaxCount = chatrooms.count + + if let roomsLoadedCount = self?.roomsLoadedCount { + self?.roomsLoadedCount = roomsLoadedCount + (chatrooms.chats?.count ?? 0) + } else { + self?.roomsLoadedCount = chatrooms.chats?.count ?? 0 + } + + var array = Array() + chatrooms.chats?.forEach({ room in + if let last = room.lastTransaction { + array.append(last) + } + }) + + self?.processingQueue.async { + self?.process(messageTransactions: array, + senderId: address, + privateKey: privateKey, + context: privateContext, + contextMutatingSemaphore: cms, + completion: { if let synced = self?.isInitiallySynced, !synced { self?.isInitiallySynced = true } self?.setState(.upToDate, previous: prevState) completion?() + }) + } + } + } + + func apiGetChatrooms(address: String, offset: Int?, completion: ((ChatRooms?) ->())?) { + apiService.getChatRooms(address: address, offset: offset) { [weak self] result in + switch result { + case .success(let chatrooms): + completion?(chatrooms) + case .failure(let error): + switch error { + case .networkError: + if self?.isConnectedToTheInthernet ?? false { + self?.apiGetChatrooms(address: address, offset: offset) { result in + completion?(result) + } + } else { + self?.isRestoredConnectionToTheInthernet = { + self?.apiGetChatrooms(address: address, offset: offset) { result in + completion?(result) + } + } + } + default: + completion?(nil) } - case .failure(_): - completion?() } } } @@ -277,29 +330,52 @@ extension AdamantChatsProvider { // MARK: 3. Get transactions let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateContext.parent = self.stack.container.viewContext - + + apiGetChatMessages(address: address, addressRecipient: addressRecipient, offset: offset) { [weak self] chatroom in + self?.processingQueue.async { + self?.isChatLoaded[addressRecipient] = true + self?.chatMaxMessages[addressRecipient] = chatroom?.count ?? 0 + let loadedCount = self?.chatLoadedMessages[addressRecipient] ?? 0 + self?.chatLoadedMessages[addressRecipient] = loadedCount + (chatroom?.messages?.count ?? 0) + guard let transactions = chatroom?.messages, + transactions.count > 0 else { + completion?(0) + return + } + self?.process(messageTransactions: transactions, + senderId: address, + privateKey: privateKey, + context: privateContext, + contextMutatingSemaphore: cms, + completion: { + completion?(transactions.count) + }) + } + } + } + + func apiGetChatMessages(address: String, addressRecipient: String, offset: Int?, completion: ((ChatRooms?) ->())?) { apiService.getChatMessages(address: address, addressRecipient: addressRecipient, offset: offset) { [weak self] result in switch result { case .success(let chatroom): - let transactions = chatroom.messages - if let transactions = transactions { - self?.isChatLoaded[addressRecipient] = true - self?.chatMaxMessages[addressRecipient] = chatroom.count ?? 0 - let loadedCount = self?.chatLoadedMessages[addressRecipient] ?? 0 - self?.chatLoadedMessages[addressRecipient] = loadedCount + (chatroom.messages?.count ?? 0) - self?.processingQueue.async { - self?.process(messageTransactions: transactions, - senderId: address, - privateKey: privateKey, - context: privateContext, - contextMutatingSemaphore: cms, - completion: { - completion?(transactions.count) - }) + completion?(chatroom) + case .failure(let error): + switch error { + case .networkError: + if self?.isConnectedToTheInthernet ?? false { + self?.apiGetChatMessages(address: address, addressRecipient: addressRecipient, offset: offset) { result in + completion?(result) + } + } else { + self?.isRestoredConnectionToTheInthernet = { + self?.apiGetChatMessages(address: address, addressRecipient: addressRecipient, offset: offset) { result in + completion?(result) + } + } } + default: + completion?(nil) } - case .failure(_): - completion?(0) } } } diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index adf5bee19..db92d4f51 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -624,7 +624,22 @@ extension AdamantTransfersProvider { } } - + /// Search transaction in local storage + /// + /// - Parameter id: Transacton ID, context: NSManagedObjectContext + /// - Returns: Transaction, if found + func getTransfer(id: String, context: NSManagedObjectContext) -> TransferTransaction? { + let request = NSFetchRequest(entityName: TransferTransaction.entityName) + request.predicate = NSPredicate(format: "transactionId == %@", String(id)) + request.fetchLimit = 1 + + do { + let result = try context.fetch(request) + return result.first + } catch { + return nil + } + } /// Call Server, check if transaction updated /// /// - Parameters: @@ -873,8 +888,13 @@ extension AdamantTransfersProvider { } else { unconfirmedsSemaphore.signal() } - print("transfer=", String(t.id)) - let transfer = TransferTransaction(context: context) + let transfer: TransferTransaction + if let trs = getTransfer(id: String(t.id), context: context) { + transfer = trs + } else { + transfer = TransferTransaction(context: context) + } + // let transfer = TransferTransaction(context: context) transfer.amount = t.amount as NSDecimalNumber transfer.date = t.date as NSDate transfer.fee = t.fee as NSDecimalNumber diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index 933b220ee..b5f458554 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -505,7 +505,6 @@ extension ChatListViewController { } else { cell.accountLabel.text = partner.address } - if let avatarName = partner.avatar, let avatar = UIImage.init(named: avatarName) { cell.avatarImage = avatar cell.avatarImageView.tintColor = UIColor.adamant.primary diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 4f5f5a59f..5712c8d70 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -157,6 +157,8 @@ class ChatViewController: MessagesViewController { return richMessageStatusUpdating.contains(id) } + private let averageVisibleCount = 8 + // MARK: Fee label private var feeIsVisible: Bool = false private var feeTimer: Timer? @@ -446,7 +448,7 @@ class ChatViewController: MessagesViewController { return } - if address == AdamantContacts.adamantBountyWallet.name { + if address == AdamantContacts.adamantWelcomeWallet.name { setBusyIndicator(state: false) return } @@ -460,7 +462,9 @@ class ChatViewController: MessagesViewController { if #available(iOS 13.0, *) { } else { - self?.messagesCollectionView.scrollToItem(at: IndexPath(row: 0, section: count - 1), at: .top, animated: false) + if count > 0 { + self?.messagesCollectionView.scrollToItem(at: IndexPath(row: 0, section: count - 1), at: .top, animated: false) + } } self?.setBusyIndicator(state: false) } @@ -586,6 +590,12 @@ class ChatViewController: MessagesViewController { isFirstLayout = false self.chatsProvider.chatPositon.removeValue(forKey: address) self.messageToShow = nil + + didLoaded = true + if indexPath.row >= 0 && indexPath.row <= averageVisibleCount { + self.loadMooreMessagesIfNeeded(indexPath: IndexPath(row: 0, section: 2)) + self.reloadTopScetionIfNeeded() + } return } } @@ -1104,9 +1114,16 @@ extension ChatViewController { reloadTopScetionIfNeeded() } - messageInputBar.sendButton.isEnabled = true - messageInputBar.inputTextView.isEditable = true - messageInputBar.leftStackView.isUserInteractionEnabled = true + if chatroom?.isReadonly ?? false { + messageInputBar.inputTextView.backgroundColor = UIColor.adamant.chatSenderBackground + messageInputBar.inputTextView.isEditable = false + messageInputBar.sendButton.isEnabled = false + attachmentButton.isEnabled = false + } else { + messageInputBar.sendButton.isEnabled = true + messageInputBar.inputTextView.isEditable = true + messageInputBar.leftStackView.isUserInteractionEnabled = true + } UIView.animate(withDuration: 0.25, delay: 0.25) { [weak self] in self?.busyBackgroundView?.backgroundColor = .clear @@ -1122,12 +1139,17 @@ extension ChatViewController { //MARK: Load moore message extension ChatViewController { func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - if indexPath.section < 2, + loadMooreMessagesIfNeeded(indexPath: indexPath) + } + + func loadMooreMessagesIfNeeded(indexPath: IndexPath) { + if indexPath.section < 4, let address = chatroom?.partner?.address, !isBusy, isNeedToLoadMoore(), didLoaded { - if address == AdamantContacts.adamantBountyWallet.name { return } + if address == AdamantContacts.adamantWelcomeWallet.name { return } + print("loadMooreMessagesIfNeeded") isBusy = true let offset = chatsProvider.chatLoadedMessages[address] ?? 0 chatsProvider.getChatMessages(with: address, offset: offset) { [weak self] _count in diff --git a/Adamant/Wallets/WalletViewControllerBase.swift b/Adamant/Wallets/WalletViewControllerBase.swift index b76441880..9cf1b6b1a 100644 --- a/Adamant/Wallets/WalletViewControllerBase.swift +++ b/Adamant/Wallets/WalletViewControllerBase.swift @@ -107,7 +107,13 @@ class WalletViewControllerBase: FormViewController, WalletViewController { let height = $0.value?.fiat != nil ? BalanceTableViewCell.fullHeight : BalanceTableViewCell.compactHeight $0.cell.height = { height } - }.cellUpdate { (cell, row) in + }.cellUpdate { [weak self] (cell, row) in + let symbol = self?.service?.tokenSymbol ?? "" + if let service = self?.service, let wallet = service.wallet { + row.value = self?.balanceRowValueFor(balance: wallet.balance, symbol: symbol, alert: wallet.notifications) + } else { + row.value = self?.balanceRowValueFor(balance: 0, symbol: symbol, alert: 0) + } let height = row.value?.fiat != nil ? BalanceTableViewCell.fullHeight : BalanceTableViewCell.compactHeight cell.height = { height } diff --git a/AdamantShared/AdamantResources.swift b/AdamantShared/AdamantResources.swift index 3c8a0f16f..3cde355d5 100644 --- a/AdamantShared/AdamantResources.swift +++ b/AdamantShared/AdamantResources.swift @@ -55,6 +55,8 @@ struct AdamantResources { // MARK: Contacts struct contacts { + static let adamantWelcomeWallet = "U00000000000000000001" + static let adamantBountyWallet = "U15423595369615486571" static let adamantBountyWalletPK = "cdab95b082b9774bd975677c868261618c7ce7bea97d02e0f56d483e30c077b6" From e544f6c91649efdca2cbcb606c9fa52506e46e6f Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 1 Jul 2022 11:42:19 +0300 Subject: [PATCH 07/12] Fix mac os bugs & push notification --- Adamant.xcodeproj/project.pbxproj | 49 ++++++++++++++----- .../xcschemes/Adamant.Release.xcscheme | 2 +- Adamant/AppDelegate.swift | 46 ++++++++--------- Adamant/Debug.entitlements | 8 +++ Adamant/Release.entitlements | 8 +++ .../Services/AdamantCurrencyInfoService.swift | 25 ++++++++++ .../DataProviders/AdamantChatsProvider.swift | 1 - .../AdamantTransfersProvider.swift | 42 ++++++++-------- .../Stories/Chats/ChatViewController.swift | 49 ++++++++++++++++++- .../Debug.entitlements | 4 ++ .../Release.entitlements | 4 ++ .../Debug.entitlements | 4 ++ .../Release.entitlements | 4 ++ .../Debug.entitlements | 4 ++ .../Release.entitlements | 4 ++ 15 files changed, 194 insertions(+), 60 deletions(-) diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 09d77b8b7..2292a7b44 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -175,7 +175,7 @@ E9079A7D229DEF9C0022CA0D /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E957E112229B04280019732A /* UserNotificationsUI.framework */; }; E9079A80229DEF9C0022CA0D /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9079A7F229DEF9C0022CA0D /* NotificationViewController.swift */; }; E9079A83229DEF9C0022CA0D /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9079A81229DEF9C0022CA0D /* MainInterface.storyboard */; }; - E9079A87229DEF9C0022CA0D /* MessageNotificationContentExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E9079A7B229DEF9C0022CA0D /* MessageNotificationContentExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E9079A87229DEF9C0022CA0D /* MessageNotificationContentExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E9079A7B229DEF9C0022CA0D /* MessageNotificationContentExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E9079A8C229DF19A0022CA0D /* AdamantAvatarService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648BCA6A213D37A800875EB5 /* AdamantAvatarService.swift */; }; E9079A8D229DF19A0022CA0D /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E905D39A2048A9BD00DDB504 /* KeychainStore.swift */; }; E9079A8E229DF1A40022CA0D /* NativeAdamantCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96D64D52295C9CA00CA5587 /* NativeAdamantCore.swift */; }; @@ -325,7 +325,7 @@ E957E12F229B10F80019732A /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E957E112229B04280019732A /* UserNotificationsUI.framework */; }; E957E132229B10F80019732A /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E957E131229B10F80019732A /* NotificationViewController.swift */; }; E957E135229B10F80019732A /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E957E133229B10F80019732A /* MainInterface.storyboard */; }; - E957E139229B10F80019732A /* TransferNotificationContentExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E957E12D229B10F80019732A /* TransferNotificationContentExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E957E139229B10F80019732A /* TransferNotificationContentExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E957E12D229B10F80019732A /* TransferNotificationContentExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E957E13E229BFEB60019732A /* AdamantNotificationKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = E957E123229B0C330019732A /* AdamantNotificationKeys.swift */; }; E957E13F229BFEBB0019732A /* AdamantNotificationKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = E957E123229B0C330019732A /* AdamantNotificationKeys.swift */; }; E957E140229BFF210019732A /* AdamantBalanceFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E940086F2114EA6800CD2D67 /* AdamantBalanceFormat.swift */; }; @@ -411,7 +411,7 @@ E96D64D22295C82B00CA5587 /* TransactionSend.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85B9200A4DC90070534A /* TransactionSend.json */; }; E96D64D62295C9CB00CA5587 /* NativeAdamantCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96D64D52295C9CA00CA5587 /* NativeAdamantCore.swift */; }; E96D64DE2295CD4700CA5587 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96D64DD2295CD4700CA5587 /* NotificationService.swift */; }; - E96D64E22295CD4700CA5587 /* NotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E96D64DB2295CD4700CA5587 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E96D64E22295CD4700CA5587 /* NotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E96D64DB2295CD4700CA5587 /* NotificationServiceExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E96D64E72295CF4600CA5587 /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E905D39A2048A9BD00DDB504 /* KeychainStore.swift */; }; E96E86B821679C120061F80A /* EthTransactionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96E86B721679C120061F80A /* EthTransactionDetailsViewController.swift */; }; E971591A21681D6900A5F904 /* TransactionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E971591921681D6900A5F904 /* TransactionStatus.swift */; }; @@ -2830,16 +2830,19 @@ /* Begin PBXTargetDependency section */ E9079A86229DEF9C0022CA0D /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilter = ios; target = E9079A7A229DEF9C0022CA0D /* MessageNotificationContentExtension */; targetProxy = E9079A85229DEF9C0022CA0D /* PBXContainerItemProxy */; }; E957E138229B10F80019732A /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilter = ios; target = E957E12C229B10F80019732A /* TransferNotificationContentExtension */; targetProxy = E957E137229B10F80019732A /* PBXContainerItemProxy */; }; E96D64E12295CD4700CA5587 /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilter = ios; target = E96D64DA2295CD4700CA5587 /* NotificationServiceExtension */; targetProxy = E96D64E02295CD4700CA5587 /* PBXContainerItemProxy */; }; @@ -2916,8 +2919,9 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -2932,7 +2936,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev.MessageNotificationContentExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger Dev ext MessageNotification"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger Dev ext MessageNotification Mac"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -2945,8 +2951,9 @@ CODE_SIGN_ENTITLEMENTS = MessageNotificationContentExtension/Release.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -2960,7 +2967,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger.MessageNotificationContentExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger ext MessageNotification"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger ext MessageNotification Mac"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -3092,8 +3101,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Debug; CODE_SIGN_ENTITLEMENTS = Adamant/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3108,6 +3118,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "e4233bbf-3705-44fe-95b0-e77475672c60"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger Dev"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger Dev Mac"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3121,8 +3133,9 @@ CODE_SIGN_ENTITLEMENTS = Adamant/Release.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3137,6 +3150,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "bedd1b75-2f23-4a85-a0b2-14c424fcff42"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger Mac"; + SUPPORTS_MACCATALYST = NO; SWIFT_COMPILATION_MODE = singlefile; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3149,8 +3164,9 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3165,7 +3181,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev.TransferNotificationContentExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger Dev ext TransferNotification"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger Dev ext TransferNotification Mac"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3178,8 +3196,9 @@ CODE_SIGN_ENTITLEMENTS = TransferNotificationContentExtension/Release.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3193,7 +3212,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger.TransferNotificationContentExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger dist ext TransferNotification NE"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger dist ext TransferNotification Ma"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -3207,8 +3228,9 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Debug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3223,7 +3245,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev.NotificationServiceExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger Dev ext NotificationService"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger Dev ext NotificationService Maco"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3236,8 +3260,9 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/Release.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 190; + CURRENT_PROJECT_VERSION = 194; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3251,7 +3276,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger.NotificationServiceExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger ext NotificationService"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "ADAMANT Messenger ext NotificationService Mac"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; diff --git a/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Release.xcscheme b/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Release.xcscheme index 1dfe61d5f..bc3808e31 100644 --- a/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Release.xcscheme +++ b/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Release.xcscheme @@ -53,7 +53,7 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - launchStyle = "0" + launchStyle = "1" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index 25f21b037..ffb72e0e4 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -346,53 +346,56 @@ extension AppDelegate: UNUserNotificationCenterDelegate { //MARK: Open Chat From Notification func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if let transactionID = userInfo[AdamantNotificationUserInfoKeys.transactionId] as? String, - let recipientAddress = userInfo[AdamantNotificationUserInfoKeys.pushRecipient] as? String - { + let transactionRaw = userInfo[AdamantNotificationUserInfoKeys.transaction] as? String, + let data = transactionRaw.data(using: .utf8), + let trs = try? JSONDecoder().decode(Transaction.self, from: data) { if application.applicationState != .background && application.applicationState != .inactive { completionHandler(.noData) return } + let senderAddress = trs.senderId + if let tabbar = window?.rootViewController as? UITabBarController, let chats = tabbar.viewControllers?.first as? UISplitViewController, let chatList = chats.viewControllers.first as? UINavigationController, let list = chatList.viewControllers.first as? ChatListViewController { - switch list.accountService.state { - case .loggedIn: - self.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, recipientAddress: recipientAddress) - case .notLogged: - break - case .isLoggingIn: - break - case .updating: - break + if case .loggedIn = list.accountService.state { + self.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, senderAddress: senderAddress) } // if not logged in list.didLoadedMessages = { [weak self] in - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - self?.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, recipientAddress: recipientAddress) + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self?.dialogService.dismissProgress() + self?.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, senderAddress: senderAddress) } } } completionHandler(.newData) - } else { + } else { // for tests + if let jsonData = try? JSONSerialization.data(withJSONObject: userInfo, options: []), + let decoded = String(data: jsonData, encoding: .utf8) { + dialogService.showWarning(withMessage: decoded) + } else if let _ = userInfo[AdamantNotificationUserInfoKeys.pushRecipient] as? String { + dialogService.showWarning(withMessage: userInfo.description) + } completionHandler(.noData) } } - func openDialog(chatList: UINavigationController, tabbar: UITabBarController, list: ChatListViewController, transactionID: String, recipientAddress: String) { + func openDialog(chatList: UINavigationController, tabbar: UITabBarController, list: ChatListViewController, transactionID: String, senderAddress: String) { if let chatVCNav = chatList.viewControllers.last as? UINavigationController, let chatVC = chatVCNav.viewControllers.first as? ChatViewController, - chatVC.chatroom?.partner?.address == recipientAddress { + chatVC.chatroom?.partner?.address == senderAddress { chatVC.scrollDown() return } guard let chatroom = list.chatsController?.fetchedObjects?.first(where: { room in let transactionExist = room.transactions?.first(where: { message in - return (message as? ChatTransaction)?.txId == transactionID + return (message as? ChatTransaction)?.senderAddress == senderAddress }) return transactionExist != nil }) else { return } @@ -591,15 +594,8 @@ extension AppDelegate { let router = container.resolve(Router.self), let list = chatList.viewControllers.first as? ChatListViewController { - switch list.accountService.state { - case .loggedIn: + if case .loggedIn = list.accountService.state { self.openDialog(chatList: chatList, tabbar: tabbar, router: router, list: list, adamantAdr: adamantAdr) - case .notLogged: - break - case .isLoggingIn: - break - case .updating: - break } // if not logged in diff --git a/Adamant/Debug.entitlements b/Adamant/Debug.entitlements index 1cd3dde38..96fd56e77 100644 --- a/Adamant/Debug.entitlements +++ b/Adamant/Debug.entitlements @@ -8,6 +8,14 @@ applinks:msg.adamant.im + com.apple.security.app-sandbox + + com.apple.security.device.camera + + com.apple.security.network.client + + com.apple.security.personal-information.photos-library + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger-dev diff --git a/Adamant/Release.entitlements b/Adamant/Release.entitlements index ca588cb06..de1075df5 100644 --- a/Adamant/Release.entitlements +++ b/Adamant/Release.entitlements @@ -8,6 +8,14 @@ applinks:msg.adamant.im + com.apple.security.app-sandbox + + com.apple.security.device.camera + + com.apple.security.network.client + + com.apple.security.personal-information.photos-library + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger diff --git a/Adamant/Services/AdamantCurrencyInfoService.swift b/Adamant/Services/AdamantCurrencyInfoService.swift index 2d1299294..b6e2972a6 100644 --- a/Adamant/Services/AdamantCurrencyInfoService.swift +++ b/Adamant/Services/AdamantCurrencyInfoService.swift @@ -8,6 +8,7 @@ import Foundation import Alamofire +import UIKit extension StoreKey { struct CoinInfo { @@ -54,6 +55,8 @@ class AdamantCurrencyInfoService: CurrencyInfoService { } } + private var observerActive: NSObjectProtocol? + // MARK: - Dependencies var accountService: AccountService! { didSet { @@ -75,6 +78,28 @@ class AdamantCurrencyInfoService: CurrencyInfoService { } } + // MARK: - Init + init() { + addObservers() + } + + deinit { + removeObservers() + } + + // MARK: - Observers + func addObservers() { + observerActive = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] notification in + self?.update() + } + } + + func removeObservers() { + if let observerActive = observerActive { + NotificationCenter.default.removeObserver(observerActive) + } + } + // MARK: - Info service func update() { guard let coins = rateCoins else { diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 881da28ac..763671e2c 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -1141,7 +1141,6 @@ extension AdamantChatsProvider { print("Failed to get all accounts: Needed keys:\n\(grouppedTransactions.keys.joined(separator: "\n"))\nFounded Addresses: \(partners.keys.compactMap { $0.address }.joined(separator: "\n"))") } - // MARK: 3. Process Chatrooms and Transactions var height: Int64 = 0 let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index db92d4f51..0e52159a3 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -891,25 +891,27 @@ extension AdamantTransfersProvider { let transfer: TransferTransaction if let trs = getTransfer(id: String(t.id), context: context) { transfer = trs + transfer.confirmations = t.confirmations + transfer.statusEnum = .delivered + transfer.blockId = t.blockId } else { transfer = TransferTransaction(context: context) + transfer.amount = t.amount as NSDecimalNumber + transfer.date = t.date as NSDate + transfer.fee = t.fee as NSDecimalNumber + transfer.height = Int64(t.height) + transfer.recipientId = t.recipientId + transfer.senderId = t.senderId + transfer.transactionId = String(t.id) + transfer.type = Int16(t.type.rawValue) + transfer.blockId = t.blockId + transfer.confirmations = t.confirmations + transfer.statusEnum = .delivered + transfer.showsChatroom = false + transfer.isConfirmed = true + transfer.chatMessageId = UUID().uuidString } - // let transfer = TransferTransaction(context: context) - transfer.amount = t.amount as NSDecimalNumber - transfer.date = t.date as NSDate - transfer.fee = t.fee as NSDecimalNumber - transfer.height = Int64(t.height) - transfer.recipientId = t.recipientId - transfer.senderId = t.senderId - transfer.transactionId = String(t.id) - transfer.type = Int16(t.type.rawValue) - transfer.blockId = t.blockId - transfer.confirmations = t.confirmations - transfer.statusEnum = .delivered - transfer.showsChatroom = false - transfer.isConfirmed = true - transfer.chatMessageId = UUID().uuidString - + transfer.isOutgoing = t.senderId == address let partnerId = transfer.isOutgoing ? t.recipientId : t.senderId @@ -962,10 +964,10 @@ extension AdamantTransfersProvider { try context.save() // // MARK: 7. Update lastTransactions -// let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom } -// DispatchQueue.main.async { -// viewContextChatrooms.forEach { $0.updateLastTransaction() } -// } + let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom } + DispatchQueue.main.async { + viewContextChatrooms.forEach { $0.updateLastTransaction() } + } } catch { print("TransferProvider: Failed to save changes to CoreData: \(error.localizedDescription)") } diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 5712c8d70..f16a46100 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -123,6 +123,18 @@ class ChatViewController: MessagesViewController { var feeUpdateTimer: Timer? + private var isMacOS: Bool = { + #if targetEnvironment(macCatalyst) + return true + #else + if #available(iOS 14.0, *) { + return ProcessInfo.processInfo.isiOSAppOnMac + } else { + return false + } + #endif + }() + // MARK: Rich Messages var richMessageProviders = [String:RichMessageProvider]() var cellCalculators = [String:CellSizeCalculator]() @@ -314,8 +326,11 @@ class ChatViewController: MessagesViewController { let keyboardHeight = notification.endFrame.height let tabBarHeight = self?.tabBarController?.tabBar.bounds.height ?? 0 - self?.messagesCollectionView.contentInset.bottom = barHeight + keyboardHeight - self?.messagesCollectionView.scrollIndicatorInsets.bottom = barHeight + keyboardHeight - tabBarHeight + if !(self?.isMacOS ?? false) { + self?.messagesCollectionView.contentInset.bottom = barHeight + keyboardHeight + self?.messagesCollectionView.scrollIndicatorInsets.bottom = barHeight + keyboardHeight - tabBarHeight + } + DispatchQueue.main.async { self?.messagesCollectionView.scrollToBottom(animated: false) } @@ -471,6 +486,8 @@ class ChatViewController: MessagesViewController { } } } + + messageInputBar.inputTextView.autocorrectionType = .no } override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool { @@ -1190,3 +1207,31 @@ extension ChatViewController { amadantLogoImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true } } + +// MARK: Mac OS HotKeys +extension InputTextView { + open override var keyCommands: [UIKeyCommand]? { + let commands = [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(sendKey(sender:))), + UIKeyCommand(input: "\r", modifierFlags: .alternate, action: #selector(newLineKey(sender:))), + UIKeyCommand(input: "\r", modifierFlags: .control, action: #selector(newLineKey(sender:)))] + if #available(iOS 15, *) { + commands.forEach { $0.wantsPriorityOverSystemBehavior = true } + } + return commands + } + + @objc func sendKey(sender: UIKeyCommand) { + print("controlcontrol s") + if sender.modifierFlags == .control || sender.modifierFlags == .alternate { + newLineKey(sender: sender) + } else { + messageInputBar?.didSelectSendButton() + } + } + + @objc func newLineKey(sender: UIKeyCommand) { + print("controlcontrol f") + messageInputBar?.inputTextView.text += "\n" + } + +} diff --git a/MessageNotificationContentExtension/Debug.entitlements b/MessageNotificationContentExtension/Debug.entitlements index f0007976d..6aa188f4e 100644 --- a/MessageNotificationContentExtension/Debug.entitlements +++ b/MessageNotificationContentExtension/Debug.entitlements @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger-dev diff --git a/MessageNotificationContentExtension/Release.entitlements b/MessageNotificationContentExtension/Release.entitlements index 28b4a0f7d..155317021 100644 --- a/MessageNotificationContentExtension/Release.entitlements +++ b/MessageNotificationContentExtension/Release.entitlements @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger diff --git a/NotificationServiceExtension/Debug.entitlements b/NotificationServiceExtension/Debug.entitlements index f0007976d..6aa188f4e 100644 --- a/NotificationServiceExtension/Debug.entitlements +++ b/NotificationServiceExtension/Debug.entitlements @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger-dev diff --git a/NotificationServiceExtension/Release.entitlements b/NotificationServiceExtension/Release.entitlements index 28b4a0f7d..155317021 100644 --- a/NotificationServiceExtension/Release.entitlements +++ b/NotificationServiceExtension/Release.entitlements @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger diff --git a/TransferNotificationContentExtension/Debug.entitlements b/TransferNotificationContentExtension/Debug.entitlements index f0007976d..6aa188f4e 100644 --- a/TransferNotificationContentExtension/Debug.entitlements +++ b/TransferNotificationContentExtension/Debug.entitlements @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger-dev diff --git a/TransferNotificationContentExtension/Release.entitlements b/TransferNotificationContentExtension/Release.entitlements index 28b4a0f7d..155317021 100644 --- a/TransferNotificationContentExtension/Release.entitlements +++ b/TransferNotificationContentExtension/Release.entitlements @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)im.adamant.messenger From 4a61ed797fddb12eed84dcd9574430ded6d86beb Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Wed, 6 Jul 2022 11:43:18 +0300 Subject: [PATCH 08/12] Code refactoring --- Adamant/AppDelegate.swift | 8 +------- Adamant/Services/AdamantReachability.swift | 4 ++-- .../Services/DataProviders/AdamantTransfersProvider.swift | 6 +----- Adamant/Stories/Chats/ChatViewController.swift | 8 ++------ Adamant/Wallets/Adamant/AdmTransferViewController.swift | 2 +- 5 files changed, 7 insertions(+), 21 deletions(-) diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index ffb72e0e4..31908acd3 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -374,13 +374,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { } } completionHandler(.newData) - } else { // for tests - if let jsonData = try? JSONSerialization.data(withJSONObject: userInfo, options: []), - let decoded = String(data: jsonData, encoding: .utf8) { - dialogService.showWarning(withMessage: decoded) - } else if let _ = userInfo[AdamantNotificationUserInfoKeys.pushRecipient] as? String { - dialogService.showWarning(withMessage: userInfo.description) - } + } else { completionHandler(.noData) } } diff --git a/Adamant/Services/AdamantReachability.swift b/Adamant/Services/AdamantReachability.swift index 5f1ec511a..92ddfe1b4 100644 --- a/Adamant/Services/AdamantReachability.swift +++ b/Adamant/Services/AdamantReachability.swift @@ -34,8 +34,8 @@ class AdamantReachability: ReachabilityMonitor { private var cellularStatus: NWPath.Status = .satisfied var connection: AdamantConnection { - if wifiStatus == .satisfied { return AdamantConnection.wifi } - if cellularStatus == .satisfied { return AdamantConnection.cellular } + if wifiStatus == .satisfied { return AdamantConnection.wifi } + if cellularStatus == .satisfied { return AdamantConnection.cellular } return AdamantConnection.none } diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index 0e52159a3..437d05daa 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -917,10 +917,6 @@ extension AdamantTransfersProvider { if let partner = partners[partnerId] { transfer.partner = partner - -// if let chatroom = (partner as? CoreDataAccount)?.chatroom { -// transfer.chatroom = chatroom -// } } if t.height > height { @@ -963,7 +959,7 @@ extension AdamantTransfersProvider { do { try context.save() -// // MARK: 7. Update lastTransactions + // MARK: 7. Update lastTransactions let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom } DispatchQueue.main.async { viewContextChatrooms.forEach { $0.updateLastTransaction() } diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index f16a46100..d05fa6bc1 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -435,6 +435,7 @@ class ChatViewController: MessagesViewController { self.view.addSubview(scrollToBottomBtn) keyboardManager.on(event: .willChangeFrame) { [weak self] (notification) in + if self?.isMacOS ?? false { return } let barHeight = self?.messageInputBar.bounds.height ?? 0 let keyboardHeight = notification.endFrame.height @@ -474,9 +475,7 @@ class ChatViewController: MessagesViewController { DispatchQueue.main.async { self?.messagesCollectionView.reloadDataAndKeepOffset() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - if #available(iOS 13.0, *) { - - } else { + if #unavailable(iOS 13.0) { if count > 0 { self?.messagesCollectionView.scrollToItem(at: IndexPath(row: 0, section: count - 1), at: .top, animated: false) } @@ -628,7 +627,6 @@ class ChatViewController: MessagesViewController { if self.messageToShow == nil { if let offset = self.chatsProvider.chatPositon[address] { self.chatPositionOffset = CGFloat(offset) - print("chatPositionOffset=", offset) self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) + 38 @@ -1221,7 +1219,6 @@ extension InputTextView { } @objc func sendKey(sender: UIKeyCommand) { - print("controlcontrol s") if sender.modifierFlags == .control || sender.modifierFlags == .alternate { newLineKey(sender: sender) } else { @@ -1230,7 +1227,6 @@ extension InputTextView { } @objc func newLineKey(sender: UIKeyCommand) { - print("controlcontrol f") messageInputBar?.inputTextView.text += "\n" } diff --git a/Adamant/Wallets/Adamant/AdmTransferViewController.swift b/Adamant/Wallets/Adamant/AdmTransferViewController.swift index 0e27e794a..90fe70116 100644 --- a/Adamant/Wallets/Adamant/AdmTransferViewController.swift +++ b/Adamant/Wallets/Adamant/AdmTransferViewController.swift @@ -186,7 +186,7 @@ class AdmTransferViewController: TransferViewControllerBase { view.frame = prefix.frame $0.cell.textField.leftView = view $0.cell.textField.leftViewMode = .always - + $0.cell.textField.autocorrectionType = .no if recipientIsReadonly { $0.disabled = true // prefix.isEnabled = false From d626f54ab206e99f7f0ab09ae992561701ca7b99 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 8 Jul 2022 15:39:43 +0300 Subject: [PATCH 09/12] fix mac os toolbar bug & message field --- Adamant.xcodeproj/project.pbxproj | 20 +++++++++++------- Adamant/AppDelegate.swift | 2 +- Adamant/Helpers/MacOSDeterminer.swift | 21 +++++++++++++++++++ .../Stories/Chats/ChatViewController.swift | 16 ++------------ Adamant/Utilities/KeyboardManager.swift | 2 +- .../Dash/DashTransferViewController.swift | 1 + .../Doge/DogeTransferViewController.swift | 1 + .../ERC20/ERC20TransferViewController.swift | 1 + .../Ethereum/EthTransferViewController.swift | 1 + .../Lisk/LskTransferViewController.swift | 1 + 10 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 Adamant/Helpers/MacOSDeterminer.swift diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 2292a7b44..17d8a4481 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 3AE89DD92837F5BF0051D3A9 /* ChatRooms.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */; }; 3AE89DDB2837F5D30051D3A9 /* ChatRoomsChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */; }; 3C06931576393125C61FB8F6 /* Pods_Adamant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33975C0D891698AA7E74EBCC /* Pods_Adamant.framework */; }; + 41935848287841E20083363B /* MacOSDeterminer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41935847287841E20083363B /* MacOSDeterminer.swift */; }; 41E857A42847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; 41E857A52847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; 41E857A62847ADA200BC0783 /* AdamantSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E857A32847ADA200BC0783 /* AdamantSecret.swift */; }; @@ -619,6 +620,7 @@ 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+adamant.swift"; sourceTree = ""; }; 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRooms.swift; sourceTree = ""; }; 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomsChats.swift; sourceTree = ""; }; + 41935847287841E20083363B /* MacOSDeterminer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacOSDeterminer.swift; sourceTree = ""; }; 41E857A32847ADA200BC0783 /* AdamantSecret.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantSecret.swift; sourceTree = ""; }; 4A4D67BD3DC89C07D1351248 /* Pods-AdmCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdmCore.release.xcconfig"; path = "Target Support Files/Pods-AdmCore/Pods-AdmCore.release.xcconfig"; sourceTree = ""; }; 553B1283281C6EE100FFF24C /* GCDUtilites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCDUtilites.swift; sourceTree = ""; }; @@ -1363,6 +1365,7 @@ E98FC34320F920BD00032D65 /* UIFont+adamant.swift */, 645AE06521E67D3300AD3623 /* UITextField+adamant.swift */, 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */, + 41935847287841E20083363B /* MacOSDeterminer.swift */, ); path = Helpers; sourceTree = ""; @@ -2668,6 +2671,7 @@ E90A4943204C5ED6009F6A65 /* EurekaPassphraseRow.swift in Sources */, E90847302196FEA80095825D /* ChatTransaction+CoreDataClass.swift in Sources */, E913C9081FFFA943001A83F7 /* AdamantCore.swift in Sources */, + 41935848287841E20083363B /* MacOSDeterminer.swift in Sources */, E9EC342120052ABB00C0E546 /* TransferViewControllerBase.swift in Sources */, 640EFA962558612400E9724B /* NotificationContent.swift in Sources */, E96D64B42295BED700CA5587 /* StateAsset.swift in Sources */, @@ -2921,7 +2925,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -2953,7 +2957,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3103,7 +3107,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3135,7 +3139,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3166,7 +3170,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3198,7 +3202,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3230,7 +3234,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3262,7 +3266,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 198; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index 31908acd3..d2cbeb8b0 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -403,7 +403,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { var timeout = 0.25 if #available(iOS 13.0, *) { timeout = 0 } DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { - let chat = UINavigationController(rootViewController:vc) + let chat = UINavigationController(rootViewController: vc) split.showDetailViewController(chat, sender: self) } } else { diff --git a/Adamant/Helpers/MacOSDeterminer.swift b/Adamant/Helpers/MacOSDeterminer.swift new file mode 100644 index 000000000..f36dc4ee3 --- /dev/null +++ b/Adamant/Helpers/MacOSDeterminer.swift @@ -0,0 +1,21 @@ +// +// MacOSDeterminer.swift +// Adamant +// +// Created by Stanislav Jelezoglo on 08.07.2022. +// Copyright © 2022 Adamant. All rights reserved. +// + +import Foundation + +var isMacOS: Bool = { + #if targetEnvironment(macCatalyst) + return true + #else + if #available(iOS 14.0, *) { + return ProcessInfo.processInfo.isiOSAppOnMac + } else { + return false + } + #endif +}() diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index d05fa6bc1..72ed8e730 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -123,18 +123,6 @@ class ChatViewController: MessagesViewController { var feeUpdateTimer: Timer? - private var isMacOS: Bool = { - #if targetEnvironment(macCatalyst) - return true - #else - if #available(iOS 14.0, *) { - return ProcessInfo.processInfo.isiOSAppOnMac - } else { - return false - } - #endif - }() - // MARK: Rich Messages var richMessageProviders = [String:RichMessageProvider]() var cellCalculators = [String:CellSizeCalculator]() @@ -326,7 +314,7 @@ class ChatViewController: MessagesViewController { let keyboardHeight = notification.endFrame.height let tabBarHeight = self?.tabBarController?.tabBar.bounds.height ?? 0 - if !(self?.isMacOS ?? false) { + if !isMacOS { self?.messagesCollectionView.contentInset.bottom = barHeight + keyboardHeight self?.messagesCollectionView.scrollIndicatorInsets.bottom = barHeight + keyboardHeight - tabBarHeight } @@ -435,7 +423,7 @@ class ChatViewController: MessagesViewController { self.view.addSubview(scrollToBottomBtn) keyboardManager.on(event: .willChangeFrame) { [weak self] (notification) in - if self?.isMacOS ?? false { return } + if isMacOS { return } let barHeight = self?.messageInputBar.bounds.height ?? 0 let keyboardHeight = notification.endFrame.height diff --git a/Adamant/Utilities/KeyboardManager.swift b/Adamant/Utilities/KeyboardManager.swift index 6850d342d..ed01feb63 100644 --- a/Adamant/Utilities/KeyboardManager.swift +++ b/Adamant/Utilities/KeyboardManager.swift @@ -141,7 +141,7 @@ open class KeyboardManager: NSObject, UIGestureRecognizerDelegate { guard let superview = inputAccessoryView.superview else { fatalError("`inputAccessoryView` must have a superview") } - let tabBarHeight = tabBar?.bounds.size.height ?? 0 + let tabBarHeight = isMacOS ? 0 : tabBar?.bounds.size.height ?? 0 self.inputAccessoryView = inputAccessoryView inputAccessoryView.translatesAutoresizingMaskIntoConstraints = false constraints = NSLayoutConstraintSet( diff --git a/Adamant/Wallets/Dash/DashTransferViewController.swift b/Adamant/Wallets/Dash/DashTransferViewController.swift index 73ccd4142..835e78e4a 100644 --- a/Adamant/Wallets/Dash/DashTransferViewController.swift +++ b/Adamant/Wallets/Dash/DashTransferViewController.swift @@ -181,6 +181,7 @@ class DashTransferViewController: TransferViewControllerBase { let row = TextRow() { $0.tag = BaseRows.address.tag $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder + $0.cell.textField.autocorrectionType = .no if let recipient = recipientAddress { $0.value = recipient diff --git a/Adamant/Wallets/Doge/DogeTransferViewController.swift b/Adamant/Wallets/Doge/DogeTransferViewController.swift index 07402cf68..ffa2a9656 100644 --- a/Adamant/Wallets/Doge/DogeTransferViewController.swift +++ b/Adamant/Wallets/Doge/DogeTransferViewController.swift @@ -167,6 +167,7 @@ class DogeTransferViewController: TransferViewControllerBase { let row = TextRow() { $0.tag = BaseRows.address.tag $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder + $0.cell.textField.autocorrectionType = .no if let recipient = recipientAddress { $0.value = recipient diff --git a/Adamant/Wallets/ERC20/ERC20TransferViewController.swift b/Adamant/Wallets/ERC20/ERC20TransferViewController.swift index 10fee704d..379d66e21 100644 --- a/Adamant/Wallets/ERC20/ERC20TransferViewController.swift +++ b/Adamant/Wallets/ERC20/ERC20TransferViewController.swift @@ -156,6 +156,7 @@ class ERC20TransferViewController: TransferViewControllerBase { $0.tag = BaseRows.address.tag $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder $0.cell.textField.keyboardType = UIKeyboardType.namePhonePad + $0.cell.textField.autocorrectionType = .no if let recipient = recipientAddress { let trimmed = recipient.components(separatedBy: EthTransferViewController.invalidCharacters).joined() diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index 6f985b463..f141f2734 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -151,6 +151,7 @@ class EthTransferViewController: TransferViewControllerBase { $0.tag = BaseRows.address.tag $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder $0.cell.textField.keyboardType = UIKeyboardType.namePhonePad + $0.cell.textField.autocorrectionType = .no if let recipient = recipientAddress { let trimmed = recipient.components(separatedBy: EthTransferViewController.invalidCharacters).joined() diff --git a/Adamant/Wallets/Lisk/LskTransferViewController.swift b/Adamant/Wallets/Lisk/LskTransferViewController.swift index f7b701976..ab669d7fb 100644 --- a/Adamant/Wallets/Lisk/LskTransferViewController.swift +++ b/Adamant/Wallets/Lisk/LskTransferViewController.swift @@ -159,6 +159,7 @@ class LskTransferViewController: TransferViewControllerBase { $0.tag = BaseRows.address.tag $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder $0.cell.textField.keyboardType = UIKeyboardType.alphabet + $0.cell.textField.autocorrectionType = .no if let recipient = recipientAddress { $0.value = recipient From 34300e47f1745516f8107e317fd6d6becf279182 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Fri, 8 Jul 2022 15:50:43 +0300 Subject: [PATCH 10/12] code refactoring --- Adamant/Services/DataProviders/AdamantAccountsProvider.swift | 2 -- Adamant/Stories/Chats/ChatListViewController.swift | 2 +- Adamant/Stories/Chats/ChatViewController.swift | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift index a9638ce76..249e5f87d 100644 --- a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift @@ -231,8 +231,6 @@ extension AdamantAccountsProvider { return } - //let context = stack.container.viewContext - // Go background, to not to hang threads (especially main) on semaphores and dispatch groups queue.async { self.groupsSemaphore.wait() // 1 diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index b5f458554..92a9fcfb1 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -368,7 +368,7 @@ extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { return cellHeight } - func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { // fix jump bug in ios 12 + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return cellHeight } diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 72ed8e730..e13a3a2dc 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -1152,7 +1152,6 @@ extension ChatViewController { isNeedToLoadMoore(), didLoaded { if address == AdamantContacts.adamantWelcomeWallet.name { return } - print("loadMooreMessagesIfNeeded") isBusy = true let offset = chatsProvider.chatLoadedMessages[address] ?? 0 chatsProvider.getChatMessages(with: address, offset: offset) { [weak self] _count in From 6c44c3b4f1cb55a40eadf313cb86a98c6b4ed0f4 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Tue, 12 Jul 2022 19:40:58 +0300 Subject: [PATCH 11/12] Replace pending message animation & code refactoring --- Adamant.xcodeproj/project.pbxproj | 20 +-- Adamant/AppDelegate.swift | 62 ++++---- Adamant/Helpers/UITableView+adamant.swift | 57 ------- .../Chats/ChatListViewController.swift | 92 ++++++------ .../Chats/ChatViewController+MessageKit.swift | 28 +++- .../Stories/Chats/ChatViewController.swift | 140 ++++++++++-------- 6 files changed, 184 insertions(+), 215 deletions(-) delete mode 100644 Adamant/Helpers/UITableView+adamant.swift diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 17d8a4481..f5633c1ba 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ 3AA2D5F7280EADE3000ED971 /* SocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F6280EADE3000ED971 /* SocketService.swift */; }; 3AA2D5FA280EAF5D000ED971 /* AdamantSocketService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */; }; 3AD95B96284121E100767396 /* HeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD95B95284121E100767396 /* HeaderReusableView.swift */; }; - 3AE89DD72837F42D0051D3A9 /* UITableView+adamant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */; }; 3AE89DD92837F5BF0051D3A9 /* ChatRooms.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */; }; 3AE89DDB2837F5D30051D3A9 /* ChatRoomsChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */; }; 3C06931576393125C61FB8F6 /* Pods_Adamant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33975C0D891698AA7E74EBCC /* Pods_Adamant.framework */; }; @@ -617,7 +616,6 @@ 3AA2D5F6280EADE3000ED971 /* SocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketService.swift; sourceTree = ""; }; 3AA2D5F9280EAF5D000ED971 /* AdamantSocketService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantSocketService.swift; sourceTree = ""; }; 3AD95B95284121E100767396 /* HeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderReusableView.swift; sourceTree = ""; }; - 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+adamant.swift"; sourceTree = ""; }; 3AE89DD82837F5BF0051D3A9 /* ChatRooms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRooms.swift; sourceTree = ""; }; 3AE89DDA2837F5D30051D3A9 /* ChatRoomsChats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomsChats.swift; sourceTree = ""; }; 41935847287841E20083363B /* MacOSDeterminer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacOSDeterminer.swift; sourceTree = ""; }; @@ -1364,7 +1362,6 @@ E9256F5E2034C21100DE86E9 /* String+localized.swift */, E98FC34320F920BD00032D65 /* UIFont+adamant.swift */, 645AE06521E67D3300AD3623 /* UITextField+adamant.swift */, - 3AE89DD62837F42D0051D3A9 /* UITableView+adamant.swift */, 41935847287841E20083363B /* MacOSDeterminer.swift */, ); path = Helpers; @@ -2689,7 +2686,6 @@ 64EE46B220FE0C8D00194DDA /* LskTransactionsViewController.swift in Sources */, E94008832114EE4700CD2D67 /* LskWallet.swift in Sources */, 64E1C833222EA0F0006C4DA7 /* DogeWalletViewController.swift in Sources */, - 3AE89DD72837F42D0051D3A9 /* UITableView+adamant.swift in Sources */, E96D64BA2295BED700CA5587 /* TransactionType.swift in Sources */, E93EB09F20DA3FA4001F9601 /* NodesEditorRoutes.swift in Sources */, 6489794F24CE00C000C33A68 /* SwiftyOnboardOverlay.swift in Sources */, @@ -2925,7 +2921,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -2957,7 +2953,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = MessageNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3107,7 +3103,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = ADM.Dev; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -3139,7 +3135,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; DISPLAY_NAME = Adamant; EXCLUDED_SOURCE_FILE_NAMES = Debug.xcassets; @@ -3170,7 +3166,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3202,7 +3198,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = TransferNotificationContentExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3234,7 +3230,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3266,7 +3262,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 198; + CURRENT_PROJECT_VERSION = 202; DEVELOPMENT_TEAM = J2L77FMN46; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index d2cbeb8b0..f22f809e2 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -345,44 +345,42 @@ extension AppDelegate: UNUserNotificationCenterDelegate { //MARK: Open Chat From Notification func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - if let transactionID = userInfo[AdamantNotificationUserInfoKeys.transactionId] as? String, - let transactionRaw = userInfo[AdamantNotificationUserInfoKeys.transaction] as? String, - let data = transactionRaw.data(using: .utf8), - let trs = try? JSONDecoder().decode(Transaction.self, from: data) { - if application.applicationState != .background && application.applicationState != .inactive { - completionHandler(.noData) - return - } - - let senderAddress = trs.senderId - - if let tabbar = window?.rootViewController as? UITabBarController, - let chats = tabbar.viewControllers?.first as? UISplitViewController, - let chatList = chats.viewControllers.first as? UINavigationController, - let list = chatList.viewControllers.first as? ChatListViewController { - - if case .loggedIn = list.accountService.state { - self.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, senderAddress: senderAddress) - } - - // if not logged in - list.didLoadedMessages = { [weak self] in - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - self?.dialogService.dismissProgress() - self?.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, senderAddress: senderAddress) - } - } - } - completionHandler(.newData) - } else { + guard let transactionID = userInfo[AdamantNotificationUserInfoKeys.transactionId] as? String, + let transactionRaw = userInfo[AdamantNotificationUserInfoKeys.transaction] as? String, + let data = transactionRaw.data(using: .utf8), + let trs = try? JSONDecoder().decode(Transaction.self, from: data), + let tabbar = window?.rootViewController as? UITabBarController, + let chats = tabbar.viewControllers?.first as? UISplitViewController, + let chatList = chats.viewControllers.first as? UINavigationController, + let list = chatList.viewControllers.first as? ChatListViewController, + (application.applicationState != .active) + else { completionHandler(.noData) + return } + + if case .loggedIn = list.accountService.state { + self.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, senderAddress: trs.senderId) + } + + // if not logged in + list.didLoadedMessages = { [weak self] in + var timeout = 2.0 + if #available(iOS 13.0, *) { timeout = 0.5 } + DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { + self?.dialogService.dismissProgress() + self?.openDialog(chatList: chatList, tabbar: tabbar, list: list, transactionID: transactionID, senderAddress: trs.senderId) + } + } + + completionHandler(.newData) } func openDialog(chatList: UINavigationController, tabbar: UITabBarController, list: ChatListViewController, transactionID: String, senderAddress: String) { if let chatVCNav = chatList.viewControllers.last as? UINavigationController, let chatVC = chatVCNav.viewControllers.first as? ChatViewController, chatVC.chatroom?.partner?.address == senderAddress { + chatVC.forceScrollToBottom = true chatVC.scrollDown() return } @@ -404,7 +402,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { if #available(iOS 13.0, *) { timeout = 0 } DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { let chat = UINavigationController(rootViewController: vc) - split.showDetailViewController(chat, sender: self) + split.showDetailViewController(chat, sender: list) } } else { chatList.pushViewController(vc, animated: false) @@ -540,7 +538,7 @@ extension AppDelegate { }) } - if let welcome = AdamantContacts.adamantBountyWallet.messages["chats.welcome_message"] { + if let welcome = AdamantContacts.adamantWelcomeWallet.messages["chats.welcome_message"] { chatProvider.fakeReceived(message: welcome.message, senderId: AdamantContacts.adamantWelcomeWallet.name, date: Date.adamantNullDate, diff --git a/Adamant/Helpers/UITableView+adamant.swift b/Adamant/Helpers/UITableView+adamant.swift deleted file mode 100644 index cd939687a..000000000 --- a/Adamant/Helpers/UITableView+adamant.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// UITableView+adamant.swift -// Adamant -// -// Created by Stanislav Jelezoglo on 20.05.2022. -// Copyright © 2022 Adamant. All rights reserved. -// - -import Foundation -import UIKit - -extension UITableView { - func indicatorView() -> UIActivityIndicatorView{ - var activityIndicatorView = UIActivityIndicatorView() - if self.tableFooterView == nil { - let indicatorFrame = CGRect(x: 0, y: 0, width: self.bounds.width, height: 60) - activityIndicatorView = UIActivityIndicatorView(frame: indicatorFrame) - activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin] - - if #available(iOS 13.0, *) { - activityIndicatorView.style = .medium - } else { - activityIndicatorView.style = .gray - } - - activityIndicatorView.color = .lightGray - activityIndicatorView.hidesWhenStopped = true - - self.tableFooterView = activityIndicatorView - return activityIndicatorView - } - else { - return activityIndicatorView - } - } - - func addLoading(_ indexPath:IndexPath, closure: @escaping (() -> Void)){ - if let lastVisibleIndexPath = self.indexPathsForVisibleRows?.last { - if indexPath == lastVisibleIndexPath && indexPath.row == self.numberOfRows(inSection: 0) - 1 { - DispatchQueue.main.async() { - self.indicatorView().startAnimating() - closure() - } - } - } - } - - func stopLoading() { - if self.tableFooterView != nil { - self.indicatorView().stopAnimating() - self.tableFooterView = nil - } - else { - self.tableFooterView = nil - } - } -} diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index 92a9fcfb1..6d56ef73c 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -447,46 +447,21 @@ extension ChatListViewController { } } - if let roomsLoadedCount = chatsProvider.roomsLoadedCount, - let roomsMaxCount = chatsProvider.roomsMaxCount, - roomsLoadedCount < roomsMaxCount, - roomsMaxCount > 0, - !isBusy, - tableView.numberOfRows(inSection: 0) >= roomsLoadedCount, - let lastVisibleIndexPath = tableView.indexPathsForVisibleRows, - lastVisibleIndexPath.contains(IndexPath(row: tableView.numberOfRows(inSection: 0) - 3, section: 0)) { - isBusy = true - lastSystemChatPositionRow = getBottomSystemChatIndex() - DispatchQueue.main.async { [weak self] in - if #available(iOS 11.0, *) { - tableView.performBatchUpdates { - tableView.insertRows(at: [ - IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0) - ], with: .none) - tableView.reloadRows(at: [IndexPath(row: (self?.lastSystemChatPositionRow ?? 0) - 1, section: 0)], with: .none) - } - } else { - tableView.beginUpdates() - tableView.insertRows(at: [IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0)], with: .none) - tableView.reloadRows(at: [IndexPath(row: (self?.lastSystemChatPositionRow ?? 0) - 1, section: 0)], with: .none) - tableView.endUpdates() - } - } - - chatsProvider.getChatRooms(offset: roomsLoadedCount, completion: { [weak self] in - DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { - self?.isBusy = false - if #available(iOS 13.0, *) { - tableView.reloadData() - } else { - let oldTableViewHeight = tableView.contentSize.height; - tableView.reloadData() - let newTableViewHeight = tableView.contentSize.height; - tableView.contentOffset = CGPoint(x: 0, y: newTableViewHeight - oldTableViewHeight); - } - }) - }) + guard let roomsLoadedCount = chatsProvider.roomsLoadedCount, + let roomsMaxCount = chatsProvider.roomsMaxCount, + roomsLoadedCount < roomsMaxCount, + roomsMaxCount > 0, + !isBusy, + tableView.numberOfRows(inSection: 0) >= roomsLoadedCount, + let lastVisibleIndexPath = tableView.indexPathsForVisibleRows, + lastVisibleIndexPath.contains(IndexPath(row: tableView.numberOfRows(inSection: 0) - 3, section: 0)) + else { + return } + + isBusy = true + insertReloadRow() + loadNewChats(offset: roomsLoadedCount) } private func configureCell(_ cell: LoadingTableViewCell) { @@ -543,6 +518,34 @@ extension ChatListViewController { cell.dateLabel.text = nil } } + + private func insertReloadRow() { + lastSystemChatPositionRow = getBottomSystemChatIndex() + DispatchQueue.main.async { [weak self] in + if #available(iOS 11.0, *) { + self?.tableView.performBatchUpdates { + self?.tableView.insertRows(at: [ + IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0) + ], with: .none) + self?.tableView.reloadRows(at: [IndexPath(row: (self?.lastSystemChatPositionRow ?? 0) - 1, section: 0)], with: .none) + } + } else { + self?.tableView.beginUpdates() + self?.tableView.insertRows(at: [IndexPath(row: self?.lastSystemChatPositionRow ?? 0, section: 0)], with: .none) + self?.tableView.reloadRows(at: [IndexPath(row: (self?.lastSystemChatPositionRow ?? 0) - 1, section: 0)], with: .none) + self?.tableView.endUpdates() + } + } + } + + private func loadNewChats(offset: Int) { + chatsProvider.getChatRooms(offset: offset, completion: { [weak self] in + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { + self?.isBusy = false + self?.tableView.reloadData() + }) + }) + } } @@ -984,12 +987,15 @@ extension ChatListViewController { var index = 0 try? chatsController?.performFetch() chatsController?.fetchedObjects?.enumerated().forEach({ (i, room) in - if index == 0, - let date = room.updatedAt as? Date, - date == Date.adamantNullDate { - index = i + guard index == 0, + let date = room.updatedAt as? Date, + date == Date.adamantNullDate + else { + return } + index = i }) + return index } } diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index d41ba2228..c887946e3 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -68,9 +68,8 @@ extension ChatViewController: MessagesDataSource { switch transaction.statusEnum { case .failed: return NSAttributedString(string: String.adamantLocalized.chat.failToSend, attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.foregroundColor: UIColor.adamant.primary]) - case .pending: - return NSAttributedString(string: String.adamantLocalized.chat.pending, attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.foregroundColor: UIColor.adamant.primary]) + return nil case .delivered: return nil @@ -89,6 +88,21 @@ extension ChatViewController: MessagesDataSource { return nil } + if isFromCurrentSender(message: message), + let transaction = message as? ChatTransaction { + if case .pending = transaction.statusEnum { + let attachment = NSTextAttachment() + attachment.image = UIImage(named: "status_pending") + attachment.bounds = CGRect(x: 0, y: -1, width: 7, height: 7) + let attachmentStr = NSAttributedString(attachment: attachment) + + let mutableAttributedString = NSMutableAttributedString() + mutableAttributedString.append(attachmentStr) + + return mutableAttributedString + } + } + let humanizedTime = message.sentDate.humanizedTime() if let expire = humanizedTime.expireIn { @@ -510,12 +524,12 @@ extension ChatViewController: MessagesLayoutDelegate { guard let transaction = message as? ChatTransaction else { return 0 } - + switch transaction.statusEnum { - case .failed, .pending: + case .failed: return 16 - case .delivered: + case .delivered, .pending: return 0 } } else { @@ -534,10 +548,10 @@ extension ChatViewController: MessagesLayoutDelegate { } switch transaction.statusEnum { - case .failed, .pending: + case .failed: return 0 - case .delivered: + case .delivered, .pending: return 16 } } else { diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index e13a3a2dc..37c411afe 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -207,9 +207,7 @@ class ChatViewController: MessagesViewController { //MARK: Background UI private let amadantLogoImageView: UIImageView = { - let iv = UIImageView() - iv.image = UIImage(named: "Adamant-logo") - return iv + return UIImageView(image: UIImage(named: "Adamant-logo")) }() // MARK: - Lifecycle @@ -300,6 +298,8 @@ class ChatViewController: MessagesViewController { $0.setImage(#imageLiteral(resourceName: "Arrow_innactive"), for: UIControl.State.disabled) } + messageInputBar.inputTextView.autocorrectionType = .no + if UIScreen.main.traitCollection.userInterfaceIdiom == .pad { self.edgesForExtendedLayout = UIRectEdge.top @@ -445,36 +445,7 @@ class ChatViewController: MessagesViewController { UIMenuItem(title: String.adamantLocalized.chat.remove, action: NSSelectorFromString("remove:")), UIMenuItem(title: String.adamantLocalized.chat.report, action: NSSelectorFromString("report:"))] - if let address = chatroom.partner?.address { - if let isLoaded = chatsProvider.isChatLoaded[address], - isLoaded { - setBusyIndicator(state: false) - return - } - - if address == AdamantContacts.adamantWelcomeWallet.name { - setBusyIndicator(state: false) - return - } - - setBusyIndicator(state: true) - - chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] count in - DispatchQueue.main.async { - self?.messagesCollectionView.reloadDataAndKeepOffset() - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - if #unavailable(iOS 13.0) { - if count > 0 { - self?.messagesCollectionView.scrollToItem(at: IndexPath(row: 0, section: count - 1), at: .top, animated: false) - } - } - self?.setBusyIndicator(state: false) - } - } - } - } - - messageInputBar.inputTextView.autocorrectionType = .no + loadFirstMessagesIfNeeded() } override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool { @@ -598,7 +569,7 @@ class ChatViewController: MessagesViewController { didLoaded = true if indexPath.row >= 0 && indexPath.row <= averageVisibleCount { self.loadMooreMessagesIfNeeded(indexPath: IndexPath(row: 0, section: 2)) - self.reloadTopScetionIfNeeded() + self.reloadTopSectionIfNeeded() } return } @@ -886,9 +857,15 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { let chat = messagesCollectionView var scrollToBottom = changes.first { $0.type == .insert } != nil + var forceScrollToBottom = false if !isFirstLayout && changes.first?.type != nil { - scrollToBottom = scrollToBottomBtn.isHidden + if self.forceScrollToBottom ?? false { + scrollToBottom = true + self.forceScrollToBottom = false + } else { + scrollToBottom = scrollToBottomBtn.isHidden + } } chat.performBatchUpdates({ @@ -902,6 +879,7 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { chat.insertSections(IndexSet(integer: newIndexPath.row)) if scrollToBottom { chat.scrollToBottom(animated: true) + forceScrollToBottom = true } case .delete: @@ -929,7 +907,7 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { } } }, completion: { animationSuccess in - if scrollToBottom { + if scrollToBottom || forceScrollToBottom { chat.scrollToBottom(animated: animationSuccess) } }) @@ -1091,21 +1069,24 @@ private class StatusUpdateProcedure: Procedure { // MARK: - Busy Indicator View extension ChatViewController { + func setupBusyBackgroundView() { + busyBackgroundView = UIView() + busyBackgroundView?.backgroundColor = UIColor(white: 0, alpha: 0.1) + busyBackgroundView?.frame = view.frame + view.addSubview(busyBackgroundView!) + + spinner.translatesAutoresizingMaskIntoConstraints = false + spinner.startAnimating() + busyBackgroundView?.addSubview(spinner) + spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + } + func setBusyIndicator(state: Bool) { isBusy = state if busyBackgroundView == nil && state { setBackgroundUI() - busyBackgroundView = UIView() - busyBackgroundView?.backgroundColor = UIColor(white: 0, alpha: 0.1) - busyBackgroundView?.frame = view.frame - view.addSubview(busyBackgroundView!) - - spinner.translatesAutoresizingMaskIntoConstraints = false - spinner.startAnimating() - busyBackgroundView?.addSubview(spinner) - spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true - + setupBusyBackgroundView() messagesCollectionView.alpha = 0.0 messageInputBar.sendButton.isEnabled = false messageInputBar.inputTextView.isEditable = false @@ -1114,7 +1095,7 @@ extension ChatViewController { if !state { if busyBackgroundView != nil { - reloadTopScetionIfNeeded() + reloadTopSectionIfNeeded() } if chatroom?.isReadonly ?? false { @@ -1145,26 +1126,58 @@ extension ChatViewController { loadMooreMessagesIfNeeded(indexPath: indexPath) } - func loadMooreMessagesIfNeeded(indexPath: IndexPath) { - if indexPath.section < 4, - let address = chatroom?.partner?.address, - !isBusy, - isNeedToLoadMoore(), - didLoaded { - if address == AdamantContacts.adamantWelcomeWallet.name { return } - isBusy = true - let offset = chatsProvider.chatLoadedMessages[address] ?? 0 - chatsProvider.getChatMessages(with: address, offset: offset) { [weak self] _count in - DispatchQueue.main.async { - self?.messagesCollectionView.reloadDataAndKeepOffset() - self?.isBusy = false - self?.reloadTopScetionIfNeeded() + func loadFirstMessagesIfNeeded() { + guard let address = chatroom?.partner?.address else { return } + + if let isLoaded = chatsProvider.isChatLoaded[address], + isLoaded { + setBusyIndicator(state: false) + return + } + + if address == AdamantContacts.adamantWelcomeWallet.name { + setBusyIndicator(state: false) + return + } + + setBusyIndicator(state: true) + + chatsProvider.getChatMessages(with: address, offset: 0) { [weak self] count in + DispatchQueue.main.async { + self?.messagesCollectionView.reloadDataAndKeepOffset() + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + if count > 0 { + self?.messagesCollectionView.scrollToItem(at: IndexPath(row: 0, section: count - 1), at: .top, animated: false) + } + self?.setBusyIndicator(state: false) } } } } - func reloadTopScetionIfNeeded() { + func loadMooreMessagesIfNeeded(indexPath: IndexPath) { + guard indexPath.section < 4, + let address = chatroom?.partner?.address, + !isBusy, + isNeedToLoadMoore(), + didLoaded + else { + return + } + + if address == AdamantContacts.adamantWelcomeWallet.name { return } + isBusy = true + let offset = chatsProvider.chatLoadedMessages[address] ?? 0 + chatsProvider.getChatMessages(with: address, offset: offset) { [weak self] _count in + DispatchQueue.main.async { + self?.messagesCollectionView.reloadDataAndKeepOffset() + self?.isBusy = false + self?.reloadTopSectionIfNeeded() + } + } + } + + func reloadTopSectionIfNeeded() { try? chatController?.performFetch() if let count = chatController?.fetchedObjects?.count, count >= 1 { @@ -1216,5 +1229,4 @@ extension InputTextView { @objc func newLineKey(sender: UIKeyCommand) { messageInputBar?.inputTextView.text += "\n" } - } From 63f1dc1721b5a84295f51ddd2305c00d2cdad770 Mon Sep 17 00:00:00 2001 From: StanislavDevIOS Date: Wed, 20 Jul 2022 11:25:36 +0300 Subject: [PATCH 12/12] Code refactoring --- Adamant/Stories/Chats/ChatViewController.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 37c411afe..7d676a004 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -123,6 +123,8 @@ class ChatViewController: MessagesViewController { var feeUpdateTimer: Timer? + private let headerHeight: CGFloat = 38 + // MARK: Rich Messages var richMessageProviders = [String:RichMessageProvider]() var cellCalculators = [String:CellSizeCalculator]() @@ -587,7 +589,7 @@ class ChatViewController: MessagesViewController { if let offset = self.chatsProvider.chatPositon[address] { self.chatPositionOffset = CGFloat(offset) self.scrollToBottomBtn.isHidden = chatPositionOffset < chatPositionDelata - let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) + 38 + let collectionViewContentHeight = messagesCollectionView.collectionViewLayout.collectionViewContentSize.height - CGFloat(offset) - (messagesCollectionView.scrollIndicatorInsets.bottom + messagesCollectionView.contentInset.bottom) + headerHeight messagesCollectionView.performBatchUpdates(nil) { _ in self.messagesCollectionView.scrollRectToVisible(CGRect(x: 0.0, y: collectionViewContentHeight - 1.0, width: 1.0, height: 1.0), animated: false) } @@ -1127,7 +1129,7 @@ extension ChatViewController { } func loadFirstMessagesIfNeeded() { - guard let address = chatroom?.partner?.address else { return } + guard let address = chatroom?.partner?.address else { return } if let isLoaded = chatsProvider.isChatLoaded[address], isLoaded {