use super::*;
use js_sys::Array;
use js_sys::Promise;
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
use web_sys::window;
use web_sys::EventTarget;
pub struct HtmlEventWaiterInstance {
pub listener: Rc<RefCell<Option<HtmlEventListener<JsValue>>>>,
pub promise: Promise,
}
pub struct HtmlEventWaiter {
name: &'static str,
target: EventTarget,
rejects: bool,
}
impl HtmlEventWaiter {
pub fn new(name: &'static str) -> Self {
Self::new_with_target(name, window().unwrap())
}
pub fn new_with_target(
name: &'static str,
target: impl Into<EventTarget>,
) -> Self {
Self {
name,
target: target.into(),
rejects: false,
}
}
pub fn rejects(mut self) -> Self {
self.rejects = true;
self
}
pub fn instantiate(self) -> HtmlEventWaiterInstance {
let listener: Rc<RefCell<Option<HtmlEventListener<JsValue>>>> =
Rc::new(RefCell::new(None));
let listener2 = listener.clone();
let promise = Promise::new(&mut move |resolve, reject| {
let func = if self.rejects { reject } else { resolve };
let target = self.target.clone();
*listener2.borrow_mut() =
Some(HtmlEventListener::<JsValue>::new_with_target(
self.name,
move |value| {
func.call1(&JsValue::NULL, &value).unwrap();
},
target,
));
});
HtmlEventWaiterInstance { listener, promise }
}
pub async fn wait(self) -> Result<JsValue, JsValue> {
let inst = self.instantiate();
JsFuture::from(inst.promise).await
}
pub async fn wait_or_timeout(
self,
duration: Duration,
) -> Result<JsValue, JsValue> {
let inst = self.instantiate();
let timeout = timeout_reject(duration);
let arr = Array::new();
arr.push(&inst.promise);
arr.push(&timeout);
JsFuture::from(Promise::race(&arr)).await
}
pub async fn await_first(
items: impl IntoIterator<Item = HtmlEventWaiter>,
) -> Result<JsValue, JsValue> {
let items = items
.into_iter()
.map(|item| item.instantiate())
.collect::<Vec<_>>();
let arr = Array::new();
for item in items.iter() {
arr.push(&item.promise);
}
JsFuture::from(Promise::race(&arr)).await
}
}
pub fn timeout_reject(duration: Duration) -> Promise {
Promise::new(&mut |_resolve, reject| {
web_sys::window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_1(
&reject,
duration.as_millis() as i32,
&JsValue::from_str("Timed out"),
)
.unwrap();
})
}