diff --git a/iOSClient/GUI/Lucid Banner/UploadBannerView.swift b/iOSClient/GUI/Lucid Banner/UploadBannerView.swift index ae98fe88f3..7d85e1a0b6 100644 --- a/iOSClient/GUI/Lucid Banner/UploadBannerView.swift +++ b/iOSClient/GUI/Lucid Banner/UploadBannerView.swift @@ -90,8 +90,10 @@ struct UploadBannerView: View { if state.isMinimized { HStack(spacing: 5) { Image(systemName: state.systemImage ?? "arrow.up.circle") + .applyBannerAnimation(state.imageAnimation) .font(.body.weight(.medium)) .frame(width: 20, height: 20) + .foregroundStyle(Color(uiColor: NCBrandColor.shared.customer)) if let p = state.progress { Text("\(Int(p * 100))%") @@ -99,7 +101,6 @@ struct UploadBannerView: View { .frame(height: 20) .foregroundStyle(textColor) } - } .padding(.horizontal, 10) .padding(.vertical, 10) diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index 51df24b68c..e257881739 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -277,7 +277,6 @@ final class NCGlobal: Sendable { let notificationCenterOpenMediaDetail = "openMediaDetail" // userInfo: ocId let notificationCenterDismissScanDocument = "dismissScanDocument" - let notificationCenterDismissUploadAssets = "dismissUploadAssets" let notificationCenterEnableSwipeGesture = "enableSwipeGesture" let notificationCenterDisableSwipeGesture = "disableSwipeGesture" diff --git a/iOSClient/Utility/NCDebouncer.swift b/iOSClient/Utility/NCDebouncer.swift index 855f97b647..6055569760 100644 --- a/iOSClient/Utility/NCDebouncer.swift +++ b/iOSClient/Utility/NCDebouncer.swift @@ -7,18 +7,30 @@ import Foundation public actor NCDebouncer { private let delay: Duration private let maxEventCount: Int - private var eventCount = 0 + private var eventCount: Int = 0 private var pendingTask: Task? private var latestBlock: (@MainActor @Sendable () async -> Void)? + private var isPaused: Bool = false + + // MARK: - Init public init(delay: Duration = .seconds(2), maxEventCount: Int) { self.delay = delay self.maxEventCount = maxEventCount } + // MARK: - Public API + public func call(_ block: @MainActor @Sendable @escaping () async -> Void, immediate: Bool = false) { latestBlock = block + guard !isPaused else { + // We only store the latest block and count events, + // but we never schedule or commit while paused. + eventCount += 1 + return + } + if immediate { commit() return @@ -32,6 +44,25 @@ public actor NCDebouncer { } } + public func pause() { + guard !isPaused else { return } + + isPaused = true + pendingTask?.cancel() + pendingTask = nil + } + + public func resume() { + guard isPaused else { return } + + isPaused = false + + // If something accumulated while paused, commit immediately. + if latestBlock != nil { + commit() + } + } + public func cancel() { pendingTask?.cancel() pendingTask = nil @@ -39,25 +70,30 @@ public actor NCDebouncer { eventCount = 0 } + // MARK: - Internal + private func scheduleIfNeeded() { - guard pendingTask == nil else { return } + guard pendingTask == nil, !isPaused else { return } + pendingTask = Task { [weak self] in - guard let delay = self?.delay else { return } - try? await Task.sleep(for: delay) - await self?.commit() + guard let self else { return } + try? await Task.sleep(for: self.delay) + await self.commit() } } private func commit() { + guard !isPaused else { return } + pendingTask?.cancel() pendingTask = nil eventCount = 0 - if let block = latestBlock { - latestBlock = nil - Task { @MainActor in - await block() - } + guard let block = latestBlock else { return } + latestBlock = nil + + Task { @MainActor in + await block() } } }