use crate::*;
pub(crate) fn get_current_hook_context() -> HookContext {
match current_hook_context() {
Some(hook_context_rc) => HookContext::new(hook_context_rc.clone()),
None => {
let rc: HookContextRc = Rc::new(RefCell::new(HookContextInner::default()));
*current_hook_context_mut() = Some(rc.clone());
HookContext::new(rc)
}
}
}
pub(crate) fn with_hook_context<F, R>(context: HookContext, f: F) -> R
where
F: FnOnce() -> R,
{
let previous: Option<HookContextRc> = current_hook_context_mut().take();
*current_hook_context_mut() = Some(context.get_inner().clone());
let result: R = f();
*current_hook_context_mut() = previous;
result
}
pub(crate) fn create_hook_context() -> HookContext {
HookContext::default()
}
pub fn use_signal<T, F>(init: F) -> Signal<T>
where
T: Clone + PartialEq + 'static,
F: FnOnce() -> T,
{
let hook_context: HookContext = get_current_hook_context();
let Ok(mut inner) = hook_context.get_inner().try_borrow_mut() else {
return Signal::create(init());
};
let index: usize = inner.get_hook_index();
inner.set_hook_index(index + 1);
if index < inner.get_hooks().len()
&& let Some(existing) = inner.get_hooks()[index].downcast_ref::<Signal<T>>()
{
return *existing;
}
let signal: Signal<T> = Signal::create(init());
inner
.get_mut_cleanups()
.push(Box::new(move || signal.clear_listeners()));
if index < inner.get_hooks().len() {
inner.get_mut_hooks()[index] = Box::new(signal);
} else {
inner.get_mut_hooks().push(Box::new(signal));
}
signal
}
pub fn use_cleanup<F>(cleanup: F)
where
F: FnOnce() + 'static,
{
let hook_context: HookContext = get_current_hook_context();
let Ok(mut inner) = hook_context.get_inner().try_borrow_mut() else {
return;
};
let index: usize = inner.get_hook_index();
inner.set_hook_index(index + 1);
if index < inner.get_hooks().len() {
return;
}
inner.get_mut_cleanups().push(Box::new(cleanup));
inner.get_mut_hooks().push(Box::new(()));
}
pub fn use_window_event<F>(event_name: &str, callback: F)
where
F: FnMut() + 'static,
{
let hook_context: HookContext = get_current_hook_context();
let Ok(mut inner) = hook_context.get_inner().try_borrow_mut() else {
return;
};
let index: usize = inner.get_hook_index();
inner.set_hook_index(index + 1);
if index < inner.get_hooks().len() {
return;
}
let event_name_owned: String = event_name.to_string();
let handler_id: usize = register_window_event_handler(event_name, callback);
inner.get_mut_cleanups().push(Box::new(move || {
unregister_window_event_handler(&event_name_owned, handler_id);
}));
inner.get_mut_hooks().push(Box::new(()));
}
pub fn use_interval<F>(millis: i32, callback: F) -> IntervalHandle
where
F: FnMut() + 'static,
{
let hook_context: HookContext = get_current_hook_context();
let Ok(mut inner) = hook_context.get_inner().try_borrow_mut() else {
return IntervalHandle::new(0);
};
let index: usize = inner.get_hook_index();
inner.set_hook_index(index + 1);
if index < inner.get_hooks().len()
&& let Some(existing) = inner.get_hooks()[index].downcast_ref::<IntervalHandle>()
{
return *existing;
}
let closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(callback));
let window: Window = window().expect("no global window exists");
let interval_id: i32 = window
.set_interval_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref(),
millis,
)
.expect("failed to set interval");
closure.forget();
let handle: IntervalHandle = IntervalHandle::new(interval_id);
inner.get_mut_cleanups().push(Box::new(move || {
let Some(cleanup_window) = web_sys::window() else {
return;
};
cleanup_window.clear_interval_with_handle(interval_id);
}));
if index < inner.get_hooks().len() {
inner.get_mut_hooks()[index] = Box::new(handle);
} else {
inner.get_mut_hooks().push(Box::new(handle));
}
handle
}