2023-07-10 15:55:17 +02:00
|
|
|
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
|
|
|
|
|
|
|
|
use core::fmt;
|
|
|
|
use std::rc::Rc;
|
2023-07-11 15:08:45 +02:00
|
|
|
use std::time::Duration;
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-11 19:23:43 +02:00
|
|
|
use default_struct_builder::DefaultBuilder;
|
2023-07-10 15:55:17 +02:00
|
|
|
use js_sys::Array;
|
|
|
|
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
2023-07-12 09:20:43 +02:00
|
|
|
use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-13 08:32:30 +02:00
|
|
|
use crate::utils::CloneableFnWithArg;
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-11 19:23:43 +02:00
|
|
|
/// Creating and managing a [Websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) connection.
|
|
|
|
///
|
|
|
|
/// ## Demo
|
|
|
|
///
|
|
|
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_websocket)
|
|
|
|
///
|
|
|
|
/// ## Usage
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use leptos::*;
|
|
|
|
/// # use leptos_use::websocket::*;
|
|
|
|
/// #
|
|
|
|
/// # #[component]
|
|
|
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
|
|
|
/// let UseWebsocketReturn {
|
|
|
|
/// ready_state,
|
|
|
|
/// message,
|
|
|
|
/// message_bytes,
|
|
|
|
/// send,
|
|
|
|
/// send_bytes,
|
|
|
|
/// open,
|
|
|
|
/// close,
|
|
|
|
/// ..
|
|
|
|
/// } = use_websocket(cx, "wss://echo.websocket.events/".to_string());
|
|
|
|
///
|
|
|
|
/// let send_message = move |_| {
|
|
|
|
/// let m = "Hello, world!".to_string();
|
|
|
|
/// send(m.clone());
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let send_byte_message = move |_| {
|
|
|
|
/// let m = b"Hello, world!\r\n".to_vec();
|
|
|
|
/// send_bytes(m.clone());
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let status = move || ready_state().to_string();
|
|
|
|
///
|
|
|
|
/// let connected = move || ready_state.get() == UseWebSocketReadyState::Open;
|
|
|
|
///
|
|
|
|
/// let open_connection = move |_| {
|
|
|
|
/// open();
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let close_connection = move |_| {
|
|
|
|
/// close();
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// view! { cx,
|
|
|
|
/// <div>
|
|
|
|
/// <p>"status: " {status}</p>
|
|
|
|
/// button on:click=send_message disabled=move || !connected()>"Send"</button>
|
|
|
|
/// <button on:click=send_byte_message disabled=move || !connected()>"Send bytes"</button>
|
|
|
|
/// <button on:click=open_connection disabled=connected>"Open"</button>
|
|
|
|
/// <button on:click=close_connection disabled=move || !connected()>"Close"</button>
|
|
|
|
/// <p>"Receive message: " {format! {"{:?}", message}}</p>
|
|
|
|
/// <p>"Receive byte message: " {format! {"{:?}", message_bytes}}</p>
|
|
|
|
/// </div>
|
|
|
|
/// }
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
// #[doc(cfg(feature = "websocket"))]
|
2023-07-10 15:55:17 +02:00
|
|
|
pub fn use_websocket(
|
|
|
|
cx: Scope,
|
|
|
|
url: String,
|
|
|
|
) -> UseWebsocketReturn<
|
|
|
|
impl Fn() + Clone + 'static,
|
|
|
|
impl Fn() + Clone + 'static,
|
|
|
|
impl Fn(String) + Clone + 'static,
|
|
|
|
impl Fn(Vec<u8>) + Clone + 'static,
|
|
|
|
> {
|
|
|
|
use_websocket_with_options(cx, url, UseWebSocketOptions::default())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use.
|
2023-07-11 19:23:43 +02:00
|
|
|
// #[doc(cfg(feature = "websocket"))]
|
2023-07-10 15:55:17 +02:00
|
|
|
pub fn use_websocket_with_options(
|
|
|
|
cx: Scope,
|
|
|
|
url: String,
|
|
|
|
options: UseWebSocketOptions,
|
|
|
|
) -> UseWebsocketReturn<
|
|
|
|
impl Fn() + Clone + 'static,
|
|
|
|
impl Fn() + Clone + 'static,
|
|
|
|
impl Fn(String) + Clone + 'static,
|
|
|
|
impl Fn(Vec<u8>) + Clone,
|
|
|
|
> {
|
|
|
|
let (ready_state, set_ready_state) = create_signal(cx, UseWebSocketReadyState::Closed);
|
|
|
|
let (message, set_message) = create_signal(cx, None);
|
|
|
|
let (message_bytes, set_message_bytes) = create_signal(cx, None);
|
2023-07-11 15:08:45 +02:00
|
|
|
let ws_ref: StoredValue<Option<WebSocket>> = store_value(cx, None);
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
let onopen_ref = store_value(cx, options.onopen);
|
|
|
|
let onmessage_ref = store_value(cx, options.onmessage);
|
|
|
|
let onmessage_bytes_ref = store_value(cx, options.onmessage_bytes);
|
|
|
|
let onerror_ref = store_value(cx, options.onerror);
|
|
|
|
let onclose_ref = store_value(cx, options.onclose);
|
2023-07-10 15:55:17 +02:00
|
|
|
|
|
|
|
let reconnect_limit = options.reconnect_limit.unwrap_or(3);
|
|
|
|
let reconnect_interval = options.reconnect_interval.unwrap_or(3 * 1000);
|
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(cx, None);
|
2023-07-10 15:55:17 +02:00
|
|
|
let manual = options.manual;
|
|
|
|
let protocols = options.protocols;
|
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
let reconnect_times_ref: StoredValue<u64> = store_value(cx, 0);
|
|
|
|
let unmounted_ref = store_value(cx, false);
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
let connect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(cx, None);
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
let reconnect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(cx, None);
|
|
|
|
reconnect_ref.set_value({
|
|
|
|
let ws = ws_ref.get_value();
|
2023-07-10 15:55:17 +02:00
|
|
|
Some(Rc::new(move || {
|
2023-07-11 15:08:45 +02:00
|
|
|
if reconnect_times_ref.get_value() < reconnect_limit
|
2023-07-10 15:55:17 +02:00
|
|
|
&& ws
|
2023-07-11 15:08:45 +02:00
|
|
|
.clone()
|
|
|
|
.map_or(false, |ws: WebSocket| ws.ready_state() != WebSocket::OPEN)
|
2023-07-10 15:55:17 +02:00
|
|
|
{
|
2023-07-11 15:08:45 +02:00
|
|
|
reconnect_timer_ref.set_value(
|
|
|
|
set_timeout_with_handle(
|
|
|
|
move || {
|
|
|
|
if let Some(connect) = connect_ref.get_value() {
|
|
|
|
connect();
|
|
|
|
reconnect_times_ref.update_value(|current| *current += 1);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Duration::from_millis(reconnect_interval),
|
|
|
|
)
|
|
|
|
.ok(),
|
|
|
|
);
|
2023-07-10 15:55:17 +02:00
|
|
|
}
|
|
|
|
}))
|
2023-07-11 15:08:45 +02:00
|
|
|
});
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
connect_ref.set_value({
|
|
|
|
let ws = ws_ref.get_value();
|
2023-07-10 15:55:17 +02:00
|
|
|
let url = url.clone();
|
|
|
|
|
|
|
|
Some(Rc::new(move || {
|
2023-07-11 15:08:45 +02:00
|
|
|
reconnect_timer_ref.set_value(None);
|
2023-07-10 15:55:17 +02:00
|
|
|
{
|
2023-07-11 15:08:45 +02:00
|
|
|
if let Some(web_socket) = &ws {
|
2023-07-10 15:55:17 +02:00
|
|
|
let _ = web_socket.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let web_socket = {
|
|
|
|
protocols.as_ref().map_or_else(
|
|
|
|
|| WebSocket::new(&url).unwrap_throw(),
|
|
|
|
|protocols| {
|
|
|
|
let array = protocols
|
|
|
|
.iter()
|
|
|
|
.map(|p| JsValue::from(p.clone()))
|
|
|
|
.collect::<Array>();
|
|
|
|
WebSocket::new_with_str_sequence(&url, &JsValue::from(&array))
|
|
|
|
.unwrap_throw()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
};
|
|
|
|
web_socket.set_binary_type(BinaryType::Arraybuffer);
|
|
|
|
set_ready_state.set(UseWebSocketReadyState::Connecting);
|
|
|
|
|
|
|
|
// onopen handler
|
|
|
|
{
|
|
|
|
let onopen_closure = Closure::wrap(Box::new(move |e: Event| {
|
2023-07-11 15:08:45 +02:00
|
|
|
if unmounted_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-13 08:32:30 +02:00
|
|
|
let onopen = onopen_ref.get_value();
|
2023-07-12 09:20:43 +02:00
|
|
|
onopen(e);
|
|
|
|
|
2023-07-10 15:55:17 +02:00
|
|
|
set_ready_state.set(UseWebSocketReadyState::Open);
|
|
|
|
}) as Box<dyn FnMut(Event)>);
|
|
|
|
web_socket.set_onopen(Some(onopen_closure.as_ref().unchecked_ref()));
|
|
|
|
// Forget the closure to keep it alive
|
|
|
|
onopen_closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// onmessage handler
|
|
|
|
{
|
|
|
|
let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| {
|
2023-07-11 15:08:45 +02:00
|
|
|
if unmounted_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.data().dyn_into::<js_sys::ArrayBuffer>().map_or_else(
|
|
|
|
|_| {
|
|
|
|
e.data().dyn_into::<js_sys::JsString>().map_or_else(
|
|
|
|
|_| {
|
|
|
|
unreachable!("message event, received Unknown: {:?}", e.data());
|
|
|
|
},
|
|
|
|
|txt| {
|
|
|
|
let txt = String::from(&txt);
|
2023-07-13 08:32:30 +02:00
|
|
|
let onmessage = onmessage_ref.get_value();
|
2023-07-12 09:20:43 +02:00
|
|
|
onmessage(txt.clone());
|
|
|
|
|
2023-07-10 15:55:17 +02:00
|
|
|
set_message.set(Some(txt.clone()));
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|array_buffer| {
|
|
|
|
let array = js_sys::Uint8Array::new(&array_buffer);
|
|
|
|
let array = array.to_vec();
|
2023-07-13 08:32:30 +02:00
|
|
|
let onmessage_bytes = onmessage_bytes_ref.get_value();
|
2023-07-12 09:20:43 +02:00
|
|
|
onmessage_bytes(array.clone());
|
|
|
|
|
2023-07-10 15:55:17 +02:00
|
|
|
set_message_bytes.set(Some(array));
|
|
|
|
},
|
|
|
|
);
|
|
|
|
})
|
|
|
|
as Box<dyn FnMut(MessageEvent)>);
|
|
|
|
web_socket.set_onmessage(Some(onmessage_closure.as_ref().unchecked_ref()));
|
|
|
|
onmessage_closure.forget();
|
|
|
|
}
|
|
|
|
// onerror handler
|
|
|
|
{
|
|
|
|
let onerror_closure = Closure::wrap(Box::new(move |e: Event| {
|
2023-07-11 15:08:45 +02:00
|
|
|
if unmounted_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
if let Some(reconnect) = &reconnect_ref.get_value() {
|
|
|
|
reconnect();
|
|
|
|
}
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-13 08:32:30 +02:00
|
|
|
let onerror = onerror_ref.get_value();
|
2023-07-12 09:20:43 +02:00
|
|
|
onerror(e);
|
|
|
|
|
2023-07-10 15:55:17 +02:00
|
|
|
set_ready_state.set(UseWebSocketReadyState::Closed);
|
|
|
|
}) as Box<dyn FnMut(Event)>);
|
|
|
|
web_socket.set_onerror(Some(onerror_closure.as_ref().unchecked_ref()));
|
|
|
|
onerror_closure.forget();
|
|
|
|
}
|
|
|
|
// onclose handler
|
|
|
|
{
|
|
|
|
let onclose_closure = Closure::wrap(Box::new(move |e: CloseEvent| {
|
2023-07-11 15:08:45 +02:00
|
|
|
if unmounted_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
if let Some(reconnect) = &reconnect_ref.get_value() {
|
|
|
|
reconnect();
|
|
|
|
}
|
2023-07-10 15:55:17 +02:00
|
|
|
|
2023-07-13 08:32:30 +02:00
|
|
|
let onclose = onclose_ref.get_value();
|
2023-07-12 09:20:43 +02:00
|
|
|
onclose(e);
|
|
|
|
|
2023-07-10 15:55:17 +02:00
|
|
|
set_ready_state.set(UseWebSocketReadyState::Closed);
|
|
|
|
})
|
|
|
|
as Box<dyn FnMut(CloseEvent)>);
|
|
|
|
web_socket.set_onclose(Some(onclose_closure.as_ref().unchecked_ref()));
|
|
|
|
onclose_closure.forget();
|
|
|
|
}
|
|
|
|
|
2023-07-11 15:08:45 +02:00
|
|
|
ws_ref.set_value(Some(web_socket));
|
2023-07-10 15:55:17 +02:00
|
|
|
}))
|
2023-07-11 15:08:45 +02:00
|
|
|
});
|
2023-07-10 15:55:17 +02:00
|
|
|
|
|
|
|
// Send text (String)
|
|
|
|
let send = {
|
|
|
|
Box::new(move |data: String| {
|
|
|
|
if ready_state.get() == UseWebSocketReadyState::Open {
|
2023-07-11 15:08:45 +02:00
|
|
|
if let Some(web_socket) = ws_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
let _ = web_socket.send_with_str(&data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
// Send bytes
|
|
|
|
let send_bytes = {
|
|
|
|
move |data: Vec<u8>| {
|
|
|
|
if ready_state.get() == UseWebSocketReadyState::Open {
|
2023-07-11 15:08:45 +02:00
|
|
|
if let Some(web_socket) = ws_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
let _ = web_socket.send_with_u8_array(&data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Open connection
|
|
|
|
let open = {
|
|
|
|
move || {
|
2023-07-11 15:08:45 +02:00
|
|
|
reconnect_times_ref.set_value(0);
|
|
|
|
if let Some(connect) = connect_ref.get_value() {
|
|
|
|
connect();
|
|
|
|
}
|
2023-07-10 15:55:17 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Close connection
|
|
|
|
let close = {
|
2023-07-11 15:08:45 +02:00
|
|
|
reconnect_timer_ref.set_value(None);
|
2023-07-10 15:55:17 +02:00
|
|
|
move || {
|
2023-07-11 15:08:45 +02:00
|
|
|
reconnect_times_ref.set_value(reconnect_limit);
|
|
|
|
if let Some(web_socket) = ws_ref.get_value() {
|
2023-07-10 15:55:17 +02:00
|
|
|
let _ = web_socket.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Open connection (not called if option `manual` is true)
|
|
|
|
{
|
|
|
|
let open = open.clone();
|
|
|
|
create_effect(cx, move |_| {
|
|
|
|
if !manual {
|
|
|
|
open();
|
|
|
|
}
|
|
|
|
|
|
|
|
|| ()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// clean up (unmount)
|
|
|
|
{
|
|
|
|
let close = close.clone();
|
|
|
|
on_cleanup(cx, move || {
|
2023-07-11 15:08:45 +02:00
|
|
|
unmounted_ref.set_value(true);
|
2023-07-10 15:55:17 +02:00
|
|
|
close();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
UseWebsocketReturn {
|
|
|
|
ready_state,
|
|
|
|
message,
|
|
|
|
message_bytes,
|
2023-07-11 15:08:45 +02:00
|
|
|
ws: ws_ref.get_value(),
|
2023-07-10 15:55:17 +02:00
|
|
|
open,
|
|
|
|
close,
|
|
|
|
send,
|
|
|
|
send_bytes,
|
|
|
|
}
|
|
|
|
}
|
2023-07-11 19:23:43 +02:00
|
|
|
|
|
|
|
/// The current state of the `WebSocket` connection.
|
|
|
|
// #[doc(cfg(feature = "websocket"))]
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum UseWebSocketReadyState {
|
|
|
|
Connecting,
|
|
|
|
Open,
|
|
|
|
Closing,
|
|
|
|
Closed,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for UseWebSocketReadyState {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match *self {
|
|
|
|
UseWebSocketReadyState::Connecting => write!(f, "Connecting"),
|
|
|
|
UseWebSocketReadyState::Open => write!(f, "Open"),
|
|
|
|
UseWebSocketReadyState::Closing => write!(f, "Closing"),
|
|
|
|
UseWebSocketReadyState::Closed => write!(f, "Closed"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Options for [`use_websocket_with_options`].
|
|
|
|
// #[doc(cfg(feature = "websocket"))]
|
2023-07-12 09:20:43 +02:00
|
|
|
#[derive(DefaultBuilder)]
|
2023-07-11 19:23:43 +02:00
|
|
|
pub struct UseWebSocketOptions {
|
|
|
|
/// `WebSocket` connect callback.
|
2023-07-13 08:32:30 +02:00
|
|
|
onopen: Box<dyn CloneableFnWithArg<Event> + 'static>,
|
2023-07-11 19:23:43 +02:00
|
|
|
/// `WebSocket` message callback for text.
|
2023-07-13 08:32:30 +02:00
|
|
|
onmessage: Box<dyn CloneableFnWithArg<String> + 'static>,
|
2023-07-11 19:23:43 +02:00
|
|
|
/// `WebSocket` message callback for binary.
|
2023-07-13 08:32:30 +02:00
|
|
|
onmessage_bytes: Box<dyn CloneableFnWithArg<Vec<u8>> + 'static>,
|
2023-07-11 19:23:43 +02:00
|
|
|
/// `WebSocket` error callback.
|
2023-07-13 08:32:30 +02:00
|
|
|
onerror: Box<dyn CloneableFnWithArg<Event> + 'static>,
|
2023-07-11 19:23:43 +02:00
|
|
|
/// `WebSocket` close callback.
|
2023-07-13 08:32:30 +02:00
|
|
|
onclose: Box<dyn CloneableFnWithArg<CloseEvent> + 'static>,
|
2023-07-11 19:23:43 +02:00
|
|
|
/// Retry times.
|
|
|
|
reconnect_limit: Option<u64>,
|
|
|
|
/// Retry interval(ms).
|
|
|
|
reconnect_interval: Option<u64>,
|
|
|
|
/// Manually starts connection
|
|
|
|
manual: bool,
|
|
|
|
/// Sub protocols
|
|
|
|
protocols: Option<Vec<String>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for UseWebSocketOptions {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2023-07-12 09:20:43 +02:00
|
|
|
onopen: Box::new(|_| {}),
|
|
|
|
onmessage: Box::new(|_| {}),
|
|
|
|
onmessage_bytes: Box::new(|_| {}),
|
|
|
|
onerror: Box::new(|_| {}),
|
|
|
|
onclose: Box::new(|_| {}),
|
2023-07-11 19:23:43 +02:00
|
|
|
reconnect_limit: Some(3),
|
|
|
|
reconnect_interval: Some(3 * 1000),
|
|
|
|
manual: false,
|
|
|
|
protocols: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return type of [`use_websocket`].
|
|
|
|
// #[doc(cfg(feature = "websocket"))]
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct UseWebsocketReturn<OpenFn, CloseFn, SendFn, SendBytesFn>
|
|
|
|
where
|
|
|
|
OpenFn: Fn() + Clone + 'static,
|
|
|
|
CloseFn: Fn() + Clone + 'static,
|
|
|
|
SendFn: Fn(String) + Clone + 'static,
|
|
|
|
SendBytesFn: Fn(Vec<u8>) + Clone + 'static,
|
|
|
|
{
|
|
|
|
/// The current state of the `WebSocket` connection.
|
|
|
|
pub ready_state: ReadSignal<UseWebSocketReadyState>,
|
|
|
|
/// Latest text message received from `WebSocket`.
|
|
|
|
pub message: ReadSignal<Option<String>>,
|
|
|
|
/// Latest binary message received from `WebSocket`.
|
|
|
|
pub message_bytes: ReadSignal<Option<Vec<u8>>>,
|
|
|
|
/// The `WebSocket` instance.
|
|
|
|
pub ws: Option<WebSocket>,
|
|
|
|
/// Opens the `WebSocket` connection
|
|
|
|
pub open: OpenFn,
|
|
|
|
/// Closes the `WebSocket` connection
|
|
|
|
pub close: CloseFn,
|
|
|
|
/// Sends `text` (string) based data
|
|
|
|
pub send: SendFn,
|
|
|
|
/// Sends binary data
|
|
|
|
pub send_bytes: SendBytesFn,
|
|
|
|
}
|