From 6ac415fc7283a8661df73b215451c3cef0f67378 Mon Sep 17 00:00:00 2001 From: Antonio Bello <1085903+jeden@users.noreply.github.com> Date: Mon, 16 Dec 2024 05:10:50 +0100 Subject: [PATCH 1/2] 564 Insufficient Balance Alert --- FRW/Services/Manager/Config/RemoteConfig.swift | 3 +++ FRW/Services/Manager/WalletManager.swift | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/FRW/Services/Manager/Config/RemoteConfig.swift b/FRW/Services/Manager/Config/RemoteConfig.swift index 8aac7f62..5da41ce9 100644 --- a/FRW/Services/Manager/Config/RemoteConfig.swift +++ b/FRW/Services/Manager/Config/RemoteConfig.swift @@ -174,6 +174,7 @@ extension RemoteConfigManager { case unknow case canUpgrade case insufficientStorage + case insufficientBalance case isIOS case isAndroid case isWeb @@ -208,6 +209,8 @@ extension RemoteConfigManager { case .insufficientStorage: // TODO: [AB] Not very elegant adding a dependency here, but implementing in a different way would probably require major refactoring return WalletManager.shared.isStorageInsufficient + case .insufficientBalance: + return WalletManager.shared.isBalanceInsufficient default: return false } diff --git a/FRW/Services/Manager/WalletManager.swift b/FRW/Services/Manager/WalletManager.swift index 3cfcaa34..bd99aeb8 100644 --- a/FRW/Services/Manager/WalletManager.swift +++ b/FRW/Services/Manager/WalletManager.swift @@ -956,6 +956,12 @@ extension WalletManager { return accountInfo.storageCapacity - accountInfo.storageUsed < Self.mininumStorageThreshold } + var isBalanceInsufficient: Bool { + guard self.isSelectedFlowAccount else { return false } + guard let accountInfo else { return false } + return accountInfo.balance < Self.minFlowBalance + } + func isBalanceInsufficient(for amount: Decimal) -> Bool { guard self.isSelectedFlowAccount else { return false } guard let accountInfo else { return false } From 1f41cc8799bd9a7808a9b2b6ddb8dd041836a9fb Mon Sep 17 00:00:00 2001 From: Antonio Bello <1085903+jeden@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:29:41 +0100 Subject: [PATCH 2/2] - Fixed alerts not showing up due to threading issues --- .../Wallet/Message/WalletNewsHandler.swift | 183 +++++++++++------- FRW/Modules/Wallet/WalletHomeView.swift | 39 ++-- 2 files changed, 123 insertions(+), 99 deletions(-) diff --git a/FRW/Modules/Wallet/Message/WalletNewsHandler.swift b/FRW/Modules/Wallet/Message/WalletNewsHandler.swift index d0266b51..bcd3436a 100644 --- a/FRW/Modules/Wallet/Message/WalletNewsHandler.swift +++ b/FRW/Modules/Wallet/Message/WalletNewsHandler.swift @@ -8,6 +8,57 @@ import Foundation import SwiftUI +private extension Array where Element == RemoteConfigManager.News { + func appended(_ element: RemoteConfigManager.News) -> Self { + var list = self + list.append(element) + return list + } + + func appended(contentsOf news: [RemoteConfigManager.News]) -> Self { + var list = self + list.append(contentsOf: news) + return list + } + + func removeExpiryNew() -> Self { + let currentData = Date() + return filter { $0.expiryTime > currentData } + } + + func removeMarkedNews(removeIds: [String]) -> Self { + return filter { !removeIds.contains($0.id) } + } + + func handleCondition(force: Bool = false) -> Self { + return filter { model in + guard let conditionList = model.conditions, !conditionList.isEmpty, force == false else { + return true + } + return !conditionList.map { $0.type.boolValue() }.contains(false) + } + } + + func orderNews() -> Self { + return sorted(by: >) + } + + func addRemoteNews(_ news: [RemoteConfigManager.News]) -> Self { + var list = self + + for news in news { + if list.contains(where: { $0.id == news.id }) { + continue + } + + list = list + .appended(news) + } + + return list.orderNews() + } +} + // MARK: - WalletNewsHandler class WalletNewsHandler: ObservableObject { @@ -27,9 +78,8 @@ class WalletNewsHandler: ObservableObject { static let shared = WalletNewsHandler() // TODO: Change it to Set - @Published - var list: [RemoteConfigManager.News] = [] - + @MainActor @Published var list: [RemoteConfigManager.News] = [] + var removeIds: [String] = [] { didSet { LocalUserDefaults.shared.removedNewsIds = removeIds @@ -37,65 +87,68 @@ class WalletNewsHandler: ObservableObject { } /// Call only once when receive Firebase Config + @MainActor func addRemoteNews(_ news: [RemoteConfigManager.News]) { - accessQueue.sync { [weak self] in - guard let self else { return } - self.list.removeAll() - self.list.append(contentsOf: news) - self.removeExpiryNew() - self.removeMarkedNews() - self.handleCondition() - self.orderNews() + var list: [RemoteConfigManager.News] = [] + + accessQueue.sync { + list = list + .appended(contentsOf: news) + .removeExpiryNew() + .removeMarkedNews(removeIds: self.removeIds) + .handleCondition() + .orderNews() log.debug("[NEWS] count:\(list.count)") } + + self.list = list } - func addRemoteNews(_ news: RemoteConfigManager.News) { - accessQueue.sync { [weak self] in - guard let self else { return } - if list.contains(where: { $0.id == news.id }) { - return - } - - list.append(news) - orderNews() - } - } - + @MainActor func removeNews(_ news: RemoteConfigManager.News) { - accessQueue.sync { [weak self] in - guard let self else { return } - if let index = list.firstIndex(where: { $0.id == news.id }), let _ = list[safe: index] { + var list = self.list + + accessQueue.sync { + if let index = list.firstIndex(where: { $0.id == news.id }), list[safe: index] != nil { list.remove(at: index) - orderNews() } } + + self.list = list } func refreshWalletConnectNews(_ news: [RemoteConfigManager.News]) { - accessQueue.sync { [weak self] in - guard let self else { return } - let tmpList = list - for (index, new) in tmpList.enumerated() { - if new.flag == .walletconnect, let _ = list[safe: index] { - list.remove(at: index) + DispatchQueue.main.async { + var list = self.list + + self.accessQueue.sync { [weak self] in + guard let self else { return } + let tmpList = list + + for (index, new) in tmpList.enumerated() { + if new.flag == .walletconnect, list[safe: index] != nil { + list.remove(at: index) + } } + + list = list.addRemoteNews(news) } - - for item in news { - addRemoteNews(item) - } + + self.list = list } } /// Call only once when view appear + @MainActor func checkFirstNews() { - accessQueue.async(flags: .barrier) { [weak self] in + let list = self.list + accessQueue.sync(flags: .barrier) { [weak self] in guard let self else { return } if let item = list.first { markItemIfNeed(item.id, displatyType: [.once]) } } + self.list = list } // MARK: Private @@ -107,50 +160,26 @@ class WalletNewsHandler: ObservableObject { @objc private func onAccountDataUpdate() { - handleCondition(force: true) - } - - private func removeExpiryNew() { - accessQueue.sync { - let currentData = Date() - list = list.filter { $0.expiryTime > currentData } - log.debug("[NEWS] removeExpiryNew count:\(list.count)") - } - } - - private func removeMarkedNews() { - accessQueue.sync { - list = list.filter { !removeIds.contains($0.id) } - log.debug("[NEWS] removeMarkedNews count:\(list.count)") - } - } - - private func handleCondition(force: Bool = false) { - accessQueue.sync { - list = list.filter { model in - guard let conditionList = model.conditions, !conditionList.isEmpty, force == false else { - return true - } - return !conditionList.map { $0.type.boolValue() }.contains(false) + DispatchQueue.main.async { + var list = self.list + + self.accessQueue.sync(flags: .barrier) { + list = list + .handleCondition(force: true) + .orderNews() } - log.debug("[NEWS] handleConfition count:\(list.count)") + + self.list = list } } - private func orderNews() { - list.sort(by: >) - } - - @discardableResult - private func markItemIfNeed( - _ itemId: String, - displatyType: [RemoteConfigManager.NewDisplayType] = [.once] - ) -> Bool { + @discardableResult @MainActor + private func markItemIfNeed(_ itemId: String, displatyType: [RemoteConfigManager.NewDisplayType] = [.once]) -> Bool { let item = list.first { $0.id == itemId } guard let type = item?.displayType, displatyType.contains(type) else { return false } - accessQueue.async(flags: .barrier) { [weak self] in + accessQueue.sync(flags: .barrier) { [weak self] in guard let self else { return } removeIds.append(itemId) } @@ -161,10 +190,12 @@ class WalletNewsHandler: ObservableObject { // MARK: User Action extension WalletNewsHandler { + @MainActor func onShowItem(_ itemId: String) { markItemIfNeed(itemId, displatyType: [.once]) } + @MainActor func onCloseItem(_ itemId: String) { markItemIfNeed(itemId) withAnimation { @@ -172,6 +203,7 @@ extension WalletNewsHandler { } } + @MainActor func onClickItem(_ itemId: String) { guard let item = list.first(where: { $0.id == itemId }) else { return } @@ -198,6 +230,7 @@ extension WalletNewsHandler { } } + @MainActor func nextItem(_ itemId: String) -> String? { guard let index = list.firstIndex(where: { $0.id == itemId }) else { return nil @@ -209,6 +242,7 @@ extension WalletNewsHandler { return item.id } + @MainActor func nextIndex(_ itemId: String) -> Int? { guard let index = list.firstIndex(where: { $0.id == itemId }) else { return nil @@ -220,6 +254,7 @@ extension WalletNewsHandler { return nextIndex } + @MainActor func onScroll(index: Int) { guard index < list.count else { return } diff --git a/FRW/Modules/Wallet/WalletHomeView.swift b/FRW/Modules/Wallet/WalletHomeView.swift index 7e58e9ab..912205b5 100644 --- a/FRW/Modules/Wallet/WalletHomeView.swift +++ b/FRW/Modules/Wallet/WalletHomeView.swift @@ -34,35 +34,24 @@ extension WalletHomeView: AppTabBarPageProtocol { // MARK: - WalletHomeView struct WalletHomeView: View { - @State - var safeArea: EdgeInsets = .zero - @State - var size: CGSize = .zero - - @StateObject - var um = UserManager.shared - @StateObject - var wm = WalletManager.shared - @StateObject - private var vm = WalletViewModel() - @StateObject - var newsHandler = WalletNewsHandler.shared - @State - var isRefreshing: Bool = false - @State - private var showActionSheet = false + @State var safeArea: EdgeInsets = .zero + @State var size: CGSize = .zero + + @StateObject var um = UserManager.shared + @StateObject var wm = WalletManager.shared + @StateObject private var vm = WalletViewModel() + @StateObject var newsHandler = WalletNewsHandler.shared + @State var isRefreshing: Bool = false + @State private var showActionSheet = false @AppStorage("WalletCardBackrgound") private var walletCardBackrgound: String = "fade:0" - @State - var selectedNewsId: String? - @State - var scrollNext: Bool = false + @State var selectedNewsId: String? + @State var scrollNext: Bool = false private let scrollName: String = "WALLETSCROLL" - @State - private var logViewPresented: Bool = false + @State private var logViewPresented: Bool = false var body: some View { GeometryReader { proxy in @@ -283,9 +272,9 @@ struct WalletHomeView: View { StackPageView(newsHandler.list, selection: $selectedNewsId) { news in WalletNotificationView(item: news) { idStr in - if let nextId = newsHandler.nextItem(idStr) { +// if let nextId = newsHandler.nextItem(idStr) { // selectedNewsId = nextId - } +// } selectedNewsId = idStr newsHandler.onCloseItem(idStr) } onAction: { _ in