using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace VRC.SDKBase.Editor
{
internal class DebouncedCall
{
private bool _debounced;
private DateTime _lastCall;
private readonly TimeSpan _delay;
private readonly Action _action;
private readonly ExecuteMode _mode;
///
/// Debounce execution mode
///
internal enum ExecuteMode
{
///
/// Always execute on the first call. Consecutive calls are debounced
///
Start,
///
/// Only execute after debounce delay
///
End
}
///
/// Creates a Debounced action wrapper
///
/// How long to wait until executing the call. The actual will always be executed after the specified from the first invocation instead of being delayed more
/// Action to call
/// Execution mode
internal DebouncedCall(TimeSpan delay, Action action, ExecuteMode executeMode)
{
_delay = delay;
_action = action;
_mode = executeMode;
}
internal void Invoke()
{
if (_debounced) return;
// If we haven't called the action yet or the delay has passed, call the action
if (_mode == ExecuteMode.Start && _lastCall.Add(_delay) <= DateTime.Now)
{
_lastCall = DateTime.Now;
_action();
return;
}
_debounced = true;
// Wait full delay before calling the action
_lastCall = DateTime.Now;
// Wait for the delay to pass before calling the action
Task.Run(async () =>
{
await UniTask.WaitUntil(() => _lastCall.Add(_delay) < DateTime.Now);
await UniTask.SwitchToMainThread();
_debounced = false;
_lastCall = DateTime.Now;
_action();
})
.ConfigureAwait(false);
}
}
}