use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
pub(crate) async fn run_morph_with_optional_transition(
perform: impl FnOnce() -> Result<(), JsValue> + 'static,
) -> Result<(), JsValue> {
if !should_use_view_transition() {
return perform();
}
let outcome: Rc<RefCell<Result<(), JsValue>>> = Rc::new(RefCell::new(Ok(())));
let outcome_for_callback = outcome.clone();
let mut perform_holder = Some(perform);
let callback = wasm_bindgen::closure::Closure::once_into_js(move || {
if let Some(perform) = perform_holder.take() {
*outcome_for_callback.borrow_mut() = perform();
}
});
let update_callback_done = run_view_transition_glue(&callback);
let _ = JsFuture::from(update_callback_done).await;
Rc::try_unwrap(outcome)
.map(RefCell::into_inner)
.unwrap_or_else(|shared| shared.borrow().clone())
}
fn should_use_view_transition() -> bool {
should_view_transition_glue()
}
#[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#"
export function __islands_should_view_transition() {
if (typeof document === "undefined") return false;
if (typeof document.startViewTransition !== "function") return false;
try {
if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return false;
}
} catch (_e) {
// matchMedia unavailable — fall through to allowing the transition.
}
return true;
}
export function __islands_run_view_transition(callback) {
// The callback is synchronous (returns undefined); startViewTransition
// snapshots before, runs the callback to mutate the DOM, then animates.
// Return updateCallbackDone so the caller can sequence post-morph work.
const transition = document.startViewTransition(callback);
return transition.updateCallbackDone;
}
"#)]
extern "C" {
#[wasm_bindgen(js_name = __islands_should_view_transition)]
fn should_view_transition_glue() -> bool;
#[wasm_bindgen(js_name = __islands_run_view_transition)]
fn run_view_transition_glue(callback: &JsValue) -> js_sys::Promise;
}