use crate::prelude::*;
use beet_core::prelude::*;
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::LazyLock;
use std::sync::RwLock;
pub struct ReactiveApp;
thread_local! {
static APP: RefCell<Option<App>> = RefCell::new(None);
}
static CREATE_APP: LazyLock<
Arc<RwLock<Box<dyn 'static + Send + Sync + Fn() -> App>>>,
> = LazyLock::new(|| {
Arc::new(RwLock::new(Box::new(|| {
let mut app = App::new();
app.add_plugins((ApplyDirectivesPlugin, SignalsPlugin));
app
})))
});
impl ReactiveApp {
pub fn set_create_app(func: impl 'static + Send + Sync + Fn() -> App) {
*CREATE_APP.write().unwrap() = Box::new(func);
}
pub fn runner(mut app: App) -> AppExit {
PrettyTracing::default().init();
app.init();
app.update();
APP.with(move |app_ref| {
let mut app_cell = app_ref.borrow_mut();
*app_cell = Some(app);
});
AppExit::Success
}
pub fn with<O>(func: impl FnOnce(&mut App) -> O) -> O {
APP.with(|app_ref| {
let mut app_cell = app_ref.borrow_mut();
match app_cell.as_mut() {
Some(app) => func(app),
None => {
*app_cell = Some(CREATE_APP.read().unwrap()());
let mut app = app_cell.as_mut().unwrap();
let out = func(&mut app);
out
}
}
})
}
pub fn try_with<O>(func: impl FnOnce(&mut App) -> O) -> Option<O> {
APP.with(|app_ref| {
if let Ok(mut app_cell) = app_ref.try_borrow_mut() {
let app = app_cell.as_mut()?;
Some(func(app))
} else {
None
}
})
}
pub fn update() -> Option<AppExit> {
Self::with(|app| {
app.update();
app.should_exit()
})
}
pub fn try_update() -> Option<Option<AppExit>> {
Self::try_with(|app| {
app.update();
app.should_exit()
})
}
#[cfg(not(target_arch = "wasm32"))]
pub fn queue_update() { Self::try_update(); }
#[cfg(target_arch = "wasm32")]
pub fn queue_update() {
use wasm_bindgen::JsCast;
use wasm_bindgen::closure::Closure;
static UPDATE_QUEUED: LazyLock<Arc<std::sync::Mutex<bool>>> =
LazyLock::new(|| Arc::new(std::sync::Mutex::new(false)));
let update_flag = UPDATE_QUEUED.clone();
{
let mut is_queued = update_flag.lock().unwrap();
if *is_queued {
return;
}
*is_queued = true;
}
let update_flag_for_task = update_flag.clone();
let func = Closure::once_into_js(move || {
{
let mut is_queued = update_flag_for_task.lock().unwrap();
*is_queued = false;
}
let _ = Self::try_update();
});
let func_js: js_sys::Function = func.unchecked_into();
let _ = web_sys::window().unwrap().queue_microtask(&func_js);
}
}