Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

564 Insufficient Balance Alert #673

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 109 additions & 74 deletions FRW/Modules/Wallet/Message/WalletNewsHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -27,75 +78,77 @@ 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
}
}

/// 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
Expand All @@ -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)
}
Expand All @@ -161,17 +190,20 @@ 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 {
list.removeAll { $0.id == itemId }
}
}

@MainActor
func onClickItem(_ itemId: String) {
guard let item = list.first(where: { $0.id == itemId }) else { return }

Expand All @@ -198,6 +230,7 @@ extension WalletNewsHandler {
}
}

@MainActor
func nextItem(_ itemId: String) -> String? {
guard let index = list.firstIndex(where: { $0.id == itemId }) else {
return nil
Expand All @@ -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
Expand All @@ -220,6 +254,7 @@ extension WalletNewsHandler {
return nextIndex
}

@MainActor
func onScroll(index: Int) {
guard index < list.count else { return }

Expand Down
39 changes: 14 additions & 25 deletions FRW/Modules/Wallet/WalletHomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions FRW/Services/Manager/Config/RemoteConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ extension RemoteConfigManager {
case unknow
case canUpgrade
case insufficientStorage
case insufficientBalance
case isIOS
case isAndroid
case isWeb
Expand Down Expand Up @@ -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
}
Expand Down
6 changes: 6 additions & 0 deletions FRW/Services/Manager/WalletManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@ extension WalletManager {
return accountInfo.storageCapacity - accountInfo.storageUsed < Self.mininumStorageThreshold
}

var isBalanceInsufficient: Bool {
guard self.isSelectedFlowAccount else { return false }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be an issue, when the flag is checked but the accountInfo is not fetched yet.
So it will always return false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That actually happens, but WalletNewsHandler is subscribed to the accountDataDidUpdate event, and when an update occurs (i.e. when the account has been fetched) the logic is re-evaluated.
That means that even though the notification doesn't appear immediately, it will after a few seconds.

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 }
Expand Down