mirror of
https://github.com/lux-tel/session-ios.git
synced 2026-10-30 20:38:32 +00:00
• Added a mechanism to trigger an immediate event (it forces the debouncer to flush) • Updated the ThreadNotificationSettingsViewModel to use the ObservationBuilder • Updated the AppearanceViewModel to use the ObservationBuilder • Updated the ConversationSettingsViewModel to use the ObservationBuilder • Updated the NotificationContentViewModel to use the ObservationBuilder • Updated the NotificationSettingsViewModel to use the ObservationBuilder • Updated the PrivacySettingsViewModel to use the ObservationBuilder • Updated the SettingsViewModel to use the ObservationBuilder • Cleaned up the MessageRequestsViewModel and HomeViewModel to be consistent with the other refactored view models • Removed the `libSessionObservation` logic (replaced with new ObservationBuilder) • Fixed a bug where the notification sound setting might not work correctly • Fixed a few incorrect events being sent
112 lines
3.1 KiB
Swift
112 lines
3.1 KiB
Swift
// Copyright © 2025 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import Foundation
|
|
|
|
public actor DebounceTaskManager<Event: Sendable> {
|
|
private let debounceInterval: DispatchTimeInterval
|
|
private var debounceTask: Task<Void, Never>? = nil
|
|
private var pendingEvents: [Event] = []
|
|
private var pendingEventSet: Set<AnyHashable> = []
|
|
private var action: (@Sendable ([Event]) async -> Void)?
|
|
|
|
public init(debounceInterval: DispatchTimeInterval) {
|
|
self.debounceInterval = debounceInterval
|
|
}
|
|
|
|
public func setAction(_ newAction: @Sendable @escaping ([Event]) async -> Void) {
|
|
self.action = newAction
|
|
}
|
|
|
|
public func reset() {
|
|
debounceTask?.cancel()
|
|
debounceTask = nil
|
|
pendingEvents.removeAll()
|
|
pendingEventSet.removeAll()
|
|
action = nil
|
|
}
|
|
|
|
// MARK: - Internal Functions
|
|
|
|
fileprivate func scheduleSignal() {
|
|
debounceTask?.cancel()
|
|
debounceTask = Task { [weak self] in
|
|
guard let self = self else { return }
|
|
|
|
do {
|
|
/// Only debounce if we want to
|
|
if debounceInterval != .never {
|
|
try await Task.sleep(for: self.debounceInterval)
|
|
}
|
|
guard !Task.isCancelled else { return }
|
|
|
|
let eventsToProcess: [Event] = await self.clearPendingEvents()
|
|
await self.action?(eventsToProcess)
|
|
} catch {
|
|
// Task was cancelled so no need to do anything
|
|
}
|
|
}
|
|
}
|
|
|
|
fileprivate func flushPendingEvents() {
|
|
debounceTask?.cancel()
|
|
|
|
debounceTask = Task { [weak self] in
|
|
guard let self = self else { return }
|
|
|
|
let eventsToProcess: [Event] = await self.clearPendingEvents()
|
|
await self.action?(eventsToProcess)
|
|
}
|
|
}
|
|
|
|
private func clearPendingEvents() -> [Event] {
|
|
let events: [Event] = pendingEvents
|
|
pendingEvents.removeAll()
|
|
pendingEventSet.removeAll()
|
|
return events
|
|
}
|
|
}
|
|
|
|
public extension DebounceTaskManager where Event == Void {
|
|
func signal() {
|
|
pendingEvents.append(())
|
|
scheduleSignal()
|
|
}
|
|
|
|
func flush() {
|
|
pendingEvents.append(())
|
|
flushPendingEvents()
|
|
}
|
|
}
|
|
|
|
public extension DebounceTaskManager {
|
|
func signal(event: Event) {
|
|
pendingEvents.append(event)
|
|
scheduleSignal()
|
|
}
|
|
|
|
func flush(event: Event) {
|
|
pendingEvents.append(event)
|
|
flushPendingEvents()
|
|
}
|
|
}
|
|
|
|
public extension DebounceTaskManager where Event: Hashable {
|
|
func signal(event: Event) {
|
|
/// Ignore duplicate events
|
|
guard !pendingEventSet.contains(event) else { return }
|
|
|
|
pendingEvents.append(event)
|
|
pendingEventSet.insert(event)
|
|
scheduleSignal()
|
|
}
|
|
|
|
func flush(event: Event) {
|
|
/// Ignore duplicate events
|
|
guard !pendingEventSet.contains(event) else { return }
|
|
|
|
pendingEvents.append(event)
|
|
pendingEventSet.insert(event)
|
|
flushPendingEvents()
|
|
}
|
|
}
|