use std::{cell::Cell, rc::Rc};
use vertigo_macro::store;
use crate::{
DropResource, JsJson, JsJsonSerialize, WebsocketClose, WebsocketConnection, WebsocketMessage,
WebsocketOptions,
dev::{CallbackId, command::WebsocketMessageFromBrowser},
};
use super::{CallbackStore, api_browser_command};
#[store]
pub fn api_websocket() -> Rc<ApiWebsocket> {
ApiWebsocket::new()
}
pub struct ApiWebsocket {
store: CallbackStore<WebsocketMessageFromBrowser, Option<u32>>,
}
impl ApiWebsocket {
fn new() -> Rc<ApiWebsocket> {
Rc::new(ApiWebsocket {
store: CallbackStore::new(),
})
}
pub fn websocket<F: Fn(WebsocketMessage) + 'static>(
&self,
host: impl Into<String>,
options: WebsocketOptions,
callback: F,
) -> DropResource {
let host: String = host.into();
let connect_timeout = options.connect_timeout;
let failures = Cell::new(0_u32);
let (callback_id, drop) = self.store.register_with_id(
move |callback_id, message: WebsocketMessageFromBrowser| match message {
WebsocketMessageFromBrowser::Connected => {
failures.set(0);
let connection = WebsocketConnection::new(callback_id);
callback(WebsocketMessage::Connection(connection));
None
}
WebsocketMessageFromBrowser::Message { message } => {
callback(WebsocketMessage::frame(message));
None
}
WebsocketMessageFromBrowser::Disconnected {
code,
reason,
connected,
} => {
let close = WebsocketClose::new(code, reason, connected);
callback(WebsocketMessage::Close(close.clone()));
if close.is_stopped() {
return None;
}
failures.set(failures.get().saturating_add(1));
let (min, max) = options.reconnect_delay_range(failures.get());
Some(api_browser_command().get_random(min, max))
}
},
);
api_browser_command().websocket_register_callback(&host, callback_id, connect_timeout);
DropResource::new(move || {
api_browser_command().websocket_unregister_callback(callback_id);
drop.off();
})
}
pub fn callback(&self, callback: CallbackId, response: WebsocketMessageFromBrowser) -> JsJson {
match self.store.call(callback, response) {
Some(delay) => delay.to_json(),
None => JsJson::Null,
}
}
}