cyberdeck_client_web_sys/
lib.rs1use std::{collections::HashMap, cell::RefCell, rc::Rc};
2
3use js_sys::{Reflect, JSON, Object, Array, JsString};
4use wasm_bindgen::{prelude::Closure, JsValue, JsCast};
5use web_sys::{Request, RequestInit, RequestMode, Response, RtcPeerConnection, RtcDataChannel, RtcConfiguration, RtcSessionDescriptionInit, window };
6
7pub fn create_peer_connection(ice_server: Option<String>) -> Rc<RefCell<RtcPeerConnection>> {
9 let mut config = RtcConfiguration::new();
10 let config_servers = Array::new();
11 let ice_server_js = Object::new();
12 Reflect::set(&ice_server_js, &"urls".into(), &ice_server.unwrap_or("stun:stun.l.google.com:19302".to_string()).into()).unwrap();
13 config_servers.push(&ice_server_js);
14 config.ice_servers(&config_servers);
15
16 Rc::new(RefCell::new(RtcPeerConnection::new_with_configuration(&config).expect("Failed to create RTCPeerConnection")))
17}
18
19pub async fn init_peer_connection(pc: Rc<RefCell<RtcPeerConnection>>, connect_url: Option<String>, oniceconnectionstatechange: Closure<dyn Fn(JsValue)>) {
21 pc.borrow().set_oniceconnectionstatechange(Some(&oniceconnectionstatechange.into_js_value().unchecked_into()));
22
23 let pc_clone = pc.clone();
24 let connect_url = connect_url.unwrap_or("http://localhost:3000/connect".to_string()).clone();
25
26 let onicecandidate = Closure::<dyn Fn(JsValue)>::new(move |event: JsValue| {
27 if Reflect::get(&event, &"candidate".into()).unwrap().is_null() {
28 let local_description = window().unwrap().btoa(&JSON::stringify(&pc_clone.borrow().local_description().unwrap().unchecked_into()).unwrap().as_string().unwrap()).unwrap();
29 let mut opts = RequestInit::new();
30 opts.method("POST");
31 opts.mode(RequestMode::Cors);
32 opts.body(Some(&JSON::stringify(&local_description.into()).unwrap()));
33
34 let mut headers = HashMap::new();
35 headers.insert("Content-Type", "application/json");
36
37 opts.headers(&serde_wasm_bindgen::to_value(&headers).unwrap());
38 let request = Request::new_with_str_and_init(&connect_url, &opts).unwrap();
39
40 let pc_clone_2 = pc_clone.clone();
41 let then = Closure::<dyn FnMut(JsValue)>::new(move |answer: JsValue| {
42 let answer: Response = answer.unchecked_into();
43
44 let pc_clone_3 = pc_clone_2.clone();
45 let then = Closure::<dyn FnMut(JsValue)>::new(move |answer: JsValue| {
46 let answer: String = answer.unchecked_into::<JsString>().into();
47 let answer = answer.replace("\"", "");
48 let atob = window().unwrap().atob(&answer).unwrap();
49 let parsed = JSON::parse(&atob).unwrap();
50 pc_clone_3.borrow().set_remote_description(
51 &RtcSessionDescriptionInit::unchecked_from_js(parsed)
52 );
53 });
54
55 answer.text().unwrap().then(&then);
56 then.into_js_value();
57 });
58
59 let _fetch = wasm_bindgen_futures::JsFuture::from(window().unwrap().fetch_with_request(&request).then(&then));
60 then.into_js_value();
61 }
62 });
63
64 pc.borrow().set_onicecandidate(Some(&onicecandidate.into_js_value().unchecked_into()));
65
66 let pc_clone = pc.clone();
67 let onnegotiationneeded = Closure::<dyn Fn()>::new(move || {
68 let pc_clone_2 = pc_clone.clone();
69 let then = Closure::<dyn FnMut(JsValue)>::new(move |d: JsValue| {
70 pc_clone_2.borrow().set_local_description(&d.unchecked_into());
71 });
72 pc_clone.borrow().create_offer().then(&then);
73 then.into_js_value();
74 });
75 pc.borrow().set_onnegotiationneeded(Some(&onnegotiationneeded.into_js_value().unchecked_into()));
76}
77
78pub fn create_data_channel(pc: Rc<RefCell<RtcPeerConnection>>, label: &str) -> Rc<RefCell<RtcDataChannel>> {
80 Rc::new(RefCell::new(pc.borrow().create_data_channel(label)))
81}
82
83pub fn init_data_channel(channel: Rc<RefCell<RtcDataChannel>>, onclose: Closure<dyn Fn()>, onopen: Closure<dyn Fn()>, onmessage: Closure<dyn Fn(JsValue)>) {
85 channel.borrow().set_onclose(Some(&onclose.into_js_value().unchecked_into()));
86 channel.borrow().set_onclose(Some(&onopen.into_js_value().unchecked_into()));
87 channel.borrow().set_onmessage(Some(&onmessage.into_js_value().unchecked_into()));
88}