use crate::reactor::Reactor;
use crate::{i18n::TranslationsManager, init::PerseusAppBase, stores::MutableStore};
use crate::{plugins::PluginAction, template::BrowserNodeType, utils::checkpoint};
use sycamore::prelude::create_scope;
use wasm_bindgen::JsValue;
use web_sys::{CustomEvent, CustomEventInit};
pub fn run_client<M: MutableStore, T: TranslationsManager>(
app: impl Fn() -> PerseusAppBase<BrowserNodeType, M, T>,
) {
let mut app = app();
let (general_panic_handler, view_panic_handler) = app.take_panic_handlers();
checkpoint("begin");
std::panic::set_hook(Box::new(move |panic_info| {
#[cfg(debug_assertions)]
console_error_panic_hook::hook(panic_info);
#[cfg(debug_assertions)]
crate::web_log!("[CRITICAL ERROR]: Perseus has panicked! An error message has hopefully been displayed on your screen explaining this; if not, then something has gone terribly wrong, and, unless your code is panicking, you should report this as a bug. (If you're seeing this as an end user, please report it to the website administrator.)");
#[cfg(not(debug_assertions))]
crate::web_log!("[CRITICAL ERROR]: Perseus has panicked! An error message has hopefully been displayed on your screen explaining this; if not, then reloading the page might help.");
#[cfg(panic = "unwind")]
crate::web_log!("[WARNING]: The app has been compiled with unwinding panics, and it is possible that the app will now continue normal operation if this panic is handled.");
dispatch_loaded(false, true);
if let Some(panic_handler) = &general_panic_handler {
panic_handler(panic_info);
}
Reactor::handle_panic(panic_info, view_panic_handler.clone());
}));
let plugins = app.plugins.clone();
let error_views;
#[cfg(debug_assertions)]
{
error_views = app.error_views.clone().unwrap_or_default();
}
#[cfg(not(debug_assertions))]
{
error_views = app
.error_views
.clone()
.expect("you must provide your own error views in production");
}
let mut running = true;
let app_disposer = create_scope(|cx| {
running = {
match Reactor::try_from(app) {
Ok(reactor) => {
reactor.add_self_to_cx(cx);
let reactor = Reactor::from_cx(cx);
reactor.start(cx)
}
Err(err) => {
Reactor::handle_critical_error(cx, err, &error_views);
false
}
}
};
});
dispatch_loaded(running, false);
if !running {
unsafe { app_disposer.dispose() }
plugins
.functional_actions
.client_actions
.crash
.run((), plugins.get_plugin_data())
.expect("plugin action on crash failed");
}
}
pub type ClientReturn = Result<(), JsValue>;
fn dispatch_loaded(running: bool, panic: bool) {
let document = web_sys::window().unwrap().document().unwrap();
let mut ev_init = CustomEventInit::new();
ev_init.detail(&if panic { JsValue::NULL } else { running.into() });
let ev = CustomEvent::new_with_event_init_dict("__perseus_loaded", &ev_init).unwrap();
document.dispatch_event(&ev).unwrap();
}