1use std::rc::Rc;
2
3use async_channel::{Receiver, Sender};
4use wallet_adapter_common::standardized_events::{
5 WINDOW_APP_READY_EVENT_TYPE, WINDOW_REGISTER_WALLET_EVENT_TYPE,
6};
7use web_sys::{
8 js_sys::{Object, Reflect},
9 wasm_bindgen::{prelude::Closure, JsValue},
10 CustomEvent, CustomEventInit, Window,
11};
12
13use crate::{
14 InnerUtils, Reflection, StorageType, Wallet, WalletAccount, WalletAdapter, WalletError,
15 WalletResult,
16};
17
18pub type WalletEventSender = Sender<WalletEvent>;
20
21pub type WalletEventReceiver = Receiver<WalletEvent>;
23
24#[derive(Debug, PartialEq, Eq)]
26pub struct InitEvents<'a> {
27 window: &'a Window,
28}
29
30impl<'a> InitEvents<'a> {
31 pub fn new(window: &'a Window) -> Self {
33 Self { window }
34 }
35
36 pub fn init(&self, adapter: &mut WalletAdapter) -> WalletResult<()> {
39 let storage = adapter.storage();
40 self.register_wallet_event(storage.clone_inner())?;
41 self.dispatch_app_event(storage.clone_inner());
42
43 Ok(())
44 }
45
46 pub fn dispatch_app_event(&self, storage: StorageType) {
48 let app_ready_init = CustomEventInit::new();
49 app_ready_init.set_bubbles(false);
50 app_ready_init.set_cancelable(false);
51 app_ready_init.set_composed(false);
52 app_ready_init.set_detail(&Self::register_object(storage));
53
54 let app_ready_ev =
55 CustomEvent::new_with_event_init_dict(WINDOW_APP_READY_EVENT_TYPE, &app_ready_init)
56 .unwrap();
57
58 self.window.dispatch_event(&app_ready_ev).unwrap();
59 }
60
61 pub fn register_wallet_event(&self, storage: StorageType) -> WalletResult<()> {
63 let inner_storage = Rc::clone(&storage);
64
65 let listener_closure = Closure::wrap(Box::new(move |custom_event: CustomEvent| {
66 let detail = Reflection::new(custom_event
67 .detail()).unwrap().into_function()
68 .expect("Unable to get the `detail` function from the `Event` object. This is a fatal error as the register handler won't execute.");
69
70 InnerUtils::jsvalue_to_error(detail.call1(
71 &JsValue::null(),
72 &Self::register_object(inner_storage.clone()),
73 ))
74 .unwrap()
75 }) as Box<dyn Fn(_)>);
76
77 let listener_fn = Reflection::new(listener_closure.into_js_value())
78 .unwrap()
79 .into_function()
80 .unwrap();
81
82 self.window
83 .add_event_listener_with_callback(WINDOW_REGISTER_WALLET_EVENT_TYPE, &listener_fn)?;
84
85 Ok(())
86 }
87
88 pub fn register_object(storage: StorageType) -> Object {
90 let register =
92 Closure::wrap(
93 Box::new(move |value: JsValue| match Wallet::from_jsvalue(value) {
94 Ok(wallet) => {
95 let inner_outcome = storage.clone();
96
97 inner_outcome.borrow_mut().insert(
98 blake3::hash(wallet.name().to_lowercase().as_bytes()),
99 wallet,
100 );
101 }
102 Err(error) => {
103 let error = error.to_string();
104 if error.contains("is not supported") {
105 } else {
106 web_sys::console::error_2(
107 &"REGISTER EVENT ERROR".into(),
108 &error.into(),
109 );
110 }
111 }
112 }) as Box<dyn Fn(_)>,
113 );
114
115 let register_object = Object::new();
117
118 if let Err(error) = Reflect::set(
119 ®ister_object,
120 &JsValue::from("register"),
121 ®ister.into_js_value(),
122 ) {
123 web_sys::console::error_2(&"REGISTER EVENT ERROR".into(), &error);
124 }
125
126 register_object
127 }
128}
129
130#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Clone)]
136pub enum WalletEvent {
137 Connected(WalletAccount),
139 Reconnected(WalletAccount),
141 Disconnected,
143 AccountChanged(WalletAccount),
146 BackgroundTaskError(WalletError),
151 #[default]
153 Skip,
154}
155
156impl core::fmt::Display for WalletEvent {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 let as_str = match self {
159 Self::Connected(_) => "Connected",
160 Self::Reconnected(_) => "Reconnected",
161 Self::Disconnected => "Disconnected",
162 Self::AccountChanged(_) => "Account Changed",
163 Self::BackgroundTaskError(error) => &format!("Task error: {error:?}"),
164 Self::Skip => "Skipped",
165 };
166 write!(f, "{as_str}")
167 }
168}