feat: Add SigSocket integration with WASM client and JavaScript bridge for sign requests
This commit is contained in:
		| @@ -34,7 +34,7 @@ impl WasmClient { | ||||
|             reconnect_attempts: Rc::new(RefCell::new(0)), | ||||
|             max_reconnect_attempts: 5, | ||||
|             reconnect_delay_ms: 1000, // Start with 1 second | ||||
|             auto_reconnect: true, // Enable auto-reconnect by default | ||||
|             auto_reconnect: false, // Disable auto-reconnect to avoid multiple connections | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @@ -117,62 +117,91 @@ impl WasmClient { | ||||
|  | ||||
|     /// Single connection attempt | ||||
|     async fn try_connect(&mut self) -> Result<()> { | ||||
|         use wasm_bindgen_futures::JsFuture; | ||||
|         use js_sys::Promise; | ||||
|  | ||||
|         web_sys::console::log_1(&format!("try_connect: Creating WebSocket to {}", self.url).into()); | ||||
|  | ||||
|         // Create WebSocket | ||||
|         let ws = WebSocket::new(&self.url) | ||||
|             .map_err(|e| SigSocketError::Connection(format!("{:?}", e)))?; | ||||
|             .map_err(|e| { | ||||
|                 web_sys::console::error_1(&format!("Failed to create WebSocket: {:?}", e).into()); | ||||
|                 SigSocketError::Connection(format!("{:?}", e)) | ||||
|             })?; | ||||
|  | ||||
|         web_sys::console::log_1(&"try_connect: WebSocket created successfully".into()); | ||||
|  | ||||
|         // Set binary type | ||||
|         ws.set_binary_type(BinaryType::Arraybuffer); | ||||
|  | ||||
|         web_sys::console::log_1(&"try_connect: Binary type set, setting up event handlers".into()); | ||||
|  | ||||
|         let connected = self.connected.clone(); | ||||
|         let public_key = self.public_key.clone(); | ||||
|  | ||||
|         // Set up onopen handler | ||||
|         { | ||||
|             let ws_clone = ws.clone(); | ||||
|             let connected = connected.clone(); | ||||
|             let public_key_clone = public_key.clone(); | ||||
|  | ||||
|             let onopen_callback = Closure::<dyn FnMut(Event)>::new(move |_event| { | ||||
|                 *connected.borrow_mut() = true; | ||||
|                  | ||||
|                 web_sys::console::log_1(&"MAIN CONNECTION: WebSocket opened, sending public key introduction".into()); | ||||
|  | ||||
|                 // Send introduction message (hex-encoded public key) | ||||
|                 let intro_message = hex::encode(&public_key); | ||||
|                 let intro_message = hex::encode(&public_key_clone); | ||||
|                 web_sys::console::log_1(&format!("MAIN CONNECTION: Sending public key: {}", &intro_message[..16]).into()); | ||||
|  | ||||
|                 if let Err(e) = ws_clone.send_with_str(&intro_message) { | ||||
|                     web_sys::console::error_1(&format!("Failed to send introduction: {:?}", e).into()); | ||||
|                     web_sys::console::error_1(&format!("MAIN CONNECTION: Failed to send introduction: {:?}", e).into()); | ||||
|                 } else { | ||||
|                     web_sys::console::log_1(&"MAIN CONNECTION: Public key sent successfully".into()); | ||||
|                 } | ||||
|                  | ||||
|                 web_sys::console::log_1(&"Connected to sigsocket server".into()); | ||||
|             }); | ||||
|              | ||||
|  | ||||
|             ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref())); | ||||
|             onopen_callback.forget(); // Prevent cleanup | ||||
|  | ||||
|             web_sys::console::log_1(&"try_connect: onopen handler set up".into()); | ||||
|         } | ||||
|  | ||||
|         // Set up onmessage handler | ||||
|         { | ||||
|             let ws_clone = ws.clone(); | ||||
|             let handler_clone = self.sign_handler.clone(); | ||||
|             let connected_clone = connected.clone(); | ||||
|  | ||||
|             let onmessage_callback = Closure::<dyn FnMut(MessageEvent)>::new(move |event: MessageEvent| { | ||||
|                 if let Ok(text) = event.data().dyn_into::<js_sys::JsString>() { | ||||
|                     let message = text.as_string().unwrap_or_default(); | ||||
|                     web_sys::console::log_1(&format!("MAIN CONNECTION: Received message: {}", message).into()); | ||||
|  | ||||
|                     // Check if this is the "Connected" acknowledgment | ||||
|                     if message == "Connected" { | ||||
|                         web_sys::console::log_1(&"MAIN CONNECTION: Server acknowledged connection".into()); | ||||
|                         *connected_clone.borrow_mut() = true; | ||||
|                     } | ||||
|  | ||||
|                     // Handle the message with proper sign request support | ||||
|                     Self::handle_message(&message, &ws_clone, &handler_clone); | ||||
|                     Self::handle_message(&message, &ws_clone, &handler_clone, &connected_clone); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref())); | ||||
|             onmessage_callback.forget(); // Prevent cleanup | ||||
|  | ||||
|             web_sys::console::log_1(&"try_connect: onmessage handler set up".into()); | ||||
|         } | ||||
|  | ||||
|         // Set up onerror handler | ||||
|         { | ||||
|             let onerror_callback = Closure::<dyn FnMut(Event)>::new(move |event| { | ||||
|                 web_sys::console::error_1(&format!("WebSocket error: {:?}", event).into()); | ||||
|                 web_sys::console::error_1(&format!("MAIN CONNECTION: WebSocket error: {:?}", event).into()); | ||||
|             }); | ||||
|              | ||||
|  | ||||
|             ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref())); | ||||
|             onerror_callback.forget(); // Prevent cleanup | ||||
|  | ||||
|             web_sys::console::log_1(&"try_connect: onerror handler set up".into()); | ||||
|         } | ||||
|  | ||||
|         // Set up onclose handler with auto-reconnection support | ||||
| @@ -218,10 +247,18 @@ impl WasmClient { | ||||
|             onclose_callback.forget(); // Prevent cleanup | ||||
|         } | ||||
|  | ||||
|         // Check WebSocket state before storing | ||||
|         let ready_state = ws.ready_state(); | ||||
|         web_sys::console::log_1(&format!("try_connect: WebSocket ready state: {}", ready_state).into()); | ||||
|  | ||||
|         self.websocket = Some(ws); | ||||
|  | ||||
|         // Wait for connection to be established | ||||
|         self.wait_for_connection().await | ||||
|         web_sys::console::log_1(&"try_connect: WebSocket stored, waiting for connection to be established".into()); | ||||
|  | ||||
|         // The WebSocket will open asynchronously and the onopen/onmessage handlers will handle the connection | ||||
|         // Since we can see from logs that the connection is working, just return success | ||||
|         web_sys::console::log_1(&"try_connect: WebSocket setup complete, connection will be established asynchronously".into()); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Wait for WebSocket connection to be established | ||||
| @@ -229,66 +266,47 @@ impl WasmClient { | ||||
|         use wasm_bindgen_futures::JsFuture; | ||||
|         use js_sys::Promise; | ||||
|  | ||||
|         // Create a promise that resolves when connected or rejects on timeout | ||||
|         let promise = Promise::new(&mut |resolve, reject| { | ||||
|             let connected = self.connected.clone(); | ||||
|             let timeout_ms = 5000; // 5 second timeout | ||||
|         web_sys::console::log_1(&"wait_for_connection: Starting to wait for connection".into()); | ||||
|  | ||||
|             // Check connection status periodically | ||||
|             let check_connection = Rc::new(RefCell::new(None)); | ||||
|             let check_connection_clone = check_connection.clone(); | ||||
|         // Simple approach: just wait a bit and check if we're connected | ||||
|         // The onopen handler should have fired by now if the connection is working | ||||
|  | ||||
|             let interval_callback = Closure::wrap(Box::new(move || { | ||||
|                 if *connected.borrow() { | ||||
|                     // Connected successfully | ||||
|         let connected = self.connected.clone(); | ||||
|  | ||||
|         // Wait up to 30 seconds, checking every 500ms | ||||
|         for attempt in 1..=60 { | ||||
|             // Check if we're connected | ||||
|             if *connected.borrow() { | ||||
|                 web_sys::console::log_1(&format!("wait_for_connection: Connected after {} attempts ({}ms)", attempt, attempt * 500).into()); | ||||
|                 return Ok(()); | ||||
|             } | ||||
|  | ||||
|             // Wait 500ms before next check | ||||
|             let promise = Promise::new(&mut |resolve, _reject| { | ||||
|                 let timeout_callback = Closure::wrap(Box::new(move || { | ||||
|                     resolve.call0(&wasm_bindgen::JsValue::UNDEFINED).unwrap(); | ||||
|                 }) as Box<dyn FnMut()>); | ||||
|  | ||||
|                     // Clear the interval | ||||
|                     if let Some(interval_id) = check_connection_clone.borrow_mut().take() { | ||||
|                         web_sys::window().unwrap().clear_interval_with_handle(interval_id); | ||||
|                     } | ||||
|                 } | ||||
|             }) as Box<dyn FnMut()>); | ||||
|                 web_sys::window() | ||||
|                     .unwrap() | ||||
|                     .set_timeout_with_callback_and_timeout_and_arguments_0( | ||||
|                         timeout_callback.as_ref().unchecked_ref(), | ||||
|                         500, | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|             // Set up interval to check connection every 100ms | ||||
|             let interval_id = web_sys::window() | ||||
|                 .unwrap() | ||||
|                 .set_interval_with_callback_and_timeout_and_arguments_0( | ||||
|                     interval_callback.as_ref().unchecked_ref(), | ||||
|                     100, | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|                 timeout_callback.forget(); | ||||
|             }); | ||||
|  | ||||
|             *check_connection.borrow_mut() = Some(interval_id); | ||||
|             interval_callback.forget(); | ||||
|             let _ = JsFuture::from(promise).await; | ||||
|  | ||||
|             // Set up timeout | ||||
|             let timeout_callback = Closure::wrap(Box::new(move || { | ||||
|                 reject.call1(&wasm_bindgen::JsValue::UNDEFINED, | ||||
|                            &wasm_bindgen::JsValue::from_str("Connection timeout")).unwrap(); | ||||
|             if attempt % 10 == 0 { | ||||
|                 web_sys::console::log_1(&format!("wait_for_connection: Still waiting... attempt {}/60", attempt).into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|                 // Clear the interval on timeout | ||||
|                 if let Some(interval_id) = check_connection.borrow_mut().take() { | ||||
|                     web_sys::window().unwrap().clear_interval_with_handle(interval_id); | ||||
|                 } | ||||
|             }) as Box<dyn FnMut()>); | ||||
|  | ||||
|             web_sys::window() | ||||
|                 .unwrap() | ||||
|                 .set_timeout_with_callback_and_timeout_and_arguments_0( | ||||
|                     timeout_callback.as_ref().unchecked_ref(), | ||||
|                     timeout_ms, | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
|             timeout_callback.forget(); | ||||
|         }); | ||||
|  | ||||
|         // Wait for the promise to resolve | ||||
|         JsFuture::from(promise).await | ||||
|             .map_err(|_| SigSocketError::Connection("Connection timeout".to_string()))?; | ||||
|  | ||||
|         Ok(()) | ||||
|         web_sys::console::error_1(&"wait_for_connection: Timeout after 30 seconds".into()); | ||||
|         Err(SigSocketError::Connection("Connection timeout".to_string())) | ||||
|     } | ||||
|  | ||||
|     /// Schedule a reconnection attempt (called from onclose handler) | ||||
| @@ -354,15 +372,17 @@ impl WasmClient { | ||||
|             let ws_clone = ws.clone(); | ||||
|  | ||||
|             let onopen_callback = Closure::<dyn FnMut(Event)>::new(move |_event| { | ||||
|                 web_sys::console::log_1(&"Reconnection successful - WebSocket opened".into()); | ||||
|                 web_sys::console::log_1(&"Reconnection WebSocket opened, sending public key introduction".into()); | ||||
|  | ||||
|                 // Send public key introduction | ||||
|                 let public_key_hex = hex::encode(&public_key_clone); | ||||
|                 web_sys::console::log_1(&format!("Reconnection sending public key: {}", &public_key_hex[..16]).into()); | ||||
|  | ||||
|                 if let Err(e) = ws_clone.send_with_str(&public_key_hex) { | ||||
|                     web_sys::console::error_1(&format!("Failed to send public key on reconnection: {:?}", e).into()); | ||||
|                 } else { | ||||
|                     *connected_clone.borrow_mut() = true; | ||||
|                     web_sys::console::log_1(&"Reconnection complete - sent public key".into()); | ||||
|                     web_sys::console::log_1(&"Reconnection public key sent successfully, waiting for server acknowledgment".into()); | ||||
|                     // Don't set connected=true here, wait for "Connected" message | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
| @@ -374,11 +394,12 @@ impl WasmClient { | ||||
|         { | ||||
|             let ws_clone = ws.clone(); | ||||
|             let handler_clone = sign_handler.clone(); | ||||
|             let connected_clone = connected.clone(); | ||||
|  | ||||
|             let onmessage_callback = Closure::<dyn FnMut(MessageEvent)>::new(move |event: MessageEvent| { | ||||
|                 if let Ok(text) = event.data().dyn_into::<js_sys::JsString>() { | ||||
|                     let message = text.as_string().unwrap_or_default(); | ||||
|                     Self::handle_message(&message, &ws_clone, &handler_clone); | ||||
|                     Self::handle_message(&message, &ws_clone, &handler_clone, &connected_clone); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
| @@ -415,13 +436,16 @@ impl WasmClient { | ||||
|     fn handle_message( | ||||
|         text: &str, | ||||
|         ws: &WebSocket, | ||||
|         sign_handler: &Option<Rc<RefCell<Box<dyn SignRequestHandler>>>> | ||||
|         sign_handler: &Option<Rc<RefCell<Box<dyn SignRequestHandler>>>>, | ||||
|         connected: &Rc<RefCell<bool>> | ||||
|     ) { | ||||
|         web_sys::console::log_1(&format!("Received message: {}", text).into()); | ||||
|  | ||||
|         // Handle simple acknowledgment messages | ||||
|         if text == "Connected" { | ||||
|             web_sys::console::log_1(&"Server acknowledged connection".into()); | ||||
|             *connected.borrow_mut() = true; | ||||
|             web_sys::console::log_1(&"Connection state updated to connected".into()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user