use crate::*;
impl HookContext {
pub fn reset_index(&mut self) {
if let Ok(mut inner) = self.get_inner().try_borrow_mut() {
inner.set_hook_index(0);
}
}
pub fn switch_arm(&mut self, changed: usize) {
let cleanups: Vec<Box<dyn FnOnce()>>;
{
let Ok(mut inner) = self.get_inner().try_borrow_mut() else {
return;
};
if inner.get_arm_changed() == changed {
drop(inner);
self.reset_index();
return;
}
cleanups = take(inner.get_mut_cleanups());
inner.get_mut_hooks().clear();
inner.set_arm_changed(changed);
}
for cleanup in cleanups {
cleanup();
}
self.reset_index();
}
}
impl Clone for HookContext {
fn clone(&self) -> Self {
Self::new(self.get_inner().clone())
}
}
impl Default for HookContext {
fn default() -> Self {
Self::new(Rc::new(RefCell::new(HookContextInner::default())))
}
}
impl IntervalHandle {
pub fn clear(&self) {
if let Some(cleanup_window) = web_sys::window() {
cleanup_window.clear_interval_with_handle(self.get_interval_id());
}
}
}
impl HookContext {
#[allow(static_mut_refs)]
fn try_get_current() -> &'static Option<HookContextRc> {
unsafe { &*CURRENT_HOOK_CONTEXT.get_0().get() }
}
#[allow(static_mut_refs)]
fn try_get_current_mut() -> &'static mut Option<HookContextRc> {
unsafe { &mut *CURRENT_HOOK_CONTEXT.get_0().get() }
}
pub(crate) fn current() -> HookContext {
match Self::try_get_current() {
Some(hook_context_rc) => HookContext::new(hook_context_rc.clone()),
None => {
let rc: HookContextRc = Rc::new(RefCell::new(HookContextInner::default()));
*Self::try_get_current_mut() = Some(rc.clone());
HookContext::new(rc)
}
}
}
pub(crate) fn with<F, R>(context: HookContext, callback: F) -> R
where
F: FnOnce() -> R,
{
let previous: Option<HookContextRc> = Self::try_get_current_mut().take();
*Self::try_get_current_mut() = Some(context.get_inner().clone());
let result: R = callback();
*Self::try_get_current_mut() = previous;
result
}
pub(crate) fn signal<T, F>(init: F) -> Signal<T>
where
T: Clone + PartialEq + 'static,
F: FnOnce() -> T,
{
let hook_context: HookContext = Self::current();
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.deactivate()));
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(crate) fn cleanup<F>(cleanup: F)
where
F: FnOnce() + 'static,
{
let hook_context: HookContext = Self::current();
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(crate) fn window_event<E, F>(event_name: E, callback: F)
where
E: AsRef<str>,
F: FnMut() + 'static,
{
let event_name: &str = event_name.as_ref();
let hook_context: HookContext = Self::current();
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_owned();
let handler_id: usize = Registry::register_window_event(event_name, callback);
inner.get_mut_cleanups().push(Box::new(move || {
Registry::unregister_window_event(&event_name_owned, handler_id);
}));
inner.get_mut_hooks().push(Box::new(()));
}
pub(crate) fn interval<F>(millis: i32, callback: F) -> IntervalHandle
where
F: FnMut() + 'static,
{
let hook_context: HookContext = Self::current();
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
}
}