use std::cell::RefCell;
use wasm_bindgen::{JsCast, JsValue};
use crate::error::Error;
struct Handles {
window: web_sys::Window,
document: web_sys::Document,
live_socket: JsValue,
exec_js: js_sys::Function,
}
thread_local! {
static HANDLES: RefCell<Option<Handles>> = const { RefCell::new(None) };
}
fn ensure_handles(slot: &mut Option<Handles>) -> Result<(), Error> {
if slot.is_some() {
return Ok(());
}
let window = web_sys::window().ok_or(Error::NoWindow)?;
let document = window.document().ok_or(Error::NoDocument)?;
let live_socket = js_sys::Reflect::get(&window, &JsValue::from_str("liveSocket"))
.map_err(|_| Error::NoLiveSocket)?;
if live_socket.is_undefined() || live_socket.is_null() {
return Err(Error::NoLiveSocket);
}
let exec_js_val = js_sys::Reflect::get(&live_socket, &JsValue::from_str("execJS"))
.map_err(|_| Error::NoLiveSocket)?;
let exec_js: js_sys::Function = exec_js_val.dyn_into().map_err(|_| Error::NoLiveSocket)?;
*slot = Some(Handles {
window,
document,
live_socket,
exec_js,
});
Ok(())
}
pub fn window() -> Result<web_sys::Window, Error> {
HANDLES.with(|cell| {
let mut slot = cell.borrow_mut();
ensure_handles(&mut slot)?;
Ok(slot.as_ref().unwrap().window.clone())
})
}
pub fn document() -> Result<web_sys::Document, Error> {
HANDLES.with(|cell| {
let mut slot = cell.borrow_mut();
ensure_handles(&mut slot)?;
Ok(slot.as_ref().unwrap().document.clone())
})
}
pub fn with_live_view<Return, Callback>(callback: Callback) -> Result<Return, Error>
where
Callback: FnOnce(&js_sys::Function, &JsValue, &web_sys::Element) -> Result<Return, Error>,
{
HANDLES.with(|cell| {
let mut slot = cell.borrow_mut();
ensure_handles(&mut slot)?;
let handles = slot.as_ref().unwrap();
let root = handles
.document
.query_selector("[data-phx-session]")
.ok()
.flatten()
.ok_or(Error::NoLiveViewRoot)?;
callback(&handles.exec_js, &handles.live_socket, &root)
})
}