AzureCommunicationUI/sdk/AzureCommunicationUICalling/Sources/Redux/Middleware/ThrottleMiddleware.swift (40 lines of code) (raw):

// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // import Foundation import Combine extension Middleware { // Throttles a particular action. // // Can be thought of "button smashing protection" // // I.e. if an action is dispatch 10 times in 0.4 seconds, only the first will pass through. // Meant for user-action's that could conflict with animations. // // The KeyGenerator is used to "Key" actions->Strings. These s static func throttleMiddleware(timeoutS: CGFloat = 0.4, actionKeyGenerator: @escaping (Action) -> String?) -> Middleware<State, Action> { let throttler = Throttler(timeoutS: timeoutS, actionKeyGenerator: actionKeyGenerator) return .init( apply: { _, _ in return { next in return { action in // Delegate to the Throttler to check if the action should be processed if throttler.shouldProcess(action: action) { return next(action) } } } } ) } } internal class Throttler<Action> { private let timeoutSeconds: CGFloat private var lastActionTime: [String: Date] = [:] private let actionKeyGenerator: (Action) -> String? init(timeoutS: CGFloat = 0.4, actionKeyGenerator: @escaping (Action) -> String?) { self.timeoutSeconds = timeoutS self.actionKeyGenerator = actionKeyGenerator } func shouldProcess(action: Action) -> Bool { guard let actionID: String = actionKeyGenerator(action) else { return true } let currentTime = Date() if let lastTime = lastActionTime[actionID], currentTime.timeIntervalSince(lastTime) < TimeInterval(timeoutSeconds) { // Drop the action if it's within the throttle timeout and is the same as the last action return false } // Update the last action time and allow the action to pass through lastActionTime[actionID] = currentTime return true } }