use crate::*;
thread_local! {
static DISPATCH_CLOSURE: closure::Closure<dyn FnMut()> =
closure::Closure::wrap(Box::new(|| {
SCHEDULED.store(false, Ordering::Relaxed);
dispatch_signal_update_callbacks();
}) as Box<dyn FnMut()>);
}
fn with_dispatch_function<F, R>(callback: F) -> R
where
F: FnOnce(&Function) -> R,
{
DISPATCH_CLOSURE.with(|dispatch_closure| {
let dispatch_function: &Function = dispatch_closure.as_ref().unchecked_ref::<Function>();
callback(dispatch_function)
})
}
pub fn schedule_signal_update() {
schedule_signal_update_targeted(&[]);
}
pub fn schedule_signal_update_targeted(dependents: &[usize]) {
if SUPPRESS_SCHEDULE.load(Ordering::Relaxed) {
if !dependents.is_empty() {
mark_slots_dirty_targeted(dependents);
} else {
mark_all_slots_dirty();
}
return;
}
if !dependents.is_empty() {
mark_slots_dirty_targeted(dependents);
} else {
mark_all_slots_dirty();
}
if SCHEDULED.load(Ordering::Relaxed) {
return;
}
SCHEDULED.store(true, Ordering::Relaxed);
let window_value: Window = match window() {
Some(window_instance) => window_instance,
None => {
SCHEDULED.store(false, Ordering::Relaxed);
return;
}
};
let queued_microtask: bool = with_dispatch_function(|dispatch_function| {
let queue_microtask_value: JsValue =
Reflect::get(&window_value, &JsValue::from_str(QUEUE_MICROTASK))
.unwrap_or(JsValue::UNDEFINED);
matches!(
queue_microtask_value.dyn_into::<Function>(),
Ok(queue_microtask) if queue_microtask.call1(&window_value, dispatch_function).is_ok()
)
});
if queued_microtask {
return;
}
let scheduled: bool = with_dispatch_function(|dispatch_function| {
window_value
.set_timeout_with_callback_and_timeout_and_arguments_0(dispatch_function, 0)
.is_ok()
});
if scheduled {
return;
}
let requested_frame: bool = with_dispatch_function(|dispatch_function| {
window_value
.request_animation_frame(dispatch_function)
.is_ok()
});
if requested_frame {
return;
}
SCHEDULED.store(false, Ordering::Relaxed);
}
pub fn batch_updates<F, R>(callback: F) -> R
where
F: FnOnce() -> R,
{
let was_outermost: bool = !SUPPRESS_SCHEDULE.load(Ordering::Relaxed);
SUPPRESS_SCHEDULE.store(true, Ordering::Relaxed);
let result: R = callback();
if was_outermost {
SUPPRESS_SCHEDULE.store(false, Ordering::Relaxed);
schedule_signal_update();
} else {
SUPPRESS_SCHEDULE.store(false, Ordering::Relaxed);
}
result
}
pub(crate) fn subscribe_attr_signal<F>(attr_signal: Signal<String>, compute: F)
where
F: Fn() -> String + 'static,
{
register_attr_signal_listener(
attr_signal.get_inner(),
Box::new(move || {
attr_signal.set_silent(compute());
}),
);
}
pub(crate) fn bool_signal_to_string_attribute_value(source: Signal<bool>) -> AttributeValue {
let string_signal: Signal<String> = Signal::create(source.get().to_string());
let string_signal_clone: Signal<String> = string_signal;
source.subscribe({
let source_inner: Signal<bool> = source;
move || {
string_signal_clone.set_silent(source_inner.get().to_string());
}
});
AttributeValue::Signal(string_signal)
}
#[allow(static_mut_refs)]
pub(crate) fn current_hook_context_mut() -> &'static mut Option<HookContextRc> {
unsafe { &mut *CURRENT_HOOK_CONTEXT.get_0().get() }
}
#[allow(static_mut_refs)]
pub(crate) fn current_hook_context() -> &'static Option<HookContextRc> {
unsafe { &*CURRENT_HOOK_CONTEXT.get_0().get() }
}