oxygengine_editor_tools_backend_web/
broadcast.rs

1use js_sys::{Object, Uint8ClampedArray};
2use oxygengine_backend_web::closure::WebClosure;
3use oxygengine_editor_tools::simp::*;
4use std::{cell::RefCell, collections::VecDeque, rc::Rc};
5use wasm_bindgen::{prelude::*, JsCast};
6use web_sys::{BroadcastChannel, MessageEvent};
7
8pub struct WebBroadcastChannel {
9    name: String,
10    channel: BroadcastChannel,
11    callback: WebClosure,
12    messages: Rc<RefCell<VecDeque<SimpMessage>>>,
13}
14
15#[cfg(feature = "web")]
16unsafe impl Send for WebBroadcastChannel {}
17#[cfg(feature = "web")]
18unsafe impl Sync for WebBroadcastChannel {}
19
20impl WebBroadcastChannel {
21    pub fn new(name: impl ToString) -> Self {
22        let name = name.to_string();
23        let channel = BroadcastChannel::new(&name).unwrap();
24        let messages = Rc::new(RefCell::new(VecDeque::default()));
25        let messages2 = Rc::clone(&messages);
26        let closure = Closure::wrap(Box::new(move |event: MessageEvent| {
27            event.prevent_default();
28            let id = js_sys::Reflect::get(&event.data(), &JsValue::from("id"))
29                .unwrap()
30                .as_string()
31                .unwrap();
32            let version = js_sys::Reflect::get(&event.data(), &JsValue::from("version"))
33                .unwrap()
34                .as_f64()
35                .unwrap() as u32;
36            let text_data = js_sys::Reflect::get(&event.data(), &JsValue::from("text"))
37                .ok()
38                .and_then(|text| text.as_string());
39            let binary_data = js_sys::Reflect::get(&event.data(), &JsValue::from("binary"))
40                .ok()
41                .and_then(|binary| binary.dyn_into::<Uint8ClampedArray>().ok())
42                .map(|binary| binary.to_vec());
43            messages2.borrow_mut().push_back(SimpMessage {
44                id: SimpMessageId::new(id, version),
45                text_data,
46                binary_data,
47            });
48        }) as Box<dyn FnMut(_)>);
49        channel
50            .add_event_listener_with_callback("message", closure.as_ref().unchecked_ref())
51            .unwrap();
52        Self {
53            name,
54            channel,
55            callback: WebClosure::acquire(closure),
56            messages,
57        }
58    }
59
60    pub fn name(&self) -> &str {
61        &self.name
62    }
63}
64
65impl Drop for WebBroadcastChannel {
66    fn drop(&mut self) {
67        self.channel.close();
68        self.callback.release();
69    }
70}
71
72impl SimpSender for WebBroadcastChannel {
73    type Error = ();
74
75    fn write(&mut self, message: SimpMessage) -> Result<(), Self::Error> {
76        let SimpMessage {
77            id,
78            text_data,
79            binary_data,
80        } = message;
81        let SimpMessageId { id, version } = id;
82        let result = Object::new();
83        js_sys::Reflect::set(&result, &JsValue::from("id"), &JsValue::from(id)).unwrap();
84        js_sys::Reflect::set(
85            &result,
86            &JsValue::from("version"),
87            &JsValue::from(version as f64),
88        )
89        .unwrap();
90        if let Some(text_data) = text_data {
91            js_sys::Reflect::set(&result, &JsValue::from("text"), &JsValue::from(text_data))
92                .unwrap();
93        }
94        if let Some(binary_data) = binary_data {
95            let buffer = Uint8ClampedArray::new_with_length(binary_data.len() as _);
96            buffer.copy_from(&binary_data);
97            js_sys::Reflect::set(&result, &JsValue::from("binary"), &buffer).unwrap();
98        }
99        self.channel.post_message(&result).map_err(|_| ())
100    }
101}
102
103impl SimpReceiver for WebBroadcastChannel {
104    type Error = ();
105
106    fn read(&mut self) -> Option<Result<SimpMessage, Self::Error>> {
107        self.messages.borrow_mut().pop_front().map(Ok)
108    }
109}
110
111impl SimpChannel for WebBroadcastChannel {}