diff --git a/README.md b/README.md index 8d97917..43d2f36 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ cargo test --all-features First you need to install ```shell -cargo install mdbook-cmdrun trunk +cargo install mdbook mdbook-cmdrun trunk ``` To build the book go in your terminal into the docs/book folder diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index 9941910..8e38c3d 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -11,6 +11,10 @@ - [use_session_storage](storage/use_session_storage.md) - [use_storage](storage/use_storage.md) +# @WebSocket + +- [use_websocket](websocket/use_websocket.md) + # Elements - [use_active_element](elements/use_active_element.md) @@ -68,4 +72,4 @@ - [use_floor](math/use_floor.md) - [use_max](math/use_max.md) - [use_min](math/use_min.md) -- [use_round](math/use_round.md) \ No newline at end of file +- [use_round](math/use_round.md) diff --git a/docs/book/src/websocket/use_websocket.md b/docs/book/src/websocket/use_websocket.md new file mode 100644 index 0000000..389c51c --- /dev/null +++ b/docs/book/src/websocket/use_websocket.md @@ -0,0 +1,3 @@ +# use_websocket + + diff --git a/examples/use_websocket/src/main.rs b/examples/use_websocket/src/main.rs index 54a87f2..8b9ddde 100644 --- a/examples/use_websocket/src/main.rs +++ b/examples/use_websocket/src/main.rs @@ -77,34 +77,32 @@ fn Demo(cx: Scope) -> impl IntoView { } = use_websocket_with_options( cx, "wss://echo.websocket.events/".to_string(), - UseWebSocketOptions { - manual: true, - onopen: Some(Box::new(move |e| { + UseWebSocketOptions::default() + .manual(true) + .onopen(Some(Box::new(move |e| { set_history2.update(|history: &mut Vec<_>| { history.push(format! {"[onopen]: event {:?}", e.type_()}) }); - })), - onclose: Some(Box::new(move |e| { + }))) + .onclose(Some(Box::new(move |e| { set_history2.update(|history: &mut Vec<_>| { history.push(format! {"[onclose]: event {:?}", e.type_()}) }); - })), - onerror: Some(Box::new(move |e| { + }))) + .onerror(Some(Box::new(move |e| { set_history2.update(|history: &mut Vec<_>| { history.push(format! {"[onerror]: event {:?}", e.type_()}) }); - })), - onmessage: Some(Box::new(move |m| { + }))) + .onmessage(Some(Box::new(move |m| { set_history2 .update(|history: &mut Vec<_>| history.push(format! {"[onmessage]: {:?}", m})); - })), - onmessage_bytes: Some(Box::new(move |m| { + }))) + .onmessage_bytes(Some(Box::new(move |m| { set_history2.update(|history: &mut Vec<_>| { history.push(format! {"[onmessage_bytes]: {:?}", m}) }); - })), - ..Default::default() - }, + }))), ); let open_connection2 = move |_| { diff --git a/src/websocket/use_websocket.rs b/src/websocket/use_websocket.rs index bff3a70..68deea5 100644 --- a/src/websocket/use_websocket.rs +++ b/src/websocket/use_websocket.rs @@ -4,6 +4,7 @@ use core::fmt; use std::rc::Rc; use std::time::Duration; +use default_struct_builder::DefaultBuilder; use js_sys::Array; use wasm_bindgen::{prelude::*, JsCast, JsValue}; use web_sys::{BinaryType, Event, MessageEvent, WebSocket}; @@ -12,91 +13,67 @@ pub use web_sys::CloseEvent; use crate::utils::CloneableFnMutWithArg; -/// The current state of the `WebSocket` connection. -#[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 `WebSocket`. -// #[derive(DefaultBuilder)] -#[derive(Clone)] -pub struct UseWebSocketOptions { - /// `WebSocket` connect callback. - pub onopen: Option>>, - /// `WebSocket` message callback for text. - pub onmessage: Option>>, - /// `WebSocket` message callback for binary. - pub onmessage_bytes: Option>>>, - /// `WebSocket` error callback. - pub onerror: Option>>, - /// `WebSocket` close callback. - pub onclose: Option>>, - - /// Retry times. - pub reconnect_limit: Option, - /// Retry interval(ms). - pub reconnect_interval: Option, - /// Manually starts connection - pub manual: bool, - /// Sub protocols - pub protocols: Option>, -} - -impl Default for UseWebSocketOptions { - fn default() -> Self { - Self { - onopen: None, - onmessage: None, - onmessage_bytes: None, - onerror: None, - onclose: None, - reconnect_limit: Some(3), - reconnect_interval: Some(3 * 1000), - manual: false, - protocols: Default::default(), - } - } -} - -/// Return type of [`use_websocket`]. -#[derive(Clone)] -pub struct UseWebsocketReturn -where - OpenFn: Fn() + Clone + 'static, - CloseFn: Fn() + Clone + 'static, - SendFn: Fn(String) + Clone + 'static, - SendBytesFn: Fn(Vec) + Clone + 'static, -{ - /// The current state of the `WebSocket` connection. - pub ready_state: ReadSignal, - /// Latest text message received from `WebSocket`. - pub message: ReadSignal>, - /// Latest binary message received from `WebSocket`. - pub message_bytes: ReadSignal>>, - /// The `WebSocket` instance. - pub ws: Option, - - pub open: OpenFn, - pub close: CloseFn, - pub send: SendFn, - pub send_bytes: SendBytesFn, -} - +/// 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, +///
+///

"status: " {status}

+/// button on:click=send_message disabled=move || !connected()>"Send" +/// +/// +/// +///

"Receive message: " {format! {"{:?}", message}}

+///

"Receive byte message: " {format! {"{:?}", message_bytes}}

+///
+/// } +/// # } +/// ``` +// #[doc(cfg(feature = "websocket"))] pub fn use_websocket( cx: Scope, url: String, @@ -110,7 +87,7 @@ pub fn use_websocket( } /// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use. - +// #[doc(cfg(feature = "websocket"))] pub fn use_websocket_with_options( cx: Scope, url: String, @@ -254,7 +231,6 @@ pub fn use_websocket_with_options( } // onerror handler { - // let reconnect = reconnect.clone(); let onerror_closure = Closure::wrap(Box::new(move |e: Event| { if unmounted_ref.get_value() { return; @@ -372,3 +348,92 @@ pub fn use_websocket_with_options( send_bytes, } } + +/// 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"))] +#[derive(DefaultBuilder, Clone)] +pub struct UseWebSocketOptions { + /// `WebSocket` connect callback. + onopen: Option>>, + /// `WebSocket` message callback for text. + onmessage: Option>>, + /// `WebSocket` message callback for binary. + onmessage_bytes: Option>>>, + /// `WebSocket` error callback. + onerror: Option>>, + /// `WebSocket` close callback. + onclose: Option>>, + /// Retry times. + reconnect_limit: Option, + /// Retry interval(ms). + reconnect_interval: Option, + /// Manually starts connection + manual: bool, + /// Sub protocols + protocols: Option>, +} + +impl Default for UseWebSocketOptions { + fn default() -> Self { + Self { + onopen: None, + onmessage: None, + onmessage_bytes: None, + onerror: None, + onclose: None, + 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 +where + OpenFn: Fn() + Clone + 'static, + CloseFn: Fn() + Clone + 'static, + SendFn: Fn(String) + Clone + 'static, + SendBytesFn: Fn(Vec) + Clone + 'static, +{ + /// The current state of the `WebSocket` connection. + pub ready_state: ReadSignal, + /// Latest text message received from `WebSocket`. + pub message: ReadSignal>, + /// Latest binary message received from `WebSocket`. + pub message_bytes: ReadSignal>>, + /// The `WebSocket` instance. + pub ws: Option, + /// 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, +}