use std::rc::Rc;
use async_channel::{Receiver, Sender};
use wallet_adapter_common::standardized_events::{
WINDOW_APP_READY_EVENT_TYPE, WINDOW_REGISTER_WALLET_EVENT_TYPE,
};
use web_sys::{
js_sys::{Object, Reflect},
wasm_bindgen::{prelude::Closure, JsValue},
CustomEvent, CustomEventInit, Window,
};
use crate::{
InnerUtils, Reflection, StorageType, Wallet, WalletAccount, WalletAdapter, WalletError,
WalletResult,
};
pub type WalletEventSender = Sender<WalletEvent>;
pub type WalletEventReceiver = Receiver<WalletEvent>;
#[derive(Debug, PartialEq, Eq)]
pub struct InitEvents<'a> {
window: &'a Window,
}
impl<'a> InitEvents<'a> {
pub fn new(window: &'a Window) -> Self {
Self { window }
}
pub fn init(&self, adapter: &mut WalletAdapter) -> WalletResult<()> {
let storage = adapter.storage();
self.register_wallet_event(storage.clone_inner())?;
self.dispatch_app_event(storage.clone_inner());
Ok(())
}
pub fn dispatch_app_event(&self, storage: StorageType) {
let app_ready_init = CustomEventInit::new();
app_ready_init.set_bubbles(false);
app_ready_init.set_cancelable(false);
app_ready_init.set_composed(false);
app_ready_init.set_detail(&Self::register_object(storage));
let app_ready_ev =
CustomEvent::new_with_event_init_dict(WINDOW_APP_READY_EVENT_TYPE, &app_ready_init)
.unwrap();
self.window.dispatch_event(&app_ready_ev).unwrap();
}
pub fn register_wallet_event(&self, storage: StorageType) -> WalletResult<()> {
let inner_storage = Rc::clone(&storage);
let listener_closure = Closure::wrap(Box::new(move |custom_event: CustomEvent| {
let detail = Reflection::new(custom_event
.detail()).unwrap().into_function()
.expect("Unable to get the `detail` function from the `Event` object. This is a fatal error as the register handler won't execute.");
InnerUtils::jsvalue_to_error(detail.call1(
&JsValue::null(),
&Self::register_object(inner_storage.clone()),
))
.unwrap()
}) as Box<dyn Fn(_)>);
let listener_fn = Reflection::new(listener_closure.into_js_value())
.unwrap()
.into_function()
.unwrap();
self.window
.add_event_listener_with_callback(WINDOW_REGISTER_WALLET_EVENT_TYPE, &listener_fn)?;
Ok(())
}
pub fn register_object(storage: StorageType) -> Object {
let register =
Closure::wrap(
Box::new(move |value: JsValue| match Wallet::from_jsvalue(value) {
Ok(wallet) => {
let inner_outcome = storage.clone();
inner_outcome.borrow_mut().insert(
blake3::hash(wallet.name().to_lowercase().as_bytes()),
wallet,
);
}
Err(error) => {
let error = error.to_string();
if error.contains("is not supported") {
} else {
web_sys::console::error_2(
&"REGISTER EVENT ERROR".into(),
&error.into(),
);
}
}
}) as Box<dyn Fn(_)>,
);
let register_object = Object::new();
if let Err(error) = Reflect::set(
®ister_object,
&JsValue::from("register"),
®ister.into_js_value(),
) {
web_sys::console::error_2(&"REGISTER EVENT ERROR".into(), &error);
}
register_object
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Clone)]
pub enum WalletEvent {
Connected(WalletAccount),
Reconnected(WalletAccount),
Disconnected,
AccountChanged(WalletAccount),
BackgroundTaskError(WalletError),
#[default]
Skip,
}
impl core::fmt::Display for WalletEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let as_str = match self {
Self::Connected(_) => "Connected",
Self::Reconnected(_) => "Reconnected",
Self::Disconnected => "Disconnected",
Self::AccountChanged(_) => "Account Changed",
Self::BackgroundTaskError(error) => &format!("Task error: {error:?}"),
Self::Skip => "Skipped",
};
write!(f, "{as_str}")
}
}