use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
use tairitsu_vdom::Platform;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TimerId(i32);
impl TimerId {
pub fn new(id: i32) -> Self {
Self(id)
}
pub fn as_i32(&self) -> i32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FrameId(u32);
impl FrameId {
pub fn new(id: u32) -> Self {
Self(id)
}
pub fn as_u32(&self) -> u32 {
self.0
}
}
pub type TimerCallback = Rc<dyn Fn()>;
pub type FrameCallback = Rc<RefCell<dyn FnMut(f64)>>;
pub struct TimerManager<P: Platform> {
platform: Rc<RefCell<P>>,
}
impl<P: Platform> TimerManager<P> {
pub fn new(platform: Rc<RefCell<P>>) -> Self {
Self { platform }
}
pub fn set_timeout(&self, callback: TimerCallback, delay: Duration) -> TimerId {
let callback_clone = callback.clone();
let handle = self
.platform
.borrow_mut()
.set_timeout(Box::new(move || callback_clone()), delay.as_millis() as i32);
TimerId(handle)
}
pub fn clear_timeout(&self, id: TimerId) {
self.platform.borrow_mut().clear_timeout(id.0);
}
pub fn set_interval(&self, callback: TimerCallback, interval: Duration) -> TimerId {
let callback_clone = callback.clone();
let platform = Rc::clone(&self.platform);
self.set_timeout(
Rc::new(move || {
callback_clone();
let callback_clone2 = callback_clone.clone();
let platform_clone = Rc::clone(&platform);
let manager = TimerManager {
platform: platform_clone,
};
manager.set_timeout(callback_clone2, interval);
}),
interval,
)
}
pub fn clear_interval(&self, id: TimerId) {
self.clear_timeout(id);
}
pub fn request_animation_frame(&self, callback: FrameCallback) -> FrameId {
let callback_clone = callback.clone();
let handle =
self.platform
.borrow_mut()
.request_animation_frame(Box::new(move |timestamp| {
if let Ok(mut cb) = callback_clone.try_borrow_mut() {
cb(timestamp);
}
}));
FrameId(handle)
}
pub fn cancel_animation_frame(&self, id: FrameId) {
self.platform.borrow_mut().cancel_animation_frame(id.0);
}
}
impl<P: Platform> Clone for TimerManager<P> {
fn clone(&self) -> Self {
Self {
platform: Rc::clone(&self.platform),
}
}
}
pub fn debounce<P: Platform + 'static>(
manager: TimerManager<P>,
delay: Duration,
callback: TimerCallback,
) -> Rc<RefCell<dyn Fn()>> {
struct DebounceState {
timer_id: Option<TimerId>,
}
let state = Rc::new(RefCell::new(DebounceState { timer_id: None }));
let state_clone = state.clone();
(Rc::new(RefCell::new(move || {
if let Some(timer_id) = state_clone.borrow_mut().timer_id.take() {
manager.clear_timeout(timer_id);
}
let callback_clone = callback.clone();
let state_for_timer = state.clone();
let timer_id = manager.set_timeout(
Rc::new(move || {
callback_clone();
state_for_timer.borrow_mut().timer_id = None;
}),
delay,
);
state_clone.borrow_mut().timer_id = Some(timer_id);
}))) as _
}
pub fn throttle<P: Platform + 'static>(
_manager: TimerManager<P>,
interval: Duration,
callback: TimerCallback,
) -> Rc<RefCell<dyn Fn()>> {
struct ThrottleState {
last_call: Option<f64>,
}
let state = Rc::new(RefCell::new(ThrottleState { last_call: None }));
(Rc::new(RefCell::new(move || {
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as f64;
let mut state_ref = state.borrow_mut();
if let Some(last_time) = state_ref.last_call
&& now - last_time < interval.as_millis() as f64
{
return; }
state_ref.last_call = Some(now);
drop(state_ref);
callback();
}))) as _
}