From 2174c22941e7ea49b17bea46d08687884f70a4ae Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Thu, 8 May 2025 21:45:17 +0300 Subject: [PATCH 01/45] [trello.com/c/dEf1yVQr] Improve overall code and its logic --- Adamant/Services/AdamantAccountService.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index 40d8d03ae..3881d2fc4 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -164,6 +164,10 @@ extension AdamantAccountService { ) isBalanceExpired = true + NotificationCenter.default.post( + name: .AdamantAccountService.walletUpdated, + object: self + ) NotificationCenter.default.post( name: .AdamantAccountService.accountDataUpdated, object: self @@ -227,8 +231,8 @@ extension AdamantAccountService { update(completion, updateOnlyVisible: true) } - func update(shouldUpdateUIBalance: Bool = false, updateOnlyADM: Bool = false, updateOnlyVisible: Bool = true) { - update(nil, updateOnlyVisible: updateOnlyVisible, shouldUpdateUIBalance: true, updateOnlyADM: updateOnlyADM) + func update(shouldUpdateUIBalance: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) { + update(nil, updateOnlyVisible: updateOnlyVisible, shouldUpdateUIBalance: shouldUpdateUIBalance, updateOnlyADM: updateOnlyADM) } func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?, updateOnlyVisible: Bool = true, shouldUpdateUIBalance: Bool = false, updateOnlyADM: Bool = false) { @@ -248,7 +252,9 @@ extension AdamantAccountService { } Task { @Sendable in + print("qwe [\(Date())] " + "Getting account" + "\n----------------------") let result = await apiService.getAccount(byPublicKey: publicKey) + print("qwe [\(Date())] " + "Stopped getting account" + "\n----------------------") switch result { case .success(let account): From 7285d3788d5410a921e39028eabdcfaec886d657 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Mon, 12 May 2025 16:37:20 +0300 Subject: [PATCH 02/45] [trello.com/c/IwfruyRT] Improve logic of reloading rows in wallets view and exclude reordering --- .../VisibleWallets/VisibleWalletsViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift b/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift index 4710bb8e6..b477b9600 100644 --- a/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift +++ b/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift @@ -109,8 +109,8 @@ final class VisibleWalletsViewController: KeyboardObservingViewController { .sink { [weak self] _ in guard let self = self else { return } let indexPath = IndexPath(row: index, section: 0) - if let cell = self.tableView.cellForRow(at: indexPath) as? VisibleWalletsTableViewCell { - cell.balance = wallet.wallet?.balance + UIView.performWithoutAnimation { [weak self] in + self?.tableView.reloadRows(at: [indexPath], with: .none) } } .store(in: &subscriptions) From 8321eca7bbb9975e4bdcdd88131154759ab79f5c Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Tue, 13 May 2025 16:31:12 +0300 Subject: [PATCH 03/45] [trello.com/c/dEf1yVQr] Improve pull-to-refresh logic for updating balances --- .../AccountViewController.swift | 2 +- .../Wallets/Adamant/AdmWalletService.swift | 1 - .../Wallets/Bitcoin/BtcWalletService.swift | 14 +++---- .../Wallets/Dash/DashWalletService.swift | 14 +++---- .../Wallets/Doge/DogeWalletService.swift | 14 +++---- .../Wallets/ERC20/ERC20WalletService.swift | 14 +++---- .../Wallets/Ethereum/EthWalletService.swift | 14 +++---- Adamant/Services/AdamantAccountService.swift | 41 ++++++++++++------- .../CommonKit/Protocols/APICoreProtocol.swift | 2 +- .../ApiService/AdamantApi+Accounts.swift | 2 +- .../ApiService/AdamantApiService.swift | 2 +- 11 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Adamant/Modules/Account/AccountViewController/AccountViewController.swift b/Adamant/Modules/Account/AccountViewController/AccountViewController.swift index a8a6912a8..51c51bb44 100644 --- a/Adamant/Modules/Account/AccountViewController/AccountViewController.swift +++ b/Adamant/Modules/Account/AccountViewController/AccountViewController.swift @@ -1057,7 +1057,7 @@ extension AccountViewController: PagingViewControllerDataSource, PagingViewContr nonisolated func pagingViewController(_: PagingViewController, pagingItemAt index: Int) -> PagingItem { MainActor.assertIsolated() - + return DispatchQueue.onMainThreadSyncSafe { return viewModel.state.wallets[safe: index] ?? AccountWalletCellState.default } diff --git a/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift b/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift index 5821095b7..da3e007c3 100644 --- a/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift +++ b/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift @@ -172,7 +172,6 @@ final class AdmWalletService: NSObject, WalletCoreProtocol, WalletStaticCoreProt func updateWithRefreshUIBalance(){ Task { - admWallet?.isBalanceInitialized = false await walletUpdateSender.send() update() } diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift index a3b274e0a..e6d1e65c0 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift @@ -289,21 +289,19 @@ final class BtcWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @unc guard let wallet = btcWallet else { return } + + if updateWithRefreshUIBalance { + wallet.isBalanceInitialized = false + walletUpdateSender.send() + } switch state { case .notInitiated, .updating, .initiationFailed: return case .upToDate: - break + setState(.updating) } - - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - - setState(.updating) if let balance = try? await getBalance() { if wallet.balance < balance, wallet.isBalanceInitialized { diff --git a/Adamant/Modules/Wallets/Dash/DashWalletService.swift b/Adamant/Modules/Wallets/Dash/DashWalletService.swift index 8b9507546..e56ddff71 100644 --- a/Adamant/Modules/Wallets/Dash/DashWalletService.swift +++ b/Adamant/Modules/Wallets/Dash/DashWalletService.swift @@ -237,21 +237,19 @@ final class DashWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @un guard let wallet = dashWallet else { return } + + if updateWithRefreshUIBalance{ + wallet.isBalanceInitialized = false + walletUpdateSender.send() + } switch state { case .notInitiated, .updating, .initiationFailed: return case .upToDate: - break + setState(.updating) } - - if updateWithRefreshUIBalance{ - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - - setState(.updating) if let balance = try? await getBalance() { if wallet.balance < balance, wallet.isBalanceInitialized { diff --git a/Adamant/Modules/Wallets/Doge/DogeWalletService.swift b/Adamant/Modules/Wallets/Doge/DogeWalletService.swift index 77fba29c2..0aab2880e 100644 --- a/Adamant/Modules/Wallets/Doge/DogeWalletService.swift +++ b/Adamant/Modules/Wallets/Doge/DogeWalletService.swift @@ -257,21 +257,19 @@ final class DogeWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @un guard let wallet = dogeWallet else { return } + + if updateWithRefreshUIBalance { + wallet.isBalanceInitialized = false + walletUpdateSender.send() + } switch state { case .notInitiated, .updating, .initiationFailed: return case .upToDate: - break + setState(.updating) } - - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - - setState(.updating) if let balance = try? await getBalance() { if wallet.balance < balance, wallet.isBalanceInitialized { diff --git a/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift b/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift index 87e52550e..3b3ac4fbc 100644 --- a/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift +++ b/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift @@ -271,6 +271,11 @@ final class ERC20WalletService: WalletCoreProtocol, ERC20GasAlgorithmComputable, func update(updateWithRefreshUIBalance: Bool = false) async { guard let wallet = ethWallet else { return + } + + if updateWithRefreshUIBalance { + wallet.isBalanceInitialized = false + walletUpdateSender.send() } switch state { @@ -278,15 +283,8 @@ final class ERC20WalletService: WalletCoreProtocol, ERC20GasAlgorithmComputable, return case .upToDate: - break - } - - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() + setState(.updating) } - - setState(.updating) if let balance = try? await getBalance(forAddress: wallet.ethAddress) { if wallet.balance < balance, wallet.isBalanceInitialized { diff --git a/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift b/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift index df4900caa..0e4e93717 100644 --- a/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift @@ -303,21 +303,19 @@ final class EthWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, ERC2 guard let wallet = await getWallet() else { return } + + if updateWithRefreshUIBalance { + wallet.isBalanceInitialized = false + walletUpdateSender.send() + } switch state { case .notInitiated, .updating, .initiationFailed: return case .upToDate: - break + setState(.updating) } - - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - - setState(.updating) if let balance = try? await getBalance(forAddress: wallet.ethAddress) { if wallet.balance < balance, wallet.isBalanceInitialized { diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index 3881d2fc4..807f1404f 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -236,6 +236,25 @@ extension AdamantAccountService { } func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?, updateOnlyVisible: Bool = true, shouldUpdateUIBalance: Bool = false, updateOnlyADM: Bool = false) { + if !updateOnlyADM { + let wallets = walletServiceCompose.getWallets() + if shouldUpdateUIBalance{ + isBalanceExpired = true + } + + for wallet in wallets { + if !updateOnlyVisible || !(walletsStoreService?.isInvisible(wallet) ?? false) { + Task{ + if shouldUpdateUIBalance { + wallet.core.updateWithRefreshUIBalance() + } else { + wallet.core.update() + } + } + } + } + } + switch state { case .notLogged, .isLoggingIn, .updating: return @@ -244,6 +263,8 @@ extension AdamantAccountService { break } + let isBalanceExpiredPrev = isBalanceExpired + let prevState = state state = .updating @@ -252,9 +273,7 @@ extension AdamantAccountService { } Task { @Sendable in - print("qwe [\(Date())] " + "Getting account" + "\n----------------------") let result = await apiService.getAccount(byPublicKey: publicKey) - print("qwe [\(Date())] " + "Stopped getting account" + "\n----------------------") switch result { case .success(let account): @@ -280,19 +299,11 @@ extension AdamantAccountService { isBalanceExpired = true state = prevState } - } - - if !updateOnlyADM { - let wallets = walletServiceCompose.getWallets() - - for wallet in wallets { - if !updateOnlyVisible || !(walletsStoreService?.isInvisible(wallet) ?? false) { - if shouldUpdateUIBalance { - wallet.core.updateWithRefreshUIBalance() - } else { - wallet.core.update() - } - } + if isBalanceExpiredPrev != isBalanceExpired, shouldUpdateUIBalance { + NotificationCenter.default.post( + name: .AdamantAccountService.walletUpdated, + object: self + ) } } } diff --git a/CommonKit/Sources/CommonKit/Protocols/APICoreProtocol.swift b/CommonKit/Sources/CommonKit/Protocols/APICoreProtocol.swift index 3a212ac05..58bc5e6d8 100644 --- a/CommonKit/Sources/CommonKit/Protocols/APICoreProtocol.swift +++ b/CommonKit/Sources/CommonKit/Protocols/APICoreProtocol.swift @@ -94,7 +94,7 @@ extension APICoreProtocol { parameters: Parameters, encoding: APIParametersEncoding ) async -> ApiServiceResult { - await sendRequest( + return await sendRequest( origin: origin, path: path, method: method, diff --git a/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApi+Accounts.swift b/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApi+Accounts.swift index ae31bc9b6..bbffbeea1 100644 --- a/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApi+Accounts.swift +++ b/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApi+Accounts.swift @@ -37,7 +37,7 @@ extension AdamantApiService { parameters: ["publicKey": publicKey], encoding: .url ) - + return response.flatMap { $0.resolved() } }) { case let .success(value): diff --git a/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApiService.swift b/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApiService.swift index 59a5bf8eb..a42ec6f17 100644 --- a/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApiService.swift +++ b/CommonKit/Sources/CommonKit/Services/ApiService/AdamantApiService.swift @@ -72,7 +72,7 @@ public final class AdamantApiService: @unchecked Sendable { task.startTask( Task { - await service.request( + return await service.request( waitsForConnectivity: waitsForConnectivity, taskId: taskId, isCancelled: { From f64b0b9327b8707f44f9a2e94fb2498f7affcf15 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Tue, 13 May 2025 18:29:23 +0300 Subject: [PATCH 04/45] [trello.com/c/dEf1yVQr] Remove unessesary update logic --- Adamant/ServiceProtocols/AccountService.swift | 1 - Adamant/Services/AdamantAccountService.swift | 9 ++++----- Adamant/Services/WalletAutoUpdaterService.swift | 5 +++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index 87ecb8086..c541050a7 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -158,7 +158,6 @@ protocol AccountService: AnyObject, Sendable { /// Update logged account info func update() func update(shouldUpdateUIBalance: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) - func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?) /// Login into Adamant using passphrase. func loginWith(passphrase: String, password: String) async throws -> AccountServiceResult diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index 807f1404f..b537d7eb4 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -227,10 +227,6 @@ extension AdamantAccountService { self.update(nil) } - func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?) { - update(completion, updateOnlyVisible: true) - } - func update(shouldUpdateUIBalance: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) { update(nil, updateOnlyVisible: updateOnlyVisible, shouldUpdateUIBalance: shouldUpdateUIBalance, updateOnlyADM: updateOnlyADM) } @@ -264,6 +260,7 @@ extension AdamantAccountService { } let isBalanceExpiredPrev = isBalanceExpired + let balancePrev = account?.balance let prevState = state state = .updating @@ -299,7 +296,9 @@ extension AdamantAccountService { isBalanceExpired = true state = prevState } - if isBalanceExpiredPrev != isBalanceExpired, shouldUpdateUIBalance { + + // Holde case when the expiring change or when the balance change + if (isBalanceExpiredPrev != isBalanceExpired && shouldUpdateUIBalance) || (account?.balance != balancePrev) { NotificationCenter.default.post( name: .AdamantAccountService.walletUpdated, object: self diff --git a/Adamant/Services/WalletAutoUpdaterService.swift b/Adamant/Services/WalletAutoUpdaterService.swift index 92aa9cabf..418dfc80c 100644 --- a/Adamant/Services/WalletAutoUpdaterService.swift +++ b/Adamant/Services/WalletAutoUpdaterService.swift @@ -109,8 +109,9 @@ actor WalletAutoUpdateService { interval: getTimeIntervalFor(wallet: core), queue: .global(qos: .utility), callback: { - if wallet.core is AdmWalletService{ - self.accountService.update() + guard !(wallet.core is AdmWalletService) else { + self.accountService.update(shouldUpdateUIBalance: false, updateOnlyADM: true, updateOnlyVisible: false) + return } core.update() } From 8c7db4712b9de91b64b072d817c8a09768e719df Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 13 May 2025 23:47:37 +0200 Subject: [PATCH 05/45] [trello.com/c/x0o1aqih] remove extra space for media cell with comment --- .../View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift index b2026e66c..86ec47c01 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift @@ -283,7 +283,7 @@ extension ChatMediaContentView.Model { } if !comment.string.isEmpty { - spaceCount += 3 + spaceCount += 2 } return fileModel.height() From 4c3bd38f5a84df18929f01e8c9f7f0ae664f8a8e Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Wed, 14 May 2025 14:34:58 +0300 Subject: [PATCH 06/45] [trello.com/c/dEf1yVQr] Improve overall code and its logic --- .../AccountViewController.swift | 2 +- .../VisibleWalletsViewController.swift | 2 +- .../Wallets/Adamant/AdmWalletService.swift | 28 ++++++++-- .../Wallets/Bitcoin/BtcWalletService.swift | 17 +++--- .../Wallets/Dash/DashWalletService.swift | 17 +++--- .../Wallets/Doge/DogeWalletService.swift | 17 +++--- .../Wallets/ERC20/ERC20WalletService.swift | 17 +++--- .../Wallets/Ethereum/EthWalletService.swift | 17 +++--- .../Klayr/KlyTransferViewController.swift | 2 +- .../WalletService/KlyWalletService.swift | 15 +++-- .../WalletsService/WalletCoreProtocol.swift | 2 +- Adamant/ServiceProtocols/AccountService.swift | 2 +- Adamant/Services/AdamantAccountService.swift | 56 +++++++++++-------- .../Services/WalletAutoUpdaterService.swift | 3 +- 14 files changed, 111 insertions(+), 86 deletions(-) diff --git a/Adamant/Modules/Account/AccountViewController/AccountViewController.swift b/Adamant/Modules/Account/AccountViewController/AccountViewController.swift index 51c51bb44..a19bfb7c4 100644 --- a/Adamant/Modules/Account/AccountViewController/AccountViewController.swift +++ b/Adamant/Modules/Account/AccountViewController/AccountViewController.swift @@ -981,7 +981,7 @@ final class AccountViewController: FormViewController { } Task { @MainActor in - accountService.update(shouldUpdateUIBalance: true, updateOnlyADM: false, updateOnlyVisible: true) + accountService.update(resetBalanceAndUpdate: true, updateOnlyADM: false, updateOnlyVisible: true) } refreshControl.endRefreshing() } diff --git a/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift b/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift index f82cf3e34..5f5010866 100644 --- a/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift +++ b/Adamant/Modules/Settings/VisibleWallets/VisibleWalletsViewController.swift @@ -145,7 +145,7 @@ final class VisibleWalletsViewController: KeyboardObservingViewController { } @objc private func updateBalances() { - accountService.update(shouldUpdateUIBalance: true, updateOnlyADM: false, updateOnlyVisible: false) + accountService.update(resetBalanceAndUpdate: true, updateOnlyADM: false, updateOnlyVisible: false) refreshControl.endRefreshing() } diff --git a/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift b/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift index da3e007c3..179c93b78 100644 --- a/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift +++ b/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift @@ -170,14 +170,26 @@ final class AdmWalletService: NSObject, WalletCoreProtocol, WalletStaticCoreProt .store(in: &subscriptions) } - func updateWithRefreshUIBalance(){ + func resetBalanceAndUpdate(){ Task { await walletUpdateSender.send() - update() + await update({ [weak self] in + guard let self = self, let isBalanceExpired = self.accountService?.isBalanceExpired else { + return + } + self.admWallet?.isBalanceInitialized = !isBalanceExpired + }) } } func update() { + Task{ + await update(nil) + } + } + + @MainActor + func update(_ completion: (() -> Void)?) async { guard let accountService = accountService, let account = accountService.account else { admWallet = nil return @@ -196,7 +208,12 @@ final class AdmWalletService: NSObject, WalletCoreProtocol, WalletStaticCoreProt isRaised = false } - admWallet?.isBalanceInitialized = !accountService.isBalanceExpired + let isBalanceInitialized = admWallet?.isBalanceInitialized + if isBalanceInitialized == false, !accountService.isBalanceExpired { + admWallet?.isBalanceInitialized = !accountService.isBalanceExpired + } + + completion?() if wallet != nil { Task { @MainActor in @@ -205,7 +222,10 @@ final class AdmWalletService: NSObject, WalletCoreProtocol, WalletStaticCoreProt } if isRaised { - Task { @MainActor in vibroService.applyVibration(.success) } + Task { + @MainActor in + vibroService.applyVibration(.success) + } } } diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift index e6d1e65c0..5f77f7307 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift @@ -271,29 +271,28 @@ final class BtcWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @unc } .store(in: &subscriptions) } - + func update() { Task { await update() } } - func updateWithRefreshUIBalance(){ + func resetBalanceAndUpdate() { Task { - await update(updateWithRefreshUIBalance: true) + if let wallet = btcWallet { + wallet.isBalanceInitialized = false + await walletUpdateSender.send() + await update() + } } } @MainActor - func update(updateWithRefreshUIBalance: Bool = false) async { + func update() async { guard let wallet = btcWallet else { return } - - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } switch state { case .notInitiated, .updating, .initiationFailed: diff --git a/Adamant/Modules/Wallets/Dash/DashWalletService.swift b/Adamant/Modules/Wallets/Dash/DashWalletService.swift index e56ddff71..2dee1ef21 100644 --- a/Adamant/Modules/Wallets/Dash/DashWalletService.swift +++ b/Adamant/Modules/Wallets/Dash/DashWalletService.swift @@ -226,23 +226,22 @@ final class DashWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @un } } - func updateWithRefreshUIBalance(){ + func resetBalanceAndUpdate() { Task { - await update(updateWithRefreshUIBalance: true) + if let wallet = dashWallet { + wallet.isBalanceInitialized = false + await walletUpdateSender.send() + await update() + } } } - + @MainActor - func update(updateWithRefreshUIBalance: Bool = false) async { + func update() async { guard let wallet = dashWallet else { return } - if updateWithRefreshUIBalance{ - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - switch state { case .notInitiated, .updating, .initiationFailed: return diff --git a/Adamant/Modules/Wallets/Doge/DogeWalletService.swift b/Adamant/Modules/Wallets/Doge/DogeWalletService.swift index 0aab2880e..52c69cdb5 100644 --- a/Adamant/Modules/Wallets/Doge/DogeWalletService.swift +++ b/Adamant/Modules/Wallets/Doge/DogeWalletService.swift @@ -245,24 +245,23 @@ final class DogeWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @un await update() } } - - func updateWithRefreshUIBalance(){ + + func resetBalanceAndUpdate() { Task { - await update(updateWithRefreshUIBalance: true) + if let wallet = dogeWallet { + wallet.isBalanceInitialized = false + await walletUpdateSender.send() + await update() + } } } @MainActor - func update(updateWithRefreshUIBalance: Bool = false) async { + func update() async { guard let wallet = dogeWallet else { return } - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - switch state { case .notInitiated, .updating, .initiationFailed: return diff --git a/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift b/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift index 3b3ac4fbc..9d64c3d16 100644 --- a/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift +++ b/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift @@ -260,24 +260,23 @@ final class ERC20WalletService: WalletCoreProtocol, ERC20GasAlgorithmComputable, await update() } } - - func updateWithRefreshUIBalance() { + + func resetBalanceAndUpdate() { Task { - await update(updateWithRefreshUIBalance: true) + if let wallet = ethWallet { + wallet.isBalanceInitialized = false + await walletUpdateSender.send() + await update() + } } } @MainActor - func update(updateWithRefreshUIBalance: Bool = false) async { + func update() async { guard let wallet = ethWallet else { return } - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - switch state { case .notInitiated, .updating, .initiationFailed: return diff --git a/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift b/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift index 0e4e93717..d25101ad1 100644 --- a/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift @@ -291,24 +291,23 @@ final class EthWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, ERC2 await update() } } - - func updateWithRefreshUIBalance(){ + + func resetBalanceAndUpdate() { Task { - await update(updateWithRefreshUIBalance: true) + if let wallet = ethWallet { + wallet.isBalanceInitialized = false + await walletUpdateSender.send() + await update() + } } } @MainActor - func update(updateWithRefreshUIBalance: Bool = false) async { + func update() async { guard let wallet = await getWallet() else { return } - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - switch state { case .notInitiated, .updating, .initiationFailed: return diff --git a/Adamant/Modules/Wallets/Klayr/KlyTransferViewController.swift b/Adamant/Modules/Wallets/Klayr/KlyTransferViewController.swift index a6b23baaa..a08f7e6b1 100644 --- a/Adamant/Modules/Wallets/Klayr/KlyTransferViewController.swift +++ b/Adamant/Modules/Wallets/Klayr/KlyTransferViewController.swift @@ -125,7 +125,7 @@ final class KlyTransferViewController: TransferViewControllerBase { throw error } - service.update() + walletCore.update() dialogService.dismissProgress() dialogService.showSuccess(withMessage: String.adamant.transfer.transferSuccess) diff --git a/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift b/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift index b4819d044..58aae80de 100644 --- a/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift +++ b/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift @@ -249,14 +249,18 @@ extension KlyWalletService { .store(in: &subscriptions) } - func updateWithRefreshUIBalance(){ + func resetBalanceAndUpdate() { Task { - await update(updateWithRefreshUIBalance: true) + if let wallet = klyWallet { + wallet.isBalanceInitialized = false + await walletUpdateSender.send() + await update() + } } } @MainActor - fileprivate func update(updateWithRefreshUIBalance: Bool = false) async { + func update() async { guard let wallet = klyWallet else { return } @@ -269,11 +273,6 @@ extension KlyWalletService { break } - if updateWithRefreshUIBalance { - wallet.isBalanceInitialized = false - walletUpdateSender.send() - } - setState(.updating) if let balance = try? await getBalance() { diff --git a/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift b/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift index d2ead8e13..584430a59 100644 --- a/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift +++ b/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift @@ -297,7 +297,7 @@ protocol WalletCoreProtocol: AnyObject, Sendable { var walletUpdatePublisher: AnyObservable { get } func update() - func updateWithRefreshUIBalance() + func resetBalanceAndUpdate() // MARK: Tools func validate(address: String) -> AddressValidationResult diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index c541050a7..264e2a37e 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -157,7 +157,7 @@ protocol AccountService: AnyObject, Sendable { /// Update logged account info func update() - func update(shouldUpdateUIBalance: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) + func update(resetBalanceAndUpdate: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) /// Login into Adamant using passphrase. func loginWith(passphrase: String, password: String) async throws -> AccountServiceResult diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index b537d7eb4..497aba721 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -59,6 +59,16 @@ final class AdamantAccountService: AccountService, @unchecked Sendable { self?.update() } + NotificationCenter.default + .notifications(named: .AdamantReachabilityMonitor.reachabilityChanged) + .sink { @MainActor [weak self] data in + let connection = data.userInfo?[AdamantUserInfoKey.ReachabilityMonitor.connection] as? Bool + + guard connection == true else { return } + self?.update(resetBalanceAndUpdate: false, updateOnlyADM: false, updateOnlyVisible: true) + } + .store(in: &subscriptions) + NotificationCenter.default .notifications(named: UIApplication.didBecomeActiveNotification, object: nil) .sink { @MainActor [weak self] _ in @@ -227,22 +237,14 @@ extension AdamantAccountService { self.update(nil) } - func update(shouldUpdateUIBalance: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) { - update(nil, updateOnlyVisible: updateOnlyVisible, shouldUpdateUIBalance: shouldUpdateUIBalance, updateOnlyADM: updateOnlyADM) - } - - func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?, updateOnlyVisible: Bool = true, shouldUpdateUIBalance: Bool = false, updateOnlyADM: Bool = false) { + func update(resetBalanceAndUpdate: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) { + let wallets = walletServiceCompose.getWallets() if !updateOnlyADM { - let wallets = walletServiceCompose.getWallets() - if shouldUpdateUIBalance{ - isBalanceExpired = true - } - - for wallet in wallets { + for wallet in wallets where !(wallet.core is AdmWalletService) { if !updateOnlyVisible || !(walletsStoreService?.isInvisible(wallet) ?? false) { Task{ - if shouldUpdateUIBalance { - wallet.core.updateWithRefreshUIBalance() + if resetBalanceAndUpdate { + wallet.core.resetBalanceAndUpdate() } else { wallet.core.update() } @@ -251,6 +253,17 @@ extension AdamantAccountService { } } + if resetBalanceAndUpdate{ + isBalanceExpired = true + wallets.first(where: {$0.core is AdmWalletService})?.core.resetBalanceAndUpdate() + } + + update({ _ in + wallets.first(where: {$0.core is AdmWalletService})?.core.update() + }) + } + + func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?) { switch state { case .notLogged, .isLoggingIn, .updating: return @@ -259,9 +272,6 @@ extension AdamantAccountService { break } - let isBalanceExpiredPrev = isBalanceExpired - let balancePrev = account?.balance - let prevState = state state = .updating @@ -297,13 +307,13 @@ extension AdamantAccountService { state = prevState } - // Holde case when the expiring change or when the balance change - if (isBalanceExpiredPrev != isBalanceExpired && shouldUpdateUIBalance) || (account?.balance != balancePrev) { - NotificationCenter.default.post( - name: .AdamantAccountService.walletUpdated, - object: self - ) - } + // Holde case when t he expiring change or when the balance change +// if (isBalanceExpiredPrev != isBalanceExpired && resetBalanceAndUpdate) || (account?.balance != balancePrev) { +// NotificationCenter.default.post( +// name: .AdamantAccountService.walletUpdated, +// object: self +// ) +// } } } } diff --git a/Adamant/Services/WalletAutoUpdaterService.swift b/Adamant/Services/WalletAutoUpdaterService.swift index 418dfc80c..cf71adc08 100644 --- a/Adamant/Services/WalletAutoUpdaterService.swift +++ b/Adamant/Services/WalletAutoUpdaterService.swift @@ -109,8 +109,9 @@ actor WalletAutoUpdateService { interval: getTimeIntervalFor(wallet: core), queue: .global(qos: .utility), callback: { + print("qwe [\(Date())] " + "Update \(wallet.core.tokenName)" + "\n----------------------") guard !(wallet.core is AdmWalletService) else { - self.accountService.update(shouldUpdateUIBalance: false, updateOnlyADM: true, updateOnlyVisible: false) + self.accountService.update(resetBalanceAndUpdate: false, updateOnlyADM: true, updateOnlyVisible: false) return } core.update() From 2db0f94fc2d21adafc9dbda55ff6bc72231b65e5 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 14 May 2025 22:43:52 +0200 Subject: [PATCH 07/45] [trello.com/c/7YVA2NTe] update animation and resend error catch --- .../Chat/View/Helpers/FileMessageStatus.swift | 4 +- .../Container/ChatMediaContainerView.swift | 6 +- .../Chat/ViewModel/ChatViewModel.swift | 55 ++++++++++--------- .../Helpers/UIHelpers/UIColor+adamant.swift | 3 + 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift b/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift index ed628239c..9800c81ee 100644 --- a/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift +++ b/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift @@ -19,7 +19,9 @@ enum FileMessageStatus: Hashable { var image: UIImage { switch self { case .uploading: - return UIImage(systemName: "square.fill") ?? UIImage() + let config = UIImage.SymbolConfiguration(pointSize: 15, weight: .regular) + let image = UIImage(systemName: "square.fill", withConfiguration: config) ?? UIImage() + return image case .downloading: return .asset(named: "status_pending") ?? .init() case .success: diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift index b951db691..02b985c8a 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift @@ -185,7 +185,7 @@ extension ChatMediaContainerView { reactionsStack.insertSubview(progressRingHostingView, aboveSubview: statusButton) progressRingHostingView.snp.makeConstraints { make in make.center.equalTo(statusButton) - make.size.equalTo(30) + make.size.equalTo(29) } } @@ -225,14 +225,14 @@ extension ChatMediaContainerView { if averageProgress == 0 { statusProgressState.backgroundGradient = LinearGradient( - gradient: Gradient(colors: [.white, .black]), + gradient: Gradient(colors: [.white, Color(.adamant.sendingFileAnimationGrayColor)]), startPoint: .topLeading, endPoint: .bottomTrailing )} else { statusProgressState.backgroundGradient = nil } statusProgressState.progress = averageProgress / 100 - statusProgressState.hidden = averageProgress == 100 || !(model.status == .uploading) + statusProgressState.hidden = !(model.status == .uploading) statusProgressState.isSpinning = (model.status == .uploading && averageProgress == 0) } diff --git a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift index 8b51fb971..84d52546e 100644 --- a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift +++ b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift @@ -500,37 +500,38 @@ final class ChatViewModel: NSObject { func retrySendMessage(id: String) { Task { - guard let transaction = chatTransactions.first(where: { $0.chatMessageId == id }) + guard let transaction = chatTransactions.first(where: { $0.chatMessageId == id }), + let message = messages.first(where: { $0.messageId == id }) else { return } - - let message = messages.first(where: { $0.messageId == id }) - - if case let .file(model) = message?.content { - try? await chatFileService.resendMessage( - with: id, - text: model.value.content.comment.string, - chatroom: chatroom, - replyMessage: nil, - saveEncrypted: filesStorageProprieties.saveFileEncrypted() - ) - return - } - + do { - try await chatsProvider.retrySendMessage(transaction) - } catch { - switch error as? ChatsProviderError { - case .invalidTransactionStatus: - break - case let .serverError(serverError): - switch serverError { - case .timestampIsInTheFuture: - dialog.send(.timestampIsInTheFuture) - default: dialog.send(.warning(error.localizedDescription)) - } + if case let .file(model) = message.content { + try await chatFileService.resendMessage( + with: id, + text: model.value.content.comment.string, + chatroom: chatroom, + replyMessage: nil, + saveEncrypted: filesStorageProprieties.saveFileEncrypted() + ) + } else { + try await chatsProvider.retrySendMessage(transaction) + } + } catch let error as ChatsProviderError { + switch error { + case .invalidTransactionStatus: + break + case let .serverError(serverError): + switch serverError { + case .timestampIsInTheFuture: + dialog.send(.timestampIsInTheFuture) default: - dialog.send(.richError(error)) + dialog.send(.warning(error.localizedDescription)) + } + default: + dialog.send(.richError(error)) } + } catch { + dialog.send(.richError(error)) } }.stored(in: tasksStorage) } diff --git a/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift b/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift index 86be22157..1fcb99e89 100644 --- a/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift +++ b/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift @@ -273,5 +273,8 @@ extension UIColor { // Outcome transfer icon background, light red public static let transferOutcomeIconBackground = #colorLiteral(red: 0.9411764706, green: 0.5215686275, blue: 0.5294117647, alpha: 1) //#F08587 + + // Sending file Animation + public static let sendingFileAnimationGrayColor = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) } } From 83919a54d522a6d9cfe5595a3f7314bb3ffd18d2 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 14 May 2025 22:58:54 +0200 Subject: [PATCH 08/45] [trello.com/c/jKqpxiCa/12] scroll to newMessage fix --- Adamant/Modules/Chat/View/ChatViewController.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index 26e802314..2833b6f23 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -796,8 +796,10 @@ extension ChatViewController { scrollDownOnNewMessageIfNeeded(previousBottomMessageId: bottomMessageId) bottomMessageId = viewModel.messages.last?.messageId if !state.isMessagesLoaded { - viewModel.startPosition.map { scrollToPosition($0) } - state.shouldScrollToNewMessages = false + viewModel.startPosition.map { + scrollToPosition($0) + state.shouldScrollToNewMessages = false + } } } From 0b9699147b07a0189c9f34d4347a25fccd1d5f17 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Thu, 15 May 2025 14:07:38 +0200 Subject: [PATCH 09/45] [trello.com/c/jKqpxiCa/12] update scroll to bottom reading added a method that guarantees reading messages from the current position to the bottom --- .../Modules/Chat/View/ChatViewController.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index 2833b6f23..dbf0fd755 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -811,7 +811,6 @@ extension ChatViewController { viewModel.animationType = MessageAnimationType.none let isFirstMessagesInChat = viewModel.unreadMessagesIds?.count == viewModel.messages.count scrollToPosition(.messageId(unreadMessage), setExtraOffset: !isFirstMessagesInChat, scrollAt: .top) - } } } @@ -881,6 +880,22 @@ extension ChatViewController { break } } + + fileprivate func markMessageFromCurrentToBottomAsRead() { + guard let unreadIndexes = viewModel.unreadMesaggesIndexes, !unreadIndexes.isEmpty else { return } + + let visibleSections = messagesCollectionView.indexPathsForVisibleItems.map { $0.section } + guard let minSection = visibleSections.min() else { return } + + let totalSections = messagesCollectionView.numberOfSections + guard totalSections > 0 else { return } + + for section in minSection.. Date: Thu, 15 May 2025 14:34:59 +0200 Subject: [PATCH 10/45] [trello.com/c/jKqpxiCa/12] enter reading improvements --- Adamant/Modules/Chat/View/ChatViewController.swift | 8 ++++++-- Adamant/Modules/Chat/View/ChatViewControllerState.swift | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index dbf0fd755..aa694e985 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -164,7 +164,6 @@ final class ChatViewController: MessagesViewController { super.viewDidAppear(animated) defer { state.isViewAppeared = true - updateUnreadMessages() } inputBar.isUserInteractionEnabled = true chatMessagesCollectionView.fixedBottomOffset = nil @@ -220,7 +219,9 @@ final class ChatViewController: MessagesViewController { override func scrollViewDidScroll(_ scrollView: UIScrollView) { super.scrollViewDidScroll(scrollView) - updateUnreadMessages() + if state.canReadByDidScroll { + updateUnreadMessages() + } updateIsScrollPositionNearlyTheBottom() updateScrollDownButtonVisibility() @@ -340,6 +341,7 @@ extension ChatViewController { viewModel.messagesUpdated .sink { [weak self] _ in self?.updateMessagesPosition() + self?.state.canReadByDidScroll = true self?.updateUnreadMessages() } .store(in: &subscriptions) @@ -808,6 +810,7 @@ extension ChatViewController { state.isMessagesLoaded = true if state.shouldScrollToNewMessages { if let unreadMessage = viewModel.unreadMessagesIds?.first { + state.isAutoScrolling = true viewModel.animationType = MessageAnimationType.none let isFirstMessagesInChat = viewModel.unreadMessagesIds?.count == viewModel.messages.count scrollToPosition(.messageId(unreadMessage), setExtraOffset: !isFirstMessagesInChat, scrollAt: .top) @@ -1030,6 +1033,7 @@ extension ChatViewController { setExtraOffsetForNewMessages() } state.isAutoScrolling = false + updateUnreadMessages() } if viewModel.animationType != MessageAnimationType.none { diff --git a/Adamant/Modules/Chat/View/ChatViewControllerState.swift b/Adamant/Modules/Chat/View/ChatViewControllerState.swift index fb68cb9d2..e983dfc90 100644 --- a/Adamant/Modules/Chat/View/ChatViewControllerState.swift +++ b/Adamant/Modules/Chat/View/ChatViewControllerState.swift @@ -20,6 +20,7 @@ struct ChatViewControllerState { var isAppActive = true var isScrollingToBottom = false var shouldScrollToNewMessages = true + var canReadByDidScroll = false //calculation for animation, might use for something else in the future var isAnimationAllowed: Bool { From 927e6068aeaa70ad8fc686f116ec0847df8dc7b5 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Wed, 14 May 2025 17:32:01 +0300 Subject: [PATCH 11/45] [trello.com/c/dEf1yVQr] Update balance after reconnection to a node --- .../Wallets/Adamant/AdmWalletService.swift | 12 +++++++++--- .../Wallets/Bitcoin/BtcWalletService.swift | 7 ++++++- .../Modules/Wallets/Dash/DashWalletService.swift | 5 +++++ .../Modules/Wallets/Doge/DogeWalletService.swift | 5 +++++ .../Wallets/ERC20/ERC20WalletService.swift | 5 +++++ .../Wallets/Ethereum/EthWalletService.swift | 5 +++++ .../Klayr/WalletService/KlyWalletService.swift | 5 +++++ .../Wallets/WalletViewControllerBase.swift | 16 +++++++++++++++- .../WalletsService/WalletCoreProtocol.swift | 4 ++++ Adamant/ServiceProtocols/AccountService.swift | 2 +- Adamant/Services/AdamantAccountService.swift | 15 +++++---------- Adamant/Services/WalletAutoUpdaterService.swift | 1 - .../CommonKit/Helpers/Nodes+Allowance.swift | 16 ++++++++-------- .../CommonKit/Protocols/ApiServiceProtocol.swift | 10 ++++++++++ .../HealthCheck/HealthCheckWrapper.swift | 4 ++-- 15 files changed, 85 insertions(+), 27 deletions(-) diff --git a/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift b/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift index 179c93b78..d12339d18 100644 --- a/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift +++ b/Adamant/Modules/Wallets/Adamant/AdmWalletService.swift @@ -126,6 +126,11 @@ final class AdmWalletService: NSObject, WalletCoreProtocol, WalletStaticCoreProt var hasEnabledNodePublisher: AnyObservable { apiService.hasEnabledNodePublisher } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + apiService.hasAllowedNodePublisher + } private(set) lazy var coinStorage: CoinStorageService = AdamantCoinStorageService( coinId: tokenUniqueID, @@ -163,9 +168,10 @@ final class AdmWalletService: NSObject, WalletCoreProtocol, WalletStaticCoreProt .store(in: &subscriptions) NotificationCenter.default - .notifications(named: .AdamantAccountService.walletUpdated, object: nil) - .sink { [weak self] _ in - self?.update() + .publisher(for: .AdamantAccountService.isBalanceExpired) + .compactMap { $0.object as? Bool } + .sink { [weak self] isExpired in + self?.resetBalanceAndUpdate() } .store(in: &subscriptions) } diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift index 5f77f7307..f2de391b2 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift @@ -206,6 +206,11 @@ final class BtcWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @unc var hasEnabledNodePublisher: AnyObservable { btcApiService.hasEnabledNodePublisher } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + btcApiService.hasAllowedNodePublisher + } private(set) lazy var coinStorage: CoinStorageService = AdamantCoinStorageService( coinId: tokenUniqueID, @@ -264,7 +269,7 @@ final class BtcWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @unc .store(in: &subscriptions) } - func addTransactionObserver() { + private func addTransactionObserver() { coinStorage.transactionsPublisher .sink { [weak self] transactions in self?.transactions = transactions diff --git a/Adamant/Modules/Wallets/Dash/DashWalletService.swift b/Adamant/Modules/Wallets/Dash/DashWalletService.swift index 2dee1ef21..bde07e706 100644 --- a/Adamant/Modules/Wallets/Dash/DashWalletService.swift +++ b/Adamant/Modules/Wallets/Dash/DashWalletService.swift @@ -115,6 +115,11 @@ final class DashWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @un var hasEnabledNodePublisher: AnyObservable { dashApiService.hasEnabledNodePublisher } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + dashApiService.hasAllowedNodePublisher + } // MARK: - Notifications let serviceEnabledChanged = Notification.Name("adamant.dashWallet.enabledChanged") diff --git a/Adamant/Modules/Wallets/Doge/DogeWalletService.swift b/Adamant/Modules/Wallets/Doge/DogeWalletService.swift index 52c69cdb5..95bc6f5db 100644 --- a/Adamant/Modules/Wallets/Doge/DogeWalletService.swift +++ b/Adamant/Modules/Wallets/Doge/DogeWalletService.swift @@ -174,6 +174,11 @@ final class DogeWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @un var hasEnabledNodePublisher: AnyObservable { dogeApiService.hasEnabledNodePublisher } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + dogeApiService.hasAllowedNodePublisher + } private(set) lazy var coinStorage: CoinStorageService = AdamantCoinStorageService( coinId: tokenUniqueID, diff --git a/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift b/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift index 9d64c3d16..4dee5b497 100644 --- a/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift +++ b/Adamant/Modules/Wallets/ERC20/ERC20WalletService.swift @@ -204,6 +204,11 @@ final class ERC20WalletService: WalletCoreProtocol, ERC20GasAlgorithmComputable, var hasEnabledNodePublisher: AnyObservable { erc20ApiService.hasEnabledNodePublisher } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + erc20ApiService.hasAllowedNodePublisher + } private(set) lazy var coinStorage: CoinStorageService = AdamantCoinStorageService( coinId: tokenUniqueID, diff --git a/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift b/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift index d25101ad1..13b0e591e 100644 --- a/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Modules/Wallets/Ethereum/EthWalletService.swift @@ -205,6 +205,11 @@ final class EthWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, ERC2 var hasEnabledNodePublisher: AnyObservable { ethApiService.hasEnabledNodePublisher } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + ethApiService.hasAllowedNodePublisher + } private(set) lazy var coinStorage: CoinStorageService = AdamantCoinStorageService( coinId: tokenUniqueID, diff --git a/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift b/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift index 58aae80de..bad47dd78 100644 --- a/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift +++ b/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift @@ -69,6 +69,11 @@ final class KlyWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @unc .map { $0.0 && $0.1 } .eraseToAnyPublisher() } + + @MainActor + var hasAllowedNodePublisher: AnyObservable { + klyNodeApiService.hasAllowedNodePublisher + } @Atomic var transactionFeeRaw: BigUInt = BigUInt(integerLiteral: 141000) diff --git a/Adamant/Modules/Wallets/WalletViewControllerBase.swift b/Adamant/Modules/Wallets/WalletViewControllerBase.swift index 2eb668bb9..8eb0b1fd9 100644 --- a/Adamant/Modules/Wallets/WalletViewControllerBase.swift +++ b/Adamant/Modules/Wallets/WalletViewControllerBase.swift @@ -448,7 +448,21 @@ extension WalletViewControllerBase { .store(in: &subscriptions) service.core.hasEnabledNodePublisher - .sink { [weak self] _ in self?.updateWalletUI() } + .sink { [weak self] _ in + self?.updateWalletUI() + } + .store(in: &subscriptions) + + service.core.hasAllowedNodePublisher + .sink { [weak self] hasAllowedNode in + if hasAllowedNode { + guard self?.service?.core is AdmWalletService == false else { + self?.accountService.update(resetBalanceAndUpdate: false, updateOnlyADM: true, updateOnlyVisible: false) + return + } + self?.service?.core.update() + } + } .store(in: &subscriptions) } diff --git a/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift b/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift index 584430a59..52f01e91b 100644 --- a/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift +++ b/Adamant/Modules/Wallets/WalletsService/WalletCoreProtocol.swift @@ -292,6 +292,10 @@ protocol WalletCoreProtocol: AnyObject, Sendable { @MainActor var hasEnabledNodePublisher: AnyObservable { get } + + // It is used to update the balance after the node is turned on + @MainActor + var hasAllowedNodePublisher: AnyObservable { get } @MainActor var walletUpdatePublisher: AnyObservable { get } diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index 264e2a37e..9e5e74bc4 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -36,7 +36,7 @@ extension Notification.Name { /// Raised when wallets collection updated /// /// Use only for communication between AdmanatAccountService and AdamantWalletService. - static let walletUpdated = Notification.Name("adamant.accountService.walletUpdated") + static let isBalanceExpired = Notification.Name("adamant.accountService.isBalanceExpired") private init() {} } diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index 497aba721..cfe570ea3 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -175,8 +175,8 @@ extension AdamantAccountService { isBalanceExpired = true NotificationCenter.default.post( - name: .AdamantAccountService.walletUpdated, - object: self + name: .AdamantAccountService.isBalanceExpired, + object: isBalanceExpired ) NotificationCenter.default.post( name: .AdamantAccountService.accountDataUpdated, @@ -233,6 +233,9 @@ extension AdamantAccountService { // MARK: - AccountService extension AdamantAccountService { // MARK: Update logged account info + /// Does not update UI balance, but the + /// `func update(resetBalanceAndUpdate: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) + /// does, via `wallets.first(where: {$0.core is AdmWalletService})?.core.update()`in completion func update() { self.update(nil) } @@ -306,14 +309,6 @@ extension AdamantAccountService { isBalanceExpired = true state = prevState } - - // Holde case when t he expiring change or when the balance change -// if (isBalanceExpiredPrev != isBalanceExpired && resetBalanceAndUpdate) || (account?.balance != balancePrev) { -// NotificationCenter.default.post( -// name: .AdamantAccountService.walletUpdated, -// object: self -// ) -// } } } } diff --git a/Adamant/Services/WalletAutoUpdaterService.swift b/Adamant/Services/WalletAutoUpdaterService.swift index cf71adc08..746e4f3c5 100644 --- a/Adamant/Services/WalletAutoUpdaterService.swift +++ b/Adamant/Services/WalletAutoUpdaterService.swift @@ -109,7 +109,6 @@ actor WalletAutoUpdateService { interval: getTimeIntervalFor(wallet: core), queue: .global(qos: .utility), callback: { - print("qwe [\(Date())] " + "Update \(wallet.core.tokenName)" + "\n----------------------") guard !(wallet.core is AdmWalletService) else { self.accountService.update(resetBalanceAndUpdate: false, updateOnlyADM: true, updateOnlyVisible: false) return diff --git a/CommonKit/Sources/CommonKit/Helpers/Nodes+Allowance.swift b/CommonKit/Sources/CommonKit/Helpers/Nodes+Allowance.swift index 55cf442a5..ea55b571b 100644 --- a/CommonKit/Sources/CommonKit/Helpers/Nodes+Allowance.swift +++ b/CommonKit/Sources/CommonKit/Helpers/Nodes+Allowance.swift @@ -9,15 +9,15 @@ extension Collection where Element == Node { public func getAllowedNodes(sortedBySpeedDescending: Bool, needWS: Bool) -> [Node] { let allowedNodes = filter { - $0.connectionStatus == .allowed - && $0.isEnabled - && (!needWS || $0.wsEnabled) + $0.connectionStatus == .allowed && $0.isEnabled && (!needWS || $0.wsEnabled) + } + + guard sortedBySpeedDescending else { + return allowedNodes.shuffled() } - return sortedBySpeedDescending - ? allowedNodes.sorted { - $0.ping ?? .greatestFiniteMagnitude < $1.ping ?? .greatestFiniteMagnitude - } - : allowedNodes.shuffled() + return allowedNodes.sorted { + $0.ping ?? .greatestFiniteMagnitude < $1.ping ?? .greatestFiniteMagnitude + } } } diff --git a/CommonKit/Sources/CommonKit/Protocols/ApiServiceProtocol.swift b/CommonKit/Sources/CommonKit/Protocols/ApiServiceProtocol.swift index 55670f6a4..786ee3805 100644 --- a/CommonKit/Sources/CommonKit/Protocols/ApiServiceProtocol.swift +++ b/CommonKit/Sources/CommonKit/Protocols/ApiServiceProtocol.swift @@ -19,6 +19,16 @@ public protocol ApiServiceProtocol: Sendable { } extension ApiServiceProtocol { + + // It is used to update the balance after the node is turned on + @MainActor + public var hasAllowedNodePublisher: AnyObservable { + nodesInfoPublisher + .map { $0.nodes.contains { $0.connectionStatus == .allowed } } + .removeDuplicates() + .eraseToAnyPublisher() + } + @MainActor public var hasEnabledNodePublisher: AnyObservable { nodesInfoPublisher diff --git a/CommonKit/Sources/CommonKit/Services/HealthCheck/HealthCheckWrapper.swift b/CommonKit/Sources/CommonKit/Services/HealthCheck/HealthCheckWrapper.swift index 62b3fc031..6d32bb896 100644 --- a/CommonKit/Sources/CommonKit/Services/HealthCheck/HealthCheckWrapper.swift +++ b/CommonKit/Sources/CommonKit/Services/HealthCheck/HealthCheckWrapper.swift @@ -79,8 +79,8 @@ open class HealthCheckWrapper: S var lastConnectionError: Error? while true { - let node = await nodesForRequest(waitsForConnectivity: waitsForConnectivity) - .first { !usedNodesIds.contains($0.id) } + let nodes = await nodesForRequest(waitsForConnectivity: waitsForConnectivity) + let node = nodes.first { !usedNodesIds.contains($0.id) } guard let node else { break } usedNodesIds.insert(node.id) From e3c10d17c40a013213343247d7665d4398d0f09b Mon Sep 17 00:00:00 2001 From: Vladimir <130711953+vovameister@users.noreply.github.com> Date: Thu, 15 May 2025 17:56:43 +0200 Subject: [PATCH 12/45] Update CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift Co-authored-by: Dmitrij Meidus <55667177+Lainaaa@users.noreply.github.com> --- .../Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift b/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift index 1fcb99e89..5a15abae3 100644 --- a/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift +++ b/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift @@ -275,6 +275,6 @@ extension UIColor { public static let transferOutcomeIconBackground = #colorLiteral(red: 0.9411764706, green: 0.5215686275, blue: 0.5294117647, alpha: 1) //#F08587 // Sending file Animation - public static let sendingFileAnimationGrayColor = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) + public static let sendingFileAnimation = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) } } From e4a741526a2e6cb3ac6bb13d0b3a45fd73f675bc Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Thu, 15 May 2025 23:38:08 +0200 Subject: [PATCH 13/45] [release/3.11.0] fix typo --- .../Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift b/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift index 5a15abae3..1fcb99e89 100644 --- a/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift +++ b/CommonKit/Sources/CommonKit/Helpers/UIHelpers/UIColor+adamant.swift @@ -275,6 +275,6 @@ extension UIColor { public static let transferOutcomeIconBackground = #colorLiteral(red: 0.9411764706, green: 0.5215686275, blue: 0.5294117647, alpha: 1) //#F08587 // Sending file Animation - public static let sendingFileAnimation = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) + public static let sendingFileAnimationGrayColor = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) } } From a4eb372a47594b1bece3b2c8ab1f3670aa0a4d0a Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Fri, 16 May 2025 17:42:07 +0200 Subject: [PATCH 14/45] [trello.com/c/sG80IZan/v2] fix reading Under transaction details --- .../Chat/View/ChatViewController.swift | 26 +++++++++++-------- .../Chat/View/ChatViewControllerState.swift | 5 ++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index 26e802314..11a237d95 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -163,16 +163,19 @@ final class ChatViewController: MessagesViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) defer { - state.isViewAppeared = true - updateUnreadMessages() + state.isFirstTimeViewAppeared = true + if state.isFirstTimeViewAppeared == true { + state.isViewDissappeared.toggle() + updateUnreadMessages() + } } inputBar.isUserInteractionEnabled = true chatMessagesCollectionView.fixedBottomOffset = nil - if !state.isViewAppeared { + if !state.isFirstTimeViewAppeared { viewModel.presentKeyboardOnStartIfNeeded() } - guard isMacOS, !state.isViewAppeared else { return } + guard isMacOS, !state.isFirstTimeViewAppeared else { return } focusInputBarWithoutAnimation() } @@ -191,6 +194,7 @@ final class ChatViewController: MessagesViewController { ? nil : chatMessagesCollectionView.bottomOffset, collectionHeight: messagesCollectionView.contentSize.height ) + state.isViewDissappeared = true } override func collectionView( @@ -228,7 +232,7 @@ final class ChatViewController: MessagesViewController { updateDateHeaderIfNeeded() } guard - state.isViewAppeared, + state.isFirstTimeViewAppeared, scrollView.contentOffset.y <= viewModel.minOffsetForStartLoadNewMessages else { return } @@ -860,7 +864,7 @@ extension ChatViewController { } fileprivate func updateDateHeaderIfNeeded() { - guard state.isViewAppeared else { return } + guard state.isFirstTimeViewAppeared else { return } let targetY: CGFloat = targetYOffset + view.safeAreaInsets.top let visibleIndexPaths = messagesCollectionView.indexPathsForVisibleItems @@ -983,9 +987,9 @@ extension ChatViewController { finalOffset += heightDiff } - chatMessagesCollectionView.setBottomOffset(finalOffset, safely: state.isViewAppeared) - state.isAutoScrolling = false - guard !state.isViewAppeared else { return } + chatMessagesCollectionView.setBottomOffset(finalOffset, safely: state.isFirstTimeViewAppeared) + state.isAutoScrolling = false + guard !state.isFirstTimeViewAppeared else { return } chatMessagesCollectionView.fixedBottomOffset = chatMessagesCollectionView.bottomOffset case let .messageId(id, scrollToBottomIfNotFound): var index = viewModel.messages.firstIndex(where: { $0.messageId == id }) @@ -1123,7 +1127,7 @@ extension ChatViewController { ) } - if state.isViewAppeared { + if state.isFirstTimeViewAppeared { messageInputBar.inputTextView.becomeFirstResponder() } } @@ -1157,7 +1161,7 @@ extension ChatViewController { ) } ) - if state.isViewAppeared { + if state.isFirstTimeViewAppeared { messageInputBar.inputTextView.becomeFirstResponder() } } diff --git a/Adamant/Modules/Chat/View/ChatViewControllerState.swift b/Adamant/Modules/Chat/View/ChatViewControllerState.swift index fb68cb9d2..7402738c2 100644 --- a/Adamant/Modules/Chat/View/ChatViewControllerState.swift +++ b/Adamant/Modules/Chat/View/ChatViewControllerState.swift @@ -11,7 +11,7 @@ import CommonKit struct ChatViewControllerState { var isMessagesLoaded = false var isScrollPositionNearlyTheBottom = true - var isViewAppeared = false + var isFirstTimeViewAppeared = false var scrollToUnreadBottomConstraint: Constraint? var isScrollDownButtonHidden = true var previousUnreadCount: Int = 0 @@ -20,12 +20,13 @@ struct ChatViewControllerState { var isAppActive = true var isScrollingToBottom = false var shouldScrollToNewMessages = true + var isViewDissappeared = false //calculation for animation, might use for something else in the future var isAnimationAllowed: Bool { isMessagesLoaded && !isAutoScrolling && !isScrollingToBottom } var canReadChat: Bool { - !isAutoScrolling && isViewAppeared && (!isMacOS || isAppActive) + !isAutoScrolling && isFirstTimeViewAppeared && (!isMacOS || isAppActive) && !isViewDissappeared } } From 099f1168f87afc0d113f3940f23f5204e85f4e8a Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Fri, 16 May 2025 19:02:56 +0300 Subject: [PATCH 15/45] [trello.com/c/x0o1aqih] Improve code quality --- .../Subviews/ChatMedia/Content/ChatMediaContnentView.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift index 86ec47c01..eef0e9e6e 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Content/ChatMediaContnentView.swift @@ -278,11 +278,7 @@ extension ChatMediaContentView.Model { var spaceCount: CGFloat = fileModel.isMediaFilesOnly ? .zero : 1 - if isReply { - spaceCount += 2 - } - - if !comment.string.isEmpty { + if isReply || !comment.string.isEmpty{ spaceCount += 2 } From 0f8d21734f9e880d1425bbd2fe413e55faca18eb Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Fri, 16 May 2025 18:03:41 +0200 Subject: [PATCH 16/45] [trello.com/c/jKqpxiCa/12] typo fix --- Adamant/Modules/Chat/View/ChatViewController.swift | 8 ++++---- Adamant/Modules/Chat/View/ChatViewControllerState.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index aa694e985..544da603f 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -219,7 +219,7 @@ final class ChatViewController: MessagesViewController { override func scrollViewDidScroll(_ scrollView: UIScrollView) { super.scrollViewDidScroll(scrollView) - if state.canReadByDidScroll { + if state.isInitialMessagesWereUpdated { updateUnreadMessages() } updateIsScrollPositionNearlyTheBottom() @@ -341,7 +341,7 @@ extension ChatViewController { viewModel.messagesUpdated .sink { [weak self] _ in self?.updateMessagesPosition() - self?.state.canReadByDidScroll = true + self?.state.isInitialMessagesWereUpdated = true self?.updateUnreadMessages() } .store(in: &subscriptions) @@ -884,7 +884,7 @@ extension ChatViewController { } } - fileprivate func markMessageFromCurrentToBottomAsRead() { + fileprivate func markMessagesFromCurrentToBottomAsRead() { guard let unreadIndexes = viewModel.unreadMesaggesIndexes, !unreadIndexes.isEmpty else { return } let visibleSections = messagesCollectionView.indexPathsForVisibleItems.map { $0.section } @@ -910,7 +910,7 @@ extension ChatViewController { guard let self else { return } if viewModel.shouldScrollToBottom { state.isScrollingToBottom = true - markMessageFromCurrentToBottomAsRead() + markMessagesFromCurrentToBottomAsRead() self.messagesCollectionView.scrollToBottom(animated: true) } else if let id = viewModel.unreadMessagesIds?.first { viewModel.animationType = MessageAnimationType.none diff --git a/Adamant/Modules/Chat/View/ChatViewControllerState.swift b/Adamant/Modules/Chat/View/ChatViewControllerState.swift index e983dfc90..f68687be4 100644 --- a/Adamant/Modules/Chat/View/ChatViewControllerState.swift +++ b/Adamant/Modules/Chat/View/ChatViewControllerState.swift @@ -20,7 +20,7 @@ struct ChatViewControllerState { var isAppActive = true var isScrollingToBottom = false var shouldScrollToNewMessages = true - var canReadByDidScroll = false + var isInitialMessagesWereUpdated = false //calculation for animation, might use for something else in the future var isAnimationAllowed: Bool { From 6f9da98e924faf1d1fc6c45b5f75a2e2a36895e2 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Fri, 16 May 2025 18:21:29 +0200 Subject: [PATCH 17/45] [trello.com/c/sG80IZan/v2] fix logic --- Adamant/Modules/Chat/View/ChatViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index 11a237d95..a43f9b83e 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -164,7 +164,7 @@ final class ChatViewController: MessagesViewController { super.viewDidAppear(animated) defer { state.isFirstTimeViewAppeared = true - if state.isFirstTimeViewAppeared == true { + if state.isViewDissappeared == true { state.isViewDissappeared.toggle() updateUnreadMessages() } From fdd98e9ffdee746f2e956e707281f2ca5a5a8c36 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Fri, 16 May 2025 22:31:00 +0300 Subject: [PATCH 18/45] [trello.com/c/dEf1yVQr] Increase speed of loading ADM wallet after recconection --- Adamant/ServiceProtocols/AccountService.swift | 1 - Adamant/Services/AdamantAccountService.swift | 41 ++++++------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index 9e5e74bc4..57917b79e 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -156,7 +156,6 @@ protocol AccountService: AnyObject, Sendable { // MARK: Account functions /// Update logged account info - func update() func update(resetBalanceAndUpdate: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) /// Login into Adamant using passphrase. diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index cfe570ea3..dedde3239 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -56,25 +56,15 @@ final class AdamantAccountService: AccountService, @unchecked Sendable { self.coreDataStack = coreDataStack NotificationCenter.default.addObserver(forName: .AdamantAccountService.forceUpdateBalance, object: nil, queue: OperationQueue.main) { [weak self] _ in - self?.update() + self?.update(resetBalanceAndUpdate: false, updateOnlyADM: false, updateOnlyVisible: true) } - NotificationCenter.default - .notifications(named: .AdamantReachabilityMonitor.reachabilityChanged) - .sink { @MainActor [weak self] data in - let connection = data.userInfo?[AdamantUserInfoKey.ReachabilityMonitor.connection] as? Bool - - guard connection == true else { return } - self?.update(resetBalanceAndUpdate: false, updateOnlyADM: false, updateOnlyVisible: true) - } - .store(in: &subscriptions) - NotificationCenter.default .notifications(named: UIApplication.didBecomeActiveNotification, object: nil) .sink { @MainActor [weak self] _ in guard self?.previousAppState == .background else { return } self?.previousAppState = .active - self?.update() + self?.update(resetBalanceAndUpdate: false, updateOnlyADM: false, updateOnlyVisible: true) } .store(in: &subscriptions) @@ -85,9 +75,12 @@ final class AdamantAccountService: AccountService, @unchecked Sendable { } .store(in: &subscriptions) - connection.filter { $0 }.sink { [weak self] _ in - self?.update() - }.store(in: &subscriptions) + connection + .filter { $0 } + .sink { [weak self] connection in + guard connection == true else { return } + self?.update(resetBalanceAndUpdate: false, updateOnlyADM: false, updateOnlyVisible: true) + }.store(in: &subscriptions) setupSecureStore() } @@ -231,15 +224,7 @@ extension AdamantAccountService { } // MARK: - AccountService -extension AdamantAccountService { - // MARK: Update logged account info - /// Does not update UI balance, but the - /// `func update(resetBalanceAndUpdate: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) - /// does, via `wallets.first(where: {$0.core is AdmWalletService})?.core.update()`in completion - func update() { - self.update(nil) - } - +extension AdamantAccountService { func update(resetBalanceAndUpdate: Bool, updateOnlyADM: Bool, updateOnlyVisible: Bool) { let wallets = walletServiceCompose.getWallets() if !updateOnlyADM { @@ -262,7 +247,7 @@ extension AdamantAccountService { } update({ _ in - wallets.first(where: {$0.core is AdmWalletService})?.core.update() + wallets.first(where: {$0.core is AdmWalletService})?.core.update() }) } @@ -296,14 +281,14 @@ extension AdamantAccountService { markBalanceAsFresh() self.account = account + state = .loggedIn + completion?(.success(account: account, alert: nil)) + NotificationCenter.default.post( name: .AdamantAccountService.accountDataUpdated, object: self ) - state = .loggedIn - completion?(.success(account: account, alert: nil)) - case .failure(let error): completion?(.failure(.apiError(error: error))) isBalanceExpired = true From 73a0485ddb7e662fbf3d0f811e2571bf011ef996 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Mon, 19 May 2025 17:52:15 +0300 Subject: [PATCH 19/45] [trello.com/c/dEf1yVQr] Fix crash because of unsafe access for AccountWalletsState in multithread environment --- .../AccountWallets/AccountWalletsState.swift | 6 ++---- .../AccountWallets/AccountWalletsViewModel.swift | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsState.swift b/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsState.swift index 30174ffa2..e3365a6fa 100644 --- a/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsState.swift +++ b/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsState.swift @@ -9,10 +9,8 @@ import CommonKit import Parchment -struct AccountWalletsState { - var wallets: [AccountWalletCellState] - - static let `default` = Self(wallets: []) +struct AccountWalletsState: Sendable { + @Atomic var wallets: [AccountWalletCellState] = [] } struct AccountWalletCellState { diff --git a/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsViewModel.swift b/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsViewModel.swift index 43f1016ce..4ee9b7008 100644 --- a/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsViewModel.swift +++ b/Adamant/Modules/Account/AccountViewController/AccountWallets/AccountWalletsViewModel.swift @@ -12,13 +12,14 @@ import Foundation @MainActor final class AccountWalletsViewModel { - var state: AccountWalletsState = .default + var state: AccountWalletsState private let walletsStoreService: WalletStoreServiceProviderProtocol private var subscriptions = Set() init(walletsStoreService: WalletStoreServiceProviderProtocol) { self.walletsStoreService = walletsStoreService + state = AccountWalletsState() setup() } } From 90d114d380e87d351f9ab4fd88a3455906810528 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Mon, 19 May 2025 21:34:43 +0200 Subject: [PATCH 20/45] [trello.com/c/7YVA2NTe] fix layout macOs --- .../ChatMedia/Container/ChatMediaContainerView.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift index 02b985c8a..c011529d8 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift @@ -184,7 +184,12 @@ extension ChatMediaContainerView { reactionsStack.insertSubview(progressRingHostingView, aboveSubview: statusButton) progressRingHostingView.snp.makeConstraints { make in - make.center.equalTo(statusButton) + if isMacOS { + make.centerX.equalTo(statusButton).offset(-1) + } else { + make.centerX.equalTo(statusButton) + } + make.centerY.equalTo(statusButton) make.size.equalTo(29) } } From e835eb13a482a25247ba6653d1dd73e96f853ffc Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Mon, 19 May 2025 22:00:40 +0200 Subject: [PATCH 21/45] [trello.com/c/7YVA2NTe] avarageProgress -> total size progress --- .../Container/ChatMediaContainerView.swift | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift index c011529d8..ae3e57316 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift @@ -211,29 +211,35 @@ extension ChatMediaContainerView { statusButton.isHidden = status == .success } - func averageUploadProgress() -> Double { + func totalUploadProgress() -> Double { let files = model.content.fileModel.files - - let progresses = files - .compactMap { $0.progress } - - guard !progresses.isEmpty else { + + let totalBytes: Int64 = files.reduce(0) { result, file in + result + file.file.size + } + + let uploadedBytes: Int64 = files.reduce(0) { result, file in + let progress = Double(file.progress ?? 0) / 100.0 + return result + Int64(Double(file.file.size) * progress) + } + + guard totalBytes > 0 else { return 0.0 } - - let totalProgress = progresses.reduce(0, +) - return Double(totalProgress) / Double(progresses.count) + + return Double(uploadedBytes) / Double(totalBytes) * 100.0 } func updateProgressRing() { - let averageProgress = averageUploadProgress() + let averageProgress = totalUploadProgress() if averageProgress == 0 { statusProgressState.backgroundGradient = LinearGradient( gradient: Gradient(colors: [.white, Color(.adamant.sendingFileAnimationGrayColor)]), startPoint: .topLeading, endPoint: .bottomTrailing - )} else { + ) + } else { statusProgressState.backgroundGradient = nil } statusProgressState.progress = averageProgress / 100 From 0e05129ba110132ca07fba13e376f7e172fad34c Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 20 May 2025 00:52:52 +0200 Subject: [PATCH 22/45] [trello.com/c/aZtAfZ44] fix message replace --- NotificationServiceExtension/NotificationService.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NotificationServiceExtension/NotificationService.swift b/NotificationServiceExtension/NotificationService.swift index b28bfd1e8..6608ef2a1 100644 --- a/NotificationServiceExtension/NotificationService.swift +++ b/NotificationServiceExtension/NotificationService.swift @@ -337,9 +337,11 @@ class NotificationService: UNNotificationServiceExtension { var badgeValue = (Int(SecureStore.get(StoreKey.notificationsService.customBadgeNumber) ?? "0") ?? 0) if !shouldIgnoreNotification { badgeValue += 1 - bestAttemptContent.userInfo[AdamantNotificationUserInfoKeys.decodedMessage] = decodedMessage + } else { + bestAttemptContent.body = "" } + bestAttemptContent.userInfo[AdamantNotificationUserInfoKeys.decodedMessage] = decodedMessage bestAttemptContent.badge = NSNumber(value: badgeValue) SecureStore.set(String(badgeValue), for: StoreKey.notificationsService.customBadgeNumber) From 901ae1f6587d5e37707334a42122eee9d18f0633 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 20 May 2025 04:19:16 +0200 Subject: [PATCH 23/45] [trello.com/c/aZtAfZ44] make read reaction faster --- Adamant/Modules/Chat/View/ChatViewController.swift | 2 +- Adamant/Modules/Chat/ViewModel/ChatViewModel.swift | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index 979e3e3c1..c0022eb79 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -166,8 +166,8 @@ final class ChatViewController: MessagesViewController { state.isFirstTimeViewAppeared = true if state.isViewDissappeared == true { state.isViewDissappeared.toggle() - updateUnreadMessages() } + updateUnreadMessages() } inputBar.isUserInteractionEnabled = true chatMessagesCollectionView.fixedBottomOffset = nil diff --git a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift index 20275530e..91b76d5e9 100644 --- a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift +++ b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift @@ -538,6 +538,12 @@ final class ChatViewModel: NSObject { func scroll(to messageId: String) { guard let partnerAddress = chatroom?.partner?.address else { return } + if let chatroom, + let messageIdToShow { + Task(priority: .high) { + await chatsProvider.markMessageAsRead(chatroom: chatroom, message: messageIdToShow) + } + } messageIdToShow = nil Task { @@ -556,9 +562,6 @@ final class ChatViewModel: NSObject { recipient: partnerAddress ) } - if let chatroom { - await chatsProvider.markMessageAsRead(chatroom: chatroom, message: messageId) - } await waitForMessage(withId: messageId) scrollToId = messageId From 4f486a87653505174fe9e140c9987333dcea3fad Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 20 May 2025 05:06:27 +0200 Subject: [PATCH 24/45] [trello.com/c/aZtAfZ44] remove extra if --- Adamant/Modules/Chat/View/ChatViewController.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index c0022eb79..899fda1d6 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -164,9 +164,7 @@ final class ChatViewController: MessagesViewController { super.viewDidAppear(animated) defer { state.isFirstTimeViewAppeared = true - if state.isViewDissappeared == true { - state.isViewDissappeared.toggle() - } + state.isViewDissappeared = false updateUnreadMessages() } inputBar.isUserInteractionEnabled = true From 68ba923f10713eab6213aa6a858528bd2f5cc05d Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 20 May 2025 17:20:03 +0200 Subject: [PATCH 25/45] [trello.com/c/7YVA2NTe] syntactic improve --- .../Subviews/ChatMedia/Container/ChatMediaContainerView.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift index ae3e57316..2b4f61355 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMedia/Container/ChatMediaContainerView.swift @@ -214,9 +214,7 @@ extension ChatMediaContainerView { func totalUploadProgress() -> Double { let files = model.content.fileModel.files - let totalBytes: Int64 = files.reduce(0) { result, file in - result + file.file.size - } + let totalBytes = files.map { $0.file.size }.reduce(0, +) let uploadedBytes: Int64 = files.reduce(0) { result, file in let progress = Double(file.progress ?? 0) / 100.0 From ae0f4488b2b3d3c13bd25bd9ea105c64a12ed750 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 20 May 2025 21:15:31 +0200 Subject: [PATCH 26/45] [trello.com/c/jKqpxiCa] update scroll to bottom --- .../Chat/View/ChatViewController.swift | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index 979e3e3c1..ecde95578 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -913,15 +913,7 @@ extension ChatViewController { let button = ChatScrollButton(position: .down) button.action = { [weak self] in guard let self else { return } - if viewModel.shouldScrollToBottom { - state.isScrollingToBottom = true - markMessagesFromCurrentToBottomAsRead() - self.messagesCollectionView.scrollToBottom(animated: true) - } else if let id = viewModel.unreadMessagesIds?.first { - viewModel.animationType = MessageAnimationType.none - self.scrollToPosition(.messageId(id), animated: true) - viewModel.shouldScrollToBottom = true - } + scrollButtonAction() } button.alpha = 0 return button @@ -958,6 +950,60 @@ extension ChatViewController { ) return collection } + + fileprivate func scrollButtonAction() { + if viewModel.shouldScrollToBottom { + scrollToBottom() + return + } + + guard let firstUnreadId = viewModel.unreadMessagesIds?.first, + let unreadIndex = viewModel.messages.firstIndex(where: { $0.messageId == firstUnreadId }) else { + scrollToBottom() + return + } + + let visibleBounds = messagesCollectionView.bounds + let restrictedVisibleRect = visibleBounds.insetBy(dx: 0, dy: 100) + + let visibleSections: Set = Set( + messagesCollectionView.indexPathsForVisibleItems.compactMap { indexPath in + guard let attributes = messagesCollectionView.layoutAttributesForItem(at: indexPath) else { + return nil + } + + let frame = attributes.frame + if restrictedVisibleRect.intersects(frame) { + return indexPath.section + } else { + return nil + } + } + ) + + if visibleSections.contains(unreadIndex) { + scrollToBottom() + return + } + + if let separatorIndex = viewModel.separatorState.separatorIndex, + unreadIndex == separatorIndex + 1 { + viewModel.animationType = .none + scrollToPosition(.messageId(firstUnreadId), animated: false, setExtraOffset: true, scrollAt: .top) + viewModel.shouldScrollToBottom = true + return + } + + viewModel.animationType = .none + scrollToPosition(.messageId(firstUnreadId), animated: true, scrollAt: .top) + viewModel.shouldScrollToBottom = true + } + + fileprivate func scrollToBottom() { + state.isScrollingToBottom = true + markMessagesFromCurrentToBottomAsRead() + messagesCollectionView.scrollToBottom(animated: true) + } } // MARK: Other From 8161202b65582f0fa8ba950ba12915ac123edef5 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 21 May 2025 05:32:14 +0200 Subject: [PATCH 27/45] [trello.com/c/jKqpxiCa] always show scroll down if have some new messages --- .../Chat/View/ChatViewController.swift | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index ecde95578..b2f5b77ad 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -836,12 +836,16 @@ extension ChatViewController { fileprivate func updateScrollDownButtonVisibility() { let topCount = viewModel.unreadMessagesIds?.count ?? 0 self.scrollDownButton.updateCounter(topCount) - guard state.isScrollDownButtonHidden != state.isScrollPositionNearlyTheBottom else { return } - state.isScrollDownButtonHidden = state.isScrollPositionNearlyTheBottom + + let shouldShowButton = (topCount > 0) || !state.isScrollPositionNearlyTheBottom + guard state.isScrollDownButtonHidden != !shouldShowButton else { return } + + state.isScrollDownButtonHidden = !shouldShowButton let buttonUpdate = { - self.scrollDownButton.alpha = self.state.isScrollPositionNearlyTheBottom ? 0 : 1 + self.scrollDownButton.alpha = shouldShowButton ? 1 : 0 self.updateScrollToUnreadButtonPosition() } + if state.isAnimationAllowed { UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut) { buttonUpdate() @@ -1441,4 +1445,11 @@ private let messagePadding: CGFloat = 12 private let filesToolbarViewHeight: CGFloat = 140 private let targetYOffset: CGFloat = 20 private let scrollButtonHeight: CGFloat = 30 -private let hiddenScrollViewPartHeight: CGFloat = 94 +//not a real height just for reading messages +private var hiddenScrollViewPartHeight: CGFloat { + if isMacOS { + return 93 + } else { + return 85 + } +} From 008e9b223e2bd8f0a548d36a20b59d9acbed284f Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 21 May 2025 06:08:35 +0200 Subject: [PATCH 28/45] [trello.com/c/jKqpxiCa] remove fast reading message to show to fix separator showing --- Adamant/Modules/Chat/ViewModel/ChatViewModel.swift | 7 ------- .../Chat/ViewModel/Models/NewMessageSeparatorState.swift | 1 - 2 files changed, 8 deletions(-) diff --git a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift index 91b76d5e9..0f5c7a581 100644 --- a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift +++ b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift @@ -308,7 +308,6 @@ final class ChatViewModel: NSObject { fileprivate func setUpMessagetoShow(messageId: String?) { if let messageId { self.messageIdToShow = messageId - separatorState.shouldScrollToNewMessagesOrSavedPosition = false } } @@ -538,12 +537,6 @@ final class ChatViewModel: NSObject { func scroll(to messageId: String) { guard let partnerAddress = chatroom?.partner?.address else { return } - if let chatroom, - let messageIdToShow { - Task(priority: .high) { - await chatsProvider.markMessageAsRead(chatroom: chatroom, message: messageIdToShow) - } - } messageIdToShow = nil Task { diff --git a/Adamant/Modules/Chat/ViewModel/Models/NewMessageSeparatorState.swift b/Adamant/Modules/Chat/ViewModel/Models/NewMessageSeparatorState.swift index c0d6cf5d4..de648aa6b 100644 --- a/Adamant/Modules/Chat/ViewModel/Models/NewMessageSeparatorState.swift +++ b/Adamant/Modules/Chat/ViewModel/Models/NewMessageSeparatorState.swift @@ -14,5 +14,4 @@ struct NewMessageSeparatorState { var isFirstUpdate: Bool = true var didAddSeparator: Bool = false var isScrollPositionNearlyTheBottom = true - var shouldScrollToNewMessagesOrSavedPosition = true } From 74e08fbb3047ea7da5fd4c34fe89f3960692f258 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Wed, 21 May 2025 16:53:41 +0300 Subject: [PATCH 29/45] [trello.com/c/SL4zLZnb] Improve coin tx sending algorithm --- Adamant/Models/BTCRawTransaction.swift | 11 +++++------ ...ervice+RichMessageProviderWithStatusCheck.swift | 2 ++ .../Bitcoin/DTO/BtcTransactionResponse.swift | 2 +- .../DogeTransactionDetailsViewController.swift | 5 ++--- ...ervice+RichMessageProviderWithStatusCheck.swift | 14 ++++++++------ .../TransactionDetailsViewControllerBase.swift | 5 ++--- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Adamant/Models/BTCRawTransaction.swift b/Adamant/Models/BTCRawTransaction.swift index c6afaf70a..01f1c0d32 100644 --- a/Adamant/Models/BTCRawTransaction.swift +++ b/Adamant/Models/BTCRawTransaction.swift @@ -27,15 +27,14 @@ struct BTCRawTransaction { func asBtcTransaction(_ as: T.Type, for address: String, blockId: String? = nil) -> T { // MARK: Known values - let confirmationsValue: String? - let transactionStatus: TransactionStatus + var confirmationsValue: String? = nil + var transactionStatus: TransactionStatus = .registered if let confirmations = confirmations { confirmationsValue = String(confirmations) - transactionStatus = confirmations > 0 ? .success : .pending - } else { - confirmationsValue = nil - transactionStatus = .registered + if confirmations > 0 { + transactionStatus = .success + } } // Transfers diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift index ff73b6b0f..850a7759c 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift @@ -44,6 +44,8 @@ extension BtcWalletService { transaction: CoinTransaction, btcTransaction: BtcTransaction ) async -> TransactionStatus { + // Before we already stored another status in RawBtcTransactionResponse.asBtcTransaction(_:for:height:). + // So we don't need to double check anything here because it is already at least "registered". guard let status = btcTransaction.transactionStatus else { return .inconsistent(.unknown) } diff --git a/Adamant/Modules/Wallets/Bitcoin/DTO/BtcTransactionResponse.swift b/Adamant/Modules/Wallets/Bitcoin/DTO/BtcTransactionResponse.swift index 911ca8fb5..909768875 100644 --- a/Adamant/Modules/Wallets/Bitcoin/DTO/BtcTransactionResponse.swift +++ b/Adamant/Modules/Wallets/Bitcoin/DTO/BtcTransactionResponse.swift @@ -69,7 +69,7 @@ struct RawBtcStatus: Decodable { extension RawBtcTransactionResponse { func asBtcTransaction(_ as: T.Type, for address: String, height: Decimal? = nil) -> T { - let transactionStatus: TransactionStatus = status.confirmed ? .success : .pending + let transactionStatus: TransactionStatus = status.confirmed ? .success : .registered var date: Date? if let time = status.time { diff --git a/Adamant/Modules/Wallets/Doge/DogeTransactionDetailsViewController.swift b/Adamant/Modules/Wallets/Doge/DogeTransactionDetailsViewController.swift index 15c2564fd..e9146c046 100644 --- a/Adamant/Modules/Wallets/Doge/DogeTransactionDetailsViewController.swift +++ b/Adamant/Modules/Wallets/Doge/DogeTransactionDetailsViewController.swift @@ -97,10 +97,9 @@ final class DogeTransactionDetailsViewController: TransactionDetailsViewControll } let trs = try await service.getTransaction(by: id, waitsForConnectivity: false) + - if let blockInfo = cachedBlockInfo, - blockInfo.hash == trs.blockHash - { + if let blockInfo = cachedBlockInfo, blockInfo.hash == trs.blockHash { transaction = trs.asBtcTransaction( DogeTransaction.self, for: address, diff --git a/Adamant/Modules/Wallets/Doge/DogeWalletService+RichMessageProviderWithStatusCheck.swift b/Adamant/Modules/Wallets/Doge/DogeWalletService+RichMessageProviderWithStatusCheck.swift index 87f92df4f..6d4d263b7 100644 --- a/Adamant/Modules/Wallets/Doge/DogeWalletService+RichMessageProviderWithStatusCheck.swift +++ b/Adamant/Modules/Wallets/Doge/DogeWalletService+RichMessageProviderWithStatusCheck.swift @@ -30,13 +30,15 @@ extension DogeWalletService { } catch { return .init(error: error) } + + let status = await getStatus( + dogeTransaction: dogeTransaction, + transaction: transaction + ) - return await .init( + return .init( sentDate: dogeTransaction.date, - status: getStatus( - dogeTransaction: dogeTransaction, - transaction: transaction - ) + status: status ) } } @@ -51,7 +53,7 @@ extension DogeWalletService { let dogeDate = dogeTransaction.date, confirmations > 0 || dogeDate.timeIntervalSinceNow > -60 * 15 else { - return .pending + return .registered } // MARK: Check amount & address diff --git a/Adamant/Modules/Wallets/TransactionDetailsViewControllerBase.swift b/Adamant/Modules/Wallets/TransactionDetailsViewControllerBase.swift index 63ffccf0f..4b6255f24 100644 --- a/Adamant/Modules/Wallets/TransactionDetailsViewControllerBase.swift +++ b/Adamant/Modules/Wallets/TransactionDetailsViewControllerBase.swift @@ -185,6 +185,7 @@ class TransactionDetailsViewControllerBase: FormViewController { ) } } + private lazy var dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium @@ -597,9 +598,7 @@ class TransactionDetailsViewControllerBase: FormViewController { cell.textLabel?.textColor = UIColor.adamant.textColor cell.detailTextLabel?.textColor = self?.transactionStatus?.color ?? UIColor.adamant.textColor - if let value = self?.transactionStatus?.localized, - !value.isEmpty - { + if let value = self?.transactionStatus?.localized, !value.isEmpty { row.value = value } else { row.value = TransactionStatus.registered.localized From 3f13cf665b792187536019ebca90c07ed415f31f Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Fri, 23 May 2025 04:01:24 +0200 Subject: [PATCH 30/45] [trello.com/c/jKqpxiCa] improved syntax --- .../Modules/Chat/View/ChatViewController.swift | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Adamant/Modules/Chat/View/ChatViewController.swift b/Adamant/Modules/Chat/View/ChatViewController.swift index b2f5b77ad..4e8daf839 100644 --- a/Adamant/Modules/Chat/View/ChatViewController.swift +++ b/Adamant/Modules/Chat/View/ChatViewController.swift @@ -971,17 +971,10 @@ extension ChatViewController { let restrictedVisibleRect = visibleBounds.insetBy(dx: 0, dy: 100) let visibleSections: Set = Set( - messagesCollectionView.indexPathsForVisibleItems.compactMap { indexPath in - guard let attributes = messagesCollectionView.layoutAttributesForItem(at: indexPath) else { - return nil - } - - let frame = attributes.frame - if restrictedVisibleRect.intersects(frame) { - return indexPath.section - } else { - return nil - } + messagesCollectionView.indexPathsForVisibleItems.compactMap { + guard let frame = messagesCollectionView.layoutAttributesForItem(at: $0)?.frame, + restrictedVisibleRect.intersects(frame) else { return nil } + return $0.section } ) From 640384e0109b0471cbdb58df32d3b3f9b0be2d89 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Mon, 26 May 2025 03:58:24 +0200 Subject: [PATCH 31/45] [trello.com/c/pRsTtPYQ] fix reply + file sending fix --- .../Chat/ViewModel/ChatFileService.swift | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Adamant/Modules/Chat/ViewModel/ChatFileService.swift b/Adamant/Modules/Chat/ViewModel/ChatFileService.swift index a9d02ff01..1a5983b1d 100644 --- a/Adamant/Modules/Chat/ViewModel/ChatFileService.swift +++ b/Adamant/Modules/Chat/ViewModel/ChatFileService.swift @@ -1104,7 +1104,9 @@ extension ChatFileService { ) } - guard var (fileMessage, richMessage) = uploadingFilesDictionary[richMessageId: txId] + guard var (fileMessage, richMessage) = + uploadingFilesDictionary[richMessageId: txId] ?? + uploadingFilesDictionary[replyRichMessageId: txId].map({ ($0.0, $0.1.reply_message) }) else { return } var richFiles = richMessage.files @@ -1126,7 +1128,17 @@ extension ChatFileService { } richMessage.files = richFiles - fileMessage.adamantMessage = .richMessage(payload: richMessage) + fileMessage.adamantMessage = { + if case let .richMessage(payload) = fileMessage.adamantMessage, + let reply = payload as? RichFileReply { + return .richMessage(payload: RichFileReply( + replyto_id: reply.replyto_id, + reply_message: richMessage + )) + } else { + return .richMessage(payload: richMessage) + } + }() uploadingFilesDictionary[txId] = fileMessage try? await chatsProvider.updateTxMessageContent( @@ -1316,4 +1328,13 @@ extension Dictionary where Key == String, Value == FileMessage { return (fileMessage, richMessage) } + + fileprivate subscript(replyRichMessageId txId: String) -> (FileMessage, RichFileReply)? { + guard let fileMessage = self[txId], + case let .richMessage(payload) = fileMessage.adamantMessage, + let reply = payload as? RichFileReply + else { return nil } + + return (fileMessage, reply) + } } From 916e332757fae42b75e3c72812f577f8b545fe05 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 27 May 2025 06:00:38 +0200 Subject: [PATCH 32/45] [trello.com/c/m0k5mrQR] fix swiftui ios menu layout --- .../CommonKit/Helpers/SwiftUI/View+Extension.swift | 3 +-- .../Implementation/ContextMenuOverlayView.swift | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CommonKit/Sources/CommonKit/Helpers/SwiftUI/View+Extension.swift b/CommonKit/Sources/CommonKit/Helpers/SwiftUI/View+Extension.swift index 4734602e2..14a872d3f 100644 --- a/CommonKit/Sources/CommonKit/Helpers/SwiftUI/View+Extension.swift +++ b/CommonKit/Sources/CommonKit/Helpers/SwiftUI/View+Extension.swift @@ -41,9 +41,8 @@ extension View { return resultView } - // TODO: Remove this function (or fix) public func fullScreen() -> some View { - return frame(width: .infinity, height: .infinity) + return frame(maxWidth: .infinity, maxHeight: .infinity) .ignoresSafeArea() } diff --git a/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/ContextMenuOverlayView.swift b/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/ContextMenuOverlayView.swift index 54a784027..3088c4add 100644 --- a/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/ContextMenuOverlayView.swift +++ b/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/ContextMenuOverlayView.swift @@ -126,7 +126,6 @@ extension ContextMenuOverlayView { .padding(.leading, viewModel.contentViewLocation.x) Spacer() } - .fullScreen() .transition(.opacity) } @@ -154,8 +153,8 @@ extension ContextMenuOverlayView { } } .frame( - width: .infinity, - height: viewModel.menuSize.height + maxWidth: .infinity, + maxHeight: viewModel.menuSize.height ) .offset(y: viewModel.menuLocation.y) .ignoresSafeArea() @@ -167,7 +166,6 @@ extension ContextMenuOverlayView { .onTapGesture {} Spacer() } - .fullScreen() .transition(.opacity) } @@ -187,9 +185,7 @@ extension ContextMenuOverlayView { .padding(.leading, viewModel.upperContentViewLocation.x) Spacer() } - .fullScreen() } - } private let minBottomOffset: CGFloat = 50 From 0f568f2388e1496227bbb09d8435fedb1fab8e2e Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 27 May 2025 06:24:18 +0200 Subject: [PATCH 33/45] [trello.com/c/m0k5mrQR] fix macOs context menu layout --- .../MacOSOverlay/ContextMenuOverlayViewMac.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/MacOSOverlay/ContextMenuOverlayViewMac.swift b/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/MacOSOverlay/ContextMenuOverlayViewMac.swift index 06883de6b..ee6f1758f 100644 --- a/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/MacOSOverlay/ContextMenuOverlayViewMac.swift +++ b/pckg/AdvancedContextMenuKit/Sources/AdvancedContextMenuKit/Implementation/MacOSOverlay/ContextMenuOverlayViewMac.swift @@ -70,7 +70,6 @@ extension ContextMenuOverlayViewMac { } Spacer() } - .fullScreen() .transition(.opacity) } @@ -94,7 +93,6 @@ extension ContextMenuOverlayViewMac { .padding(.leading, viewModel.contentLocation.x) Spacer() } - .fullScreen() .transition(.opacity) } @@ -110,7 +108,6 @@ extension ContextMenuOverlayViewMac { } Spacer() } - .fullScreen() .transition(.opacity) } @@ -120,7 +117,6 @@ extension ContextMenuOverlayViewMac { .onTapGesture {} Spacer() } - .fullScreen() .transition(.opacity) } @@ -135,7 +131,6 @@ extension ContextMenuOverlayViewMac { .padding(.leading, viewModel.upperContentViewLocation.x) Spacer() } - .fullScreen() .transition(.opacity) } } From 5948b155c20b9a2be28e79bb6a248e6562423032 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Tue, 27 May 2025 22:08:38 +0200 Subject: [PATCH 34/45] [trello.com/c/pRsTtPYQ] add failed status for incorrect media message --- Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift | 5 +++++ Adamant/Modules/Chat/ViewModel/ChatViewModel.swift | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift b/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift index 9800c81ee..4d87a23fc 100644 --- a/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift +++ b/Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift @@ -14,6 +14,7 @@ enum FileMessageStatus: Hashable { case downloading case success case failed + case unableToDownload case needToDownload(failed: Bool) var image: UIImage { @@ -33,6 +34,8 @@ enum FileMessageStatus: Hashable { return .asset(named: "download-circular-error") ?? .init() } return .asset(named: "download-circular") ?? .init() + case .unableToDownload: + return .asset(named: "download-circular-error") ?? .init() } } @@ -42,6 +45,8 @@ enum FileMessageStatus: Hashable { return .adamant.primary case .failed: return .adamant.attention + case .unableToDownload: + return .adamant.warning } } } diff --git a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift index 0f5c7a581..28861ddef 100644 --- a/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift +++ b/Adamant/Modules/Chat/ViewModel/ChatViewModel.swift @@ -1734,7 +1734,7 @@ extension ChatViewModel { if model.txStatus == .failed { return .failed } - + if model.content.fileModel.files.contains(where: { $0.isUploading }) { return .uploading } @@ -1755,6 +1755,10 @@ extension ChatViewModel { return progress < 100 }) + if model.content.fileModel.files.contains(where: { $0.file.nonce.isEmpty }) { + return .unableToDownload + } + return .needToDownload(failed: failed) } From 416a4ae8623e6ac07910e474d692cd8b611b7550 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Fri, 23 May 2025 16:41:06 +0200 Subject: [PATCH 35/45] [trello.com/c/aZtAfZ44] fix select row when we enter the chat via popup --- .../ChatsList/ChatListViewController.swift | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/Adamant/Modules/ChatsList/ChatListViewController.swift b/Adamant/Modules/ChatsList/ChatListViewController.swift index b578b321d..d67f0b1d3 100644 --- a/Adamant/Modules/ChatsList/ChatListViewController.swift +++ b/Adamant/Modules/ChatsList/ChatListViewController.swift @@ -980,6 +980,7 @@ extension ChatListViewController { @MainActor func presentChatroom(_ chatroom: Chatroom, with message: String? = nil) { + defer { updateSelectedRow(chatroom: chatroom) } // MARK: 1. Create and config ViewController let vc = chatViewController(for: chatroom, with: message) @@ -1602,14 +1603,6 @@ extension ChatListViewController: UISearchBarDelegate, UISearchResultsUpdating, return } - if let indexPath = tableView.indexPathForSelectedRow { - tableView.deselectRow(at: indexPath, animated: true) - } - - if let indexPath = self?.chatsController?.indexPath(forObject: chatroom) { - tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) - } - presenter.presentChatroom(chatroom, with: message.transactionId) } } @@ -1620,14 +1613,6 @@ extension ChatListViewController: UISearchBarDelegate, UISearchResultsUpdating, return } - if let indexPath = tableView.indexPathForSelectedRow { - tableView.deselectRow(at: indexPath, animated: true) - } - - if let indexPath = self?.chatsController?.indexPath(forObject: chatroom) { - tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) - } - presenter.presentChatroom(chatroom) } } @@ -1636,6 +1621,15 @@ extension ChatListViewController: UISearchBarDelegate, UISearchResultsUpdating, account.chatroom?.isForcedVisible = true newChatController(didSelectAccount: account, preMessage: nil, name: nil) } + + func updateSelectedRow(chatroom: Chatroom) { + if let indexPath = tableView.indexPathForSelectedRow { + tableView.deselectRow(at: indexPath, animated: true) + } + if let indexPath = self.chatsController?.indexPath(forObject: chatroom) { + tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) + } + } } // MARK: Mac OS HotKeys From 01409200f843d0706e6e2337e60102fac238c150 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 28 May 2025 00:43:36 +0200 Subject: [PATCH 36/45] [trello.com/c/sG80IZan] delay badge update for current chat --- .../ChatsList/ChatListViewController.swift | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Adamant/Modules/ChatsList/ChatListViewController.swift b/Adamant/Modules/ChatsList/ChatListViewController.swift index b578b321d..f35beff16 100644 --- a/Adamant/Modules/ChatsList/ChatListViewController.swift +++ b/Adamant/Modules/ChatsList/ChatListViewController.swift @@ -79,6 +79,7 @@ final class ChatListViewController: KeyboardObservingViewController { private var chatDeselectedIndex: IndexPath? private var isScrolling = false private var isRefreshing = false + private var selectedChatRoom: Chatroom? let defaultAvatar = UIImage.asset(named: "avatar-chat-placeholder") ?? .init() @@ -140,6 +141,10 @@ final class ChatListViewController: KeyboardObservingViewController { private var loadNewChatTask: Task<(), Never>? private var subscriptions = Set() private var swipedIndex: IndexPath? + + // MARK: Subjects + private var unreadBadgeUpdateSubject = ObservableSender() + // MARK: Init init( @@ -363,6 +368,14 @@ final class ChatListViewController: KeyboardObservingViewController { self?.configureCell(cell, for: chatroom) } .store(in: &subscriptions) + + unreadBadgeUpdateSubject + .debounce(for: .seconds(currentChatBudgeUpdateDelay), scheduler: DispatchQueue.main) + .sink { [weak self] count in + guard let self, let count else { return } + self.setBadgeValue(count) + } + .store(in: &subscriptions) } @MainActor @@ -615,6 +628,7 @@ extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { vc.modalPresentationStyle = .overFullScreen present(vc, animated: true) } + self.selectedChatRoom = chatroom } } @@ -785,7 +799,15 @@ extension ChatListViewController: NSFetchedResultsControllerDelegate { tableView.endUpdates() case let c where c == unreadController: - setBadgeValue(controller.fetchedObjects?.count) + let unreadObjects = controller.fetchedObjects as? [ChatTransaction] + + if let lastUnread = unreadObjects?.max(by: { ($0.sentDate ?? .distantPast) < ($1.sentDate ?? .distantPast) }), + lastUnread.chatroom == selectedChatRoom { + unreadBadgeUpdateSubject.send(controller.fetchedObjects?.count) + } else { + unreadBadgeUpdateSubject.send(nil) + setBadgeValue(controller.fetchedObjects?.count) + } default: break @@ -1002,6 +1024,7 @@ extension ChatListViewController { present(vc, animated: true) } } + selectedChatRoom = chatroom } private func presentBuyAndSell() { let buyAndSellVC = screensFactory.makeBuyAndSell() @@ -1669,3 +1692,6 @@ extension UITableView { } } } + +//delay badge update for current chat to not update it if user will read it instantly +fileprivate let currentChatBudgeUpdateDelay: TimeInterval = 1.0 From fa80ffd55f62174bee322caa6ab3bed60d1ff818 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Thu, 29 May 2025 13:50:00 +0300 Subject: [PATCH 37/45] [trello.com/c/SL4zLZnb] Small code improvments --- Adamant/Models/BTCRawTransaction.swift | 2 +- .../BtcWalletService+RichMessageProviderWithStatusCheck.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Adamant/Models/BTCRawTransaction.swift b/Adamant/Models/BTCRawTransaction.swift index 01f1c0d32..3eefa4451 100644 --- a/Adamant/Models/BTCRawTransaction.swift +++ b/Adamant/Models/BTCRawTransaction.swift @@ -27,7 +27,7 @@ struct BTCRawTransaction { func asBtcTransaction(_ as: T.Type, for address: String, blockId: String? = nil) -> T { // MARK: Known values - var confirmationsValue: String? = nil + var confirmationsValue: String? var transactionStatus: TransactionStatus = .registered if let confirmations = confirmations { diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift index 850a7759c..1dec21c38 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+RichMessageProviderWithStatusCheck.swift @@ -44,8 +44,8 @@ extension BtcWalletService { transaction: CoinTransaction, btcTransaction: BtcTransaction ) async -> TransactionStatus { - // Before we already stored another status in RawBtcTransactionResponse.asBtcTransaction(_:for:height:). - // So we don't need to double check anything here because it is already at least "registered". + // The status was already set in RawBtcTransactionResponse.asBtcTransaction(_:for:height:), + // so there's no need to check again for `.registered` — at this point it's guaranteed to be at least that. guard let status = btcTransaction.transactionStatus else { return .inconsistent(.unknown) } From 07f32f4f0073656b5f902d82d761c5bc1d5cc6fc Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Fri, 30 May 2025 19:17:04 +0300 Subject: [PATCH 38/45] Dependency update --- .../xcshareddata/swiftpm/Package.resolved | 83 ++++++++++--------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/Adamant.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Adamant.xcworkspace/xcshareddata/swiftpm/Package.resolved index b147e3738..99e92dc0c 100644 --- a/Adamant.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Adamant.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/attaswift/BigInt.git", "state": { "branch": null, - "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version": "5.3.0" + "revision": "793a7fac0bfc318e85994bf6900652e827aef33e", + "version": "5.4.1" } }, { @@ -96,7 +96,7 @@ "repositoryURL": "https://github.com/Finalet/Elegant-Emoji-Picker", "state": { "branch": "main", - "revision": "71d2d46092b4d550cc593614efc06438f845f6e6", + "revision": "12c1a2be1adbe7a774ebdd2c48f02d95b8884df6", "version": null } }, @@ -132,8 +132,8 @@ "repositoryURL": "https://github.com/google/GoogleDataTransport.git", "state": { "branch": null, - "revision": "aae45a320fd0d11811820335b1eabc8753902a40", - "version": "9.2.5" + "revision": "a637d318ae7ae246b02d7305121275bc75ed5565", + "version": "9.4.0" } }, { @@ -141,8 +141,8 @@ "repositoryURL": "https://github.com/google/GoogleUtilities.git", "state": { "branch": null, - "revision": "bc27fad73504f3d4af235de451f02ee22586ebd3", - "version": "7.12.1" + "revision": "57a1d307f42df690fdef2637f3e5b776da02aad6", + "version": "7.13.3" } }, { @@ -168,8 +168,8 @@ "repositoryURL": "https://github.com/nathantannar4/InputBarAccessoryView", "state": { "branch": null, - "revision": "17ced92a5dccb36512b408b6276353631d7cbe57", - "version": "6.3.0" + "revision": "c7acd5fa32e54cd7a44ad7eb9a7730da35b54c95", + "version": "6.5.1" } }, { @@ -186,8 +186,8 @@ "repositoryURL": "https://github.com/firebase/leveldb.git", "state": { "branch": null, - "revision": "9d108e9112aa1d65ce508facf804674546116d9c", - "version": "1.22.3" + "revision": "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version": "1.22.5" } }, { @@ -204,8 +204,8 @@ "repositoryURL": "https://github.com/MessageKit/MessageKit.git", "state": { "branch": null, - "revision": "1993e8e90d4e9a61b8d9bc8ceb733964ce943376", - "version": "4.2.0" + "revision": "b024c1287ac30fb6ea2ee07169e6193efc11d972", + "version": "4.3.0" } }, { @@ -222,8 +222,8 @@ "repositoryURL": "https://github.com/krzyzanowskim/OpenSSL.git", "state": { "branch": null, - "revision": "0c70e4b7d22411a7fe3ff59b913d5b760b735ce1", - "version": "1.1.2100" + "revision": "8cb1d641ab5ebce2cd7cf31c93baef07bed672d4", + "version": "1.1.2301" } }, { @@ -231,7 +231,7 @@ "repositoryURL": "https://github.com/rechsteiner/Parchment", "state": { "branch": "main", - "revision": "13aad3e9308e8a4e38cb579430dbf66465418777", + "revision": "dfb23ea5118ca8bfbc578065627fccf4ec4a362e", "version": null } }, @@ -258,8 +258,8 @@ "repositoryURL": "https://github.com/google/promises.git", "state": { "branch": null, - "revision": "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", - "version": "2.3.1" + "revision": "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version": "2.4.0" } }, { @@ -298,6 +298,15 @@ "version": "5.1.0" } }, + { + "package": "secp256k1", + "repositoryURL": "https://github.com/GigaBitcoin/secp256k1.swift", + "state": { + "branch": null, + "revision": "48fb20fce4ca3aad89180448a127d5bc16f0e44c", + "version": "0.10.0" + } + }, { "package": "ShellOut", "repositoryURL": "https://github.com/JohnSundell/ShellOut", @@ -321,8 +330,8 @@ "repositoryURL": "https://github.com/SnapKit/SnapKit.git", "state": { "branch": null, - "revision": "f222cbdf325885926566172f6f5f06af95473158", - "version": "5.6.0" + "revision": "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version": "5.7.1" } }, { @@ -330,7 +339,7 @@ "repositoryURL": "https://github.com/socketio/socket.io-client-swift", "state": { "branch": "master", - "revision": "175da8b5156f6b132436f0676cc84c2f6a766b6e", + "revision": "42da871d9369f290d6ec4930636c40672143905b", "version": null } }, @@ -348,8 +357,8 @@ "repositoryURL": "https://github.com/daltoniam/Starscream", "state": { "branch": null, - "revision": "ac6c0fc9da221873e01bd1a0d4818498a71eef33", - "version": "4.0.6" + "revision": "c6bfd1af48efcc9a9ad203665db12375ba6b145a", + "version": "4.0.8" } }, { @@ -357,8 +366,8 @@ "repositoryURL": "https://github.com/apple/swift-async-algorithms", "state": { "branch": null, - "revision": "5c8bd186f48c16af0775972700626f0b74588278", - "version": "1.0.2" + "revision": "042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b", + "version": "1.0.4" } }, { @@ -366,8 +375,8 @@ "repositoryURL": "https://github.com/apple/swift-collections.git", "state": { "branch": null, - "revision": "671108c96644956dddcd89dd59c203dcdb36cec7", - "version": "1.1.4" + "revision": "c1805596154bb3a265fd91b8ac0c4433b4348fb0", + "version": "1.2.0" } }, { @@ -375,8 +384,8 @@ "repositoryURL": "https://github.com/apple/swift-protobuf.git", "state": { "branch": null, - "revision": "07f7f26ded8df9645c072f220378879c4642e063", - "version": "1.25.1" + "revision": "102a647b573f60f73afdce5613a51d71349fe507", + "version": "1.30.0" } }, { @@ -393,8 +402,8 @@ "repositoryURL": "https://github.com/EFPrefix/swift_qrcodejs.git", "state": { "branch": null, - "revision": "817ba220a2eba840bae888e7eeb11207bec05f8c", - "version": "2.3.0" + "revision": "d1605333f7edac39b4518538ef4f2638fdd2e4d6", + "version": "2.3.1" } }, { @@ -402,8 +411,8 @@ "repositoryURL": "https://github.com/qonto/SwiftyMocky", "state": { "branch": null, - "revision": "a53a970ffbb18527f307069bc1109aad16a86522", - "version": "4.2.2" + "revision": "6ca423fd8d2a82047d831e80638fcc8ac52367f6", + "version": "4.2.3" } }, { @@ -420,8 +429,8 @@ "repositoryURL": "https://github.com/skywinder/web3swift.git", "state": { "branch": null, - "revision": "8a026108ae5ff730ac83e9b574c8cf1c14413c94", - "version": "3.2.2" + "revision": "119d179305788687e80083671698b3f08c970e7f", + "version": "3.3.0" } }, { @@ -438,8 +447,8 @@ "repositoryURL": "https://github.com/jpsim/Yams", "state": { "branch": null, - "revision": "b4b8042411dc7bbb696300a34a4bf3ba1b7ad19b", - "version": "5.3.1" + "revision": "3d6871d5b4a5cd519adf233fbb576e0a2af71c17", + "version": "5.4.0" } } ] From 86ae216b748620cce9483f8f5f86f1e2b7b2a217 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 4 Jun 2025 12:20:54 +0200 Subject: [PATCH 39/45] [trello.com/c/jKqpxiCa] fix chatbot loaded message setBottomOffset --- .../Subviews/ChatMessagesCollection.swift | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Adamant/Modules/Chat/View/Subviews/ChatMessagesCollection.swift b/Adamant/Modules/Chat/View/Subviews/ChatMessagesCollection.swift index ea09b4b33..73c305407 100644 --- a/Adamant/Modules/Chat/View/Subviews/ChatMessagesCollection.swift +++ b/Adamant/Modules/Chat/View/Subviews/ChatMessagesCollection.swift @@ -44,7 +44,11 @@ final class ChatMessagesCollectionView: MessagesCollectionView { } func reloadData(newIds: [String], isOnBottom: Bool) { - let hasNewMessagesAtTop = newIds.first != currentIds.first + //Check for non-UUID for messages so that bot generated messages are not included in comparison + let topNewId = firstNonUUID(from: newIds) + let topCurrentId = firstNonUUID(from: currentIds) + + let hasNewMessagesAtTop = topNewId != topCurrentId let hasNewMessagesAtBottom = newIds.last != currentIds.last let hasSameOrMoreMessages = newIds.count >= currentIds.count @@ -84,22 +88,24 @@ final class ChatMessagesCollectionView: MessagesCollectionView { func stopDecelerating() { setContentOffset(contentOffset, animated: false) } + + } -extension ChatMessagesCollectionView { - fileprivate var maxVerticalOffset: CGFloat { +fileprivate extension ChatMessagesCollectionView { + var maxVerticalOffset: CGFloat { contentSize.height + fullInsets.bottom - bounds.height } - fileprivate var minVerticalOffset: CGFloat { + var minVerticalOffset: CGFloat { -fullInsets.top } - fileprivate var scrollGestureRecognizers: [UIGestureRecognizer] { + var scrollGestureRecognizers: [UIGestureRecognizer] { [panGestureRecognizer, pinchGestureRecognizer].compactMap { $0 } } - fileprivate var hasActiveScrollGestures: Bool { + var hasActiveScrollGestures: Bool { scrollGestureRecognizers.contains { switch $0.state { case .began, .changed: @@ -112,13 +118,13 @@ extension ChatMessagesCollectionView { } } - fileprivate func applyNewIds(_ newIds: [String]) { + func applyNewIds(_ newIds: [String]) { reloadData() layoutIfNeeded() currentIds = newIds } - fileprivate func setVerticalContentOffset(_ offset: CGFloat, safely: Bool) { + func setVerticalContentOffset(_ offset: CGFloat, safely: Bool) { guard maxVerticalOffset > minVerticalOffset else { return } var offset = offset @@ -132,4 +138,12 @@ extension ChatMessagesCollectionView { contentOffset.y = offset } + + func isUUID(_ id: String) -> Bool { + id.contains("-") && id.count >= 36 + } + + func firstNonUUID(from ids: [String]) -> String? { + ids.first(where: { !isUUID($0) }) + } } From 26663a7fdf9601782ee5f61627ec899eaf3b2b36 Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 4 Jun 2025 12:56:25 +0200 Subject: [PATCH 40/45] update wallets jsons --- .../JsonStore/general/adamant/info.json | 59 +++++++++++++++++++ .../JsonStore/general/bitcoin/info.json | 8 +++ .../JsonStore/general/dash/info.json | 8 +++ .../JsonStore/general/doge/info.json | 12 ++++ .../JsonStore/general/ethereum/info.json | 8 +++ 5 files changed, 95 insertions(+) diff --git a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/adamant/info.json b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/adamant/info.json index a2d97dee4..4ef0c7cd9 100644 --- a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/adamant/info.json +++ b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/adamant/info.json @@ -79,6 +79,25 @@ { "url": "https://debate.bbry.org", "alt_ip": "http://54.197.36.175:56666" + }, + { + "url": "https://lake.bbry.org", + "alt_ip": "http://100.42.184.57:27579" + }, + { + "url": "https://sunshine.bbry.org", + "alt_ip": "http://23.105.226.67:28785" + }, + { + "url": "https://unusual.bbry.org", + "alt_ip": "http://51.15.238.199:13682" + }, + { + "url": "https://clown.bbry.org", + "alt_ip": "http://65.108.52.254:6687" + }, + { + "url": "http://100.42.184.57:27579" } ], "healthCheck": { @@ -106,6 +125,14 @@ { "url": "https://info2.adm.im", "alt_ip": "http://207.180.210.95:33088" + }, + { + "url": "https://currencyinfo1.bbry.org", + "alt_ip": "http://147.93.132.93:1948" + }, + { + "url": "https://currencyinfo2.bbry.org", + "alt_ip": "http://51.15.238.199:18958" } ], "healthCheck": { @@ -134,6 +161,18 @@ { "url": "https://ipfs6.adamant.business", "alt_ip": "http://75.119.138.235:44099" + }, + { + "url": "https://ipfs4.bbry.org", + "alt_ip": "http://65.108.52.254:61399" + }, + { + "url": "https://ipfs5.bbry.org", + "alt_ip": "http://23.105.226.67:41511" + }, + { + "url": "https://ipfs6.bbry.org", + "alt_ip": "http://100.42.184.57:56777" } ], "healthCheck": { @@ -206,6 +245,14 @@ { "url": "https://info2.adm.im", "alt_ip": "http://207.180.210.95:33088" + }, + { + "url": "https://currencyinfo1.bbry.org", + "alt_ip": "http://147.93.132.93:1948" + }, + { + "url": "https://currencyinfo2.bbry.org", + "alt_ip": "http://51.15.238.199:18958" } ], "healthCheck": { @@ -234,6 +281,18 @@ { "url": "https://ipfs6.adamant.business", "alt_ip": "http://75.119.138.235:44099" + }, + { + "url": "https://ipfs4.bbry.org", + "alt_ip": "http://65.108.52.254:61399" + }, + { + "url": "https://ipfs5.bbry.org", + "alt_ip": "http://23.105.226.67:41511" + }, + { + "url": "https://ipfs6.bbry.org", + "alt_ip": "http://100.42.184.57:56777" } ], "healthCheck": { diff --git a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/bitcoin/info.json b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/bitcoin/info.json index a3dbb7cb1..9ff071a2f 100644 --- a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/bitcoin/info.json +++ b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/bitcoin/info.json @@ -65,6 +65,14 @@ { "url": "https://btcnode3.adamant.im", "alt_ip": "http://195.201.242.108:44099" + }, + { + "url": "https://btcnode1.bbry.org", + "alt_ip": "http://100.42.184.57:8617" + }, + { + "url": "https://btcnode3.bbry.org", + "alt_ip": "http://23.105.226.67:39914" } ], "healthCheck": { diff --git a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/dash/info.json b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/dash/info.json index c865d7082..022b37d29 100644 --- a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/dash/info.json +++ b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/dash/info.json @@ -41,6 +41,14 @@ { "url": "https://dashnode2.adamant.im", "alt_ip": "http://207.180.210.95:44099" + }, + { + "url": "https://dashnode1.bbry.org", + "alt_ip": "http://23.105.226.67:45272" + }, + { + "url": "https://dashnode2.bbry.org", + "alt_ip": "http://51.15.238.199:50353" } ], "healthCheck": { diff --git a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/doge/info.json b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/doge/info.json index b5d79ad7c..ddc6361be 100644 --- a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/doge/info.json +++ b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/doge/info.json @@ -44,6 +44,18 @@ { "url": "https://dogenode3.adm.im", "alt_ip": "http://95.216.45.88:44098" + }, + { + "url": "https://dogenode1.bbry.org", + "alt_ip": "http://147.93.132.93:14778" + }, + { + "url": "https://dogenode2.bbry.org", + "alt_ip": "http://51.15.238.199:7954" + }, + { + "url": "https://dogenode3.bbry.org", + "alt_ip": "http://65.108.52.254:25556" } ], "healthCheck": { diff --git a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/ethereum/info.json b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/ethereum/info.json index e35846523..f5b6dd376 100644 --- a/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/ethereum/info.json +++ b/AdamantWalletsKit/Sources/AdamantWalletsKit/JsonStore/general/ethereum/info.json @@ -69,6 +69,14 @@ { "url": "https://ethnode3.adamant.im", "alt_ip": "http://46.4.37.157:44099" + }, + { + "url": "https://ethnode2.bbry.org", + "alt_ip": "http://65.108.52.254:41311" + }, + { + "url": "https://ethnode3.bbry.org", + "alt_ip": "http://100.42.184.57:7318" } ], "healthCheck": { From c919ae0b7bb5eafa41ce9fd7a71b3ee0387c955e Mon Sep 17 00:00:00 2001 From: Vladimir Klevtsov Date: Wed, 4 Jun 2025 16:31:17 +0200 Subject: [PATCH 41/45] [trello.com/c/1lX3h2xx] fix eth transaction show 0 instead of pending --- Adamant/Models/EthTransaction.swift | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Adamant/Models/EthTransaction.swift b/Adamant/Models/EthTransaction.swift index b544b3634..b781c89d1 100644 --- a/Adamant/Models/EthTransaction.swift +++ b/Adamant/Models/EthTransaction.swift @@ -50,7 +50,7 @@ struct EthTransaction: @unchecked Sendable { let from: String let to: String let gasUsed: Decimal? - let gasPrice: Decimal + let gasPrice: Decimal? let confirmations: String? let isError: Bool let receiptStatus: TransactionReceipt.TXStatus @@ -149,10 +149,10 @@ extension EthTransaction: TransactionDetails { var feeCurrencySymbol: String? { EthWalletService.currencySymbol } var feeValue: Decimal? { - guard let gasUsed = gasUsed else { - return nil - } - + guard let gasUsed = gasUsed, + let gasPrice + else { return nil } + return gasPrice * gasUsed } @@ -207,12 +207,14 @@ extension CodableTransaction { txValue = nil } - let feePrice: BigUInt - if type == .eip1559 { - feePrice = (maxFeePerGas ?? BigUInt(0)) + (maxPriorityFeePerGas ?? BigUInt(0)) - } else { - feePrice = gasPrice ?? BigUInt(0) - } + let feePrice: BigUInt? = { + if type == .eip1559 { + guard let maxFeePerGas, let maxPriorityFeePerGas else { return nil } + return maxFeePerGas + maxPriorityFeePerGas + } else { + return gasPrice + } + }() let gasPrice = gasPrice ?? feePrice @@ -223,7 +225,7 @@ extension CodableTransaction { from: sender?.address ?? "", to: recipient.address, gasUsed: gasUsed?.asDecimal(exponent: 0), - gasPrice: gasPrice.asDecimal(exponent: EthWalletService.currencyExponent), + gasPrice: gasPrice?.asDecimal(exponent: EthWalletService.currencyExponent), confirmations: confirmations, isError: receiptStatus != .failed, receiptStatus: receiptStatus, From 39e96710b833a66297cd72a61ea746c53e0dcef2 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Thu, 29 May 2025 18:21:28 +0300 Subject: [PATCH 42/45] [trello.com/c/FUnqFz3u] Improve dialogs in crypto sending screens --- .../ComplexTransferViewController.swift | 20 +------------ .../Wallets/TransferViewControllerBase.swift | 28 ++++++++++--------- .../Localization/de.lproj/Localizable.strings | 3 ++ .../Localization/en.lproj/Localizable.strings | 4 +-- .../Localization/ru.lproj/Localizable.strings | 3 ++ .../Localization/zh.lproj/Localizable.strings | 3 ++ 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/Adamant/Modules/ChatsList/ComplexTransferViewController.swift b/Adamant/Modules/ChatsList/ComplexTransferViewController.swift index fb9e1a125..f1f5c07ea 100644 --- a/Adamant/Modules/ChatsList/ComplexTransferViewController.swift +++ b/Adamant/Modules/ChatsList/ComplexTransferViewController.swift @@ -146,26 +146,8 @@ extension ComplexTransferViewController: PagingViewControllerDataSource { vc.recipientIsReadonly = true vc.commentsEnabled = service.core.commentsEnabledForRichMessages && partner?.isDummy != true vc.showProgressView(animated: false) - + Task { - guard service.core.hasEnabledNode else { - vc.showAlertView( - message: ApiServiceError.noEndpointsAvailable( - nodeGroupName: service.core.tokenName - ).errorDescription ?? .adamant.sharedErrors.unknownError, - animated: true - ) - return - } - - guard admService?.core.hasEnabledNode ?? false else { - vc.showAlertView( - message: .adamant.sharedErrors.admNodeErrorMessage(service.core.tokenSymbol), - animated: true - ) - return - } - do { let walletAddress = try await service.core .getWalletAddress( diff --git a/Adamant/Modules/Wallets/TransferViewControllerBase.swift b/Adamant/Modules/Wallets/TransferViewControllerBase.swift index f9eacd942..ddcab9e65 100644 --- a/Adamant/Modules/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Modules/Wallets/TransferViewControllerBase.swift @@ -74,6 +74,9 @@ extension String.adamant { static var unknownToken: String { String.localized("Transaction.UnknownTokenTitle", comment: "Transaction: Unknown token") } + static var noActiveMessagingNodesTitle: String { + String.localized("TransferScene.NoActiveMessagingNodes", comment: "Message to show when no active messaging nodes dialog") + } } } @@ -411,6 +414,7 @@ class TransferViewControllerBase: FormViewController { .store(in: &subscriptions) walletCore.walletUpdatePublisher + .receive(on: DispatchQueue.main) .sink { @MainActor [weak self] _ in self?.reloadFormData() } @@ -821,20 +825,9 @@ class TransferViewControllerBase: FormViewController { dialogService.showWarning(withMessage: .adamant.sharedErrors.networkError) return } - - guard - apiServiceCompose.get(.adm)?.hasEnabledNode == true || admReportRecipient == nil - else { - dialogService.showWarning( - withMessage: ApiServiceError.noEndpointsAvailable( - nodeGroupName: NodeGroup.adm.name - ).localizedDescription - ) - return - } - + for group in walletCore.nodeGroups { - guard apiServiceCompose.get(group)?.hasEnabledNode == true else { + guard apiServiceCompose.get(group)?.hasSupportedNode == true else { dialogService.showWarning( withMessage: ApiServiceError.noEndpointsAvailable( nodeGroupName: group.name @@ -844,6 +837,15 @@ class TransferViewControllerBase: FormViewController { } } + guard + apiServiceCompose.get(.adm)?.hasSupportedNode == true || admReportRecipient == nil + else { + dialogService.showWarning( + withMessage: Self.noActiveMessagingNodesTitle.localizedDescription + ) + return + } + var recipient: String = recipientAddress if let recipientName, recipientName != recipientAddress { diff --git a/CommonKit/Sources/CommonKit/Assets/Localization/de.lproj/Localizable.strings b/CommonKit/Sources/CommonKit/Assets/Localization/de.lproj/Localizable.strings index 7e70d1f25..126d549fb 100644 --- a/CommonKit/Sources/CommonKit/Assets/Localization/de.lproj/Localizable.strings +++ b/CommonKit/Sources/CommonKit/Assets/Localization/de.lproj/Localizable.strings @@ -1267,6 +1267,9 @@ /* Transfer: Processing message */ "TransferScene.SendingFundsProgress" = "Senden…"; +/* Transfer: Title text when it is no available messaging (ADM) nodes */ +"TransferScene.NoActiveMessagingNodes" = "Keine aktiven Messaging-Knoten. Überprüfen Sie die ADM-Knotenliste."; + /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "Fertig!"; diff --git a/CommonKit/Sources/CommonKit/Assets/Localization/en.lproj/Localizable.strings b/CommonKit/Sources/CommonKit/Assets/Localization/en.lproj/Localizable.strings index 01e99f3db..df8b46000 100644 --- a/CommonKit/Sources/CommonKit/Assets/Localization/en.lproj/Localizable.strings +++ b/CommonKit/Sources/CommonKit/Assets/Localization/en.lproj/Localizable.strings @@ -1243,8 +1243,8 @@ /* Transfer: Processing message */ "TransferScene.SendingFundsProgress" = "Sending tokens…"; -/* Transfer: Tokens transfered successfully message */ -"TransferScene.TransferSuccessMessage" = "Done!"; +/* Transfer: Title text when it is no available messaging (ADM) nodes */ +"TransferScene.NoActiveMessagingNodes" = "No active messaging nodes. Review ADM node list."; /* Wallet Services: Shared error, user do not have enought money. */ "WalletServices.SharedErrors.notEnoughMoney" = "Not enough tokens"; diff --git a/CommonKit/Sources/CommonKit/Assets/Localization/ru.lproj/Localizable.strings b/CommonKit/Sources/CommonKit/Assets/Localization/ru.lproj/Localizable.strings index 055ab24ca..7068bb1ad 100644 --- a/CommonKit/Sources/CommonKit/Assets/Localization/ru.lproj/Localizable.strings +++ b/CommonKit/Sources/CommonKit/Assets/Localization/ru.lproj/Localizable.strings @@ -1244,6 +1244,9 @@ /* Transfer: Processing message */ "TransferScene.SendingFundsProgress" = "Отправляем токены…"; +/* Transfer: Title text when it is no available messaging (ADM) nodes */ +"TransferScene.NoActiveMessagingNodes" = "Нет доступных нод ADM, чтобы отправить сообщение. Посмотрите список узлов."; + /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "Готово!"; diff --git a/CommonKit/Sources/CommonKit/Assets/Localization/zh.lproj/Localizable.strings b/CommonKit/Sources/CommonKit/Assets/Localization/zh.lproj/Localizable.strings index 888a6fc5b..5d6f81a7f 100644 --- a/CommonKit/Sources/CommonKit/Assets/Localization/zh.lproj/Localizable.strings +++ b/CommonKit/Sources/CommonKit/Assets/Localization/zh.lproj/Localizable.strings @@ -1233,6 +1233,9 @@ /* Transfer: Processing message */ "TransferScene.SendingFundsProgress" = "正在发送令牌…"; +/* Transfer: Title text when it is no available messaging (ADM) nodes */ +"TransferScene.NoActiveMessagingNodes" = "没有活动的消息节点。请查看 ADM 节点列表。"; + /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "完成!"; From 5f6938f81e4d8d4235dd58b2969d50fe91501dca Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Thu, 5 Jun 2025 19:26:57 +0300 Subject: [PATCH 43/45] [trello.com/c/FUnqFz3u] Syntax error fix --- Adamant/Modules/Wallets/TransferViewControllerBase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adamant/Modules/Wallets/TransferViewControllerBase.swift b/Adamant/Modules/Wallets/TransferViewControllerBase.swift index ddcab9e65..e55d8de0b 100644 --- a/Adamant/Modules/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Modules/Wallets/TransferViewControllerBase.swift @@ -841,7 +841,7 @@ class TransferViewControllerBase: FormViewController { apiServiceCompose.get(.adm)?.hasSupportedNode == true || admReportRecipient == nil else { dialogService.showWarning( - withMessage: Self.noActiveMessagingNodesTitle.localizedDescription + withMessage: String.adamant.transfer.noActiveMessagingNodesTitle ) return } From 2ecacaed7f13388f4809f4c9a55778114d0629a9 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Thu, 5 Jun 2025 19:34:49 +0300 Subject: [PATCH 44/45] [trello.com/c/IE07BHsc] Add badge for the MacOS/iPad app --- .../Services/AdamantNotificationService.swift | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Adamant/Services/AdamantNotificationService.swift b/Adamant/Services/AdamantNotificationService.swift index eb98fbb36..d8690050d 100644 --- a/Adamant/Services/AdamantNotificationService.swift +++ b/Adamant/Services/AdamantNotificationService.swift @@ -249,7 +249,7 @@ extension AdamantNotificationsService { if let number = type.badge { DispatchQueue.onMainThreadSyncSafe { - content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + backgroundNotifications + number) + content.badge = NSNumber(value: isMacOS ? getBadgeForLeftSplitViewController() ?? 0: UIApplication.shared.applicationIconBadgeNumber + backgroundNotifications + number) } if isBackgroundSession { @@ -280,24 +280,59 @@ extension AdamantNotificationsService { SecureStore.remove(StoreKey.notificationsService.customBadgeNumber) } - DispatchQueue.onMainAsync { + DispatchQueue.onMainAsync { [weak self] in UIApplication.shared.applicationIconBadgeNumber = appIconBadgeNumber + + guard let self = self else { return } + self.setBadgeForLeftSplitViewController(count: appIconBadgeNumber) } } func removeAllPendingNotificationRequests() { UNUserNotificationCenter.current().removeAllPendingNotificationRequests() UIApplication.shared.applicationIconBadgeNumber = customBadgeNumber + setBadgeForLeftSplitViewController(count: customBadgeNumber) } func removeAllDeliveredNotifications() { UNUserNotificationCenter.current().removeAllDeliveredNotifications() UIApplication.shared.applicationIconBadgeNumber = customBadgeNumber + setBadgeForLeftSplitViewController(count: customBadgeNumber) } func setValue(for key: String, value: Bool) { SecureStore.set(value, for: key) } + + private func setBadgeForLeftSplitViewController(count: Int) { + if let window = UIApplication.shared + .connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .first? + .windows + .first(where: \.isKeyWindow), + let tabBar = window.rootViewController as? UITabBarController, + let split = tabBar.viewControllers?.first(where: { $0 is UISplitViewController }) as? UISplitViewController { + split.tabBarItem.badgeValue = count > 0 ? String(count) : nil + } + } + + private func getBadgeForLeftSplitViewController() -> Int? { + if let window = UIApplication.shared + .connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .first? + .windows + .first(where: \.isKeyWindow), + let tabBar = window.rootViewController as? UITabBarController, + let split = tabBar.viewControllers?.first(where: { $0 is UISplitViewController }) as? UISplitViewController { + if let badgeValue = split.tabBarItem.badgeValue, let badge = Int(badgeValue) { + return badge + } + return nil + } + return nil + } } // MARK: - Background batch notifications @@ -317,6 +352,7 @@ extension AdamantNotificationsService { fileprivate func onUserLoggedIn() { UNUserNotificationCenter.current().removeAllDeliveredNotifications() UIApplication.shared.applicationIconBadgeNumber = 0 + setBadgeForLeftSplitViewController(count: 0) if let raw: String = SecureStore.get(StoreKey.notificationsService.notificationsMode), let mode = NotificationsMode(string: raw) From 36dbed37f3fdfde5ef49ca4ab7bc244071c4f9e5 Mon Sep 17 00:00:00 2001 From: Dmitrij Meidus Date: Thu, 12 Jun 2025 18:24:03 +0300 Subject: [PATCH 45/45] [trello.com/c/9lwbzVAp] Use suffixes (displayName like "eth-indexer") from `adamant-wallets` in the nodes/services screens --- Adamant/Helpers/NodeGroup+Constants.swift | 14 +++++++------- .../CoinsNodesList/CoinsNodesListFactory.swift | 8 ++++---- .../Klayr/WalletService/KlyWalletService.swift | 4 ++++ .../WalletsService/WalletStaticCoreProtocol.swift | 4 ++++ .../AdamantWalletsKit/Decoders/CoinInfoDTO.swift | 2 ++ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Adamant/Helpers/NodeGroup+Constants.swift b/Adamant/Helpers/NodeGroup+Constants.swift index 3c489ae19..4f0b5bb4a 100644 --- a/Adamant/Helpers/NodeGroup+Constants.swift +++ b/Adamant/Helpers/NodeGroup+Constants.swift @@ -129,20 +129,20 @@ extension NodeGroup { var name: String { switch self { case .btc: - return BtcWalletService.tokenNetworkSymbol + return BtcWalletService.nodeDisplayName ?? BtcWalletService.tokenNetworkSymbol case .eth: - return EthWalletService.tokenNetworkSymbol + return EthWalletService.nodeDisplayName ?? EthWalletService.tokenNetworkSymbol case .klyNode: - return KlyWalletService.tokenNetworkSymbol + return KlyWalletService.nodeDisplayName ?? KlyWalletService.tokenNetworkSymbol case .klyService: - return KlyWalletService.tokenNetworkSymbol + return KlyWalletService.indexerDisplayName ?? KlyWalletService.tokenNetworkSymbol + " " + .adamant.coinsNodesList.serviceNode case .doge: - return DogeWalletService.tokenNetworkSymbol + return DogeWalletService.nodeDisplayName ?? DogeWalletService.tokenNetworkSymbol case .dash: - return DashWalletService.tokenNetworkSymbol + return DashWalletService.nodeDisplayName ?? DashWalletService.tokenNetworkSymbol case .adm: - return AdmWalletService.tokenNetworkSymbol + return AdmWalletService.nodeDisplayName ?? AdmWalletService.tokenNetworkSymbol case .ipfs: return IPFSApiService.symbol case .infoService: diff --git a/Adamant/Modules/CoinsNodesList/CoinsNodesListFactory.swift b/Adamant/Modules/CoinsNodesList/CoinsNodesListFactory.swift index c820cc779..8757fb958 100644 --- a/Adamant/Modules/CoinsNodesList/CoinsNodesListFactory.swift +++ b/Adamant/Modules/CoinsNodesList/CoinsNodesListFactory.swift @@ -31,10 +31,10 @@ struct CoinsNodesListFactory { let view = CoinsNodesListView(viewModel: viewModel) switch context { - case .login: - return SelfRemovableHostingController(rootView: view) - case .menu: - return UIHostingController(rootView: view) + case .login: + return SelfRemovableHostingController(rootView: view) + case .menu: + return UIHostingController(rootView: view) } } } diff --git a/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift b/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift index bad47dd78..d31b43bca 100644 --- a/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift +++ b/Adamant/Modules/Wallets/Klayr/WalletService/KlyWalletService.swift @@ -56,6 +56,10 @@ final class KlyWalletService: WalletCoreProtocol, WalletStaticCoreProtocol, @unc var balanceValidInterval: Int? { Self.balanceValidInterval } + + static var indexerDisplayName: String? { + coinInfo?.services?.klyService?.displayName + } @MainActor var hasEnabledNode: Bool { diff --git a/Adamant/Modules/Wallets/WalletsService/WalletStaticCoreProtocol.swift b/Adamant/Modules/Wallets/WalletsService/WalletStaticCoreProtocol.swift index 398edc154..df3fe834b 100644 --- a/Adamant/Modules/Wallets/WalletsService/WalletStaticCoreProtocol.swift +++ b/Adamant/Modules/Wallets/WalletsService/WalletStaticCoreProtocol.swift @@ -151,4 +151,8 @@ extension WalletStaticCoreProtocol { ) } ?? [] } + + static var nodeDisplayName: String? { + coinInfo?.nodes?.displayName + } } diff --git a/AdamantWalletsKit/Sources/AdamantWalletsKit/Decoders/CoinInfoDTO.swift b/AdamantWalletsKit/Sources/AdamantWalletsKit/Decoders/CoinInfoDTO.swift index beb4daf5f..7b0bc8e5e 100644 --- a/AdamantWalletsKit/Sources/AdamantWalletsKit/Decoders/CoinInfoDTO.swift +++ b/AdamantWalletsKit/Sources/AdamantWalletsKit/Decoders/CoinInfoDTO.swift @@ -66,6 +66,7 @@ public struct CoinInfoDTO: Codable { } public struct Service: Codable { + public let displayName: String? let description: Description public let list: [Node] public let healthCheck: NodeHealthCheck? @@ -79,6 +80,7 @@ public struct CoinInfoDTO: Codable { } public struct Nodes: Codable { + public let displayName: String? public let list: [Node] public let healthCheck: NodeHealthCheck public let minVersion: String?