Skip to main content

leptos_use_websocket/
options.rs

1use crate::{DummyEncoder, ReconnectLimit};
2use codee::{CodecError, Encoder, HybridEncoder};
3use default_struct_builder::DefaultBuilder;
4use leptos::prelude::*;
5use std::marker::PhantomData;
6use std::sync::Arc;
7use thiserror::Error;
8use web_sys::{CloseEvent, Event};
9
10type ArcFnBytes = Arc<dyn Fn(&[u8]) + Send + Sync>;
11
12/// Options for [`use_websocket_with_options`](crate::use_websocket_with_options).
13#[derive(DefaultBuilder)]
14pub struct UseWebSocketOptions<Rx, E, D, Hb, HbCodec>
15where
16    Rx: ?Sized,
17    Hb: Default + Send + Sync + 'static,
18    HbCodec: Encoder<Hb>,
19    HbCodec: HybridEncoder<
20            Hb,
21            <HbCodec as Encoder<Hb>>::Encoded,
22            Error = <HbCodec as Encoder<Hb>>::Error,
23        >,
24{
25    /// Heartbeat options
26    #[builder(skip)]
27    pub(crate) heartbeat: Option<HeartbeatOptions<Hb, HbCodec>>,
28    /// `WebSocket` connect callback.
29    pub(crate) on_open: Arc<dyn Fn(Event) + Send + Sync>,
30    /// `WebSocket` message callback for typed message decoded by codec.
31    #[builder(skip)]
32    pub(crate) on_message: Arc<dyn Fn(&Rx) + Send + Sync>,
33    /// `WebSocket` message callback for text.
34    pub(crate) on_message_raw: Arc<dyn Fn(&str) + Send + Sync>,
35    /// `WebSocket` message callback for binary.
36    pub(crate) on_message_raw_bytes: ArcFnBytes,
37    /// `WebSocket` error callback.
38    #[builder(skip)]
39    pub(crate) on_error: Arc<dyn Fn(UseWebSocketError<E, D>) + Send + Sync>,
40    /// `WebSocket` close callback.
41    pub(crate) on_close: Arc<dyn Fn(CloseEvent) + Send + Sync>,
42    /// Retry times. Defaults to `ReconnectLimit::Limited(3)`. Use `ReconnectLimit::Infinite` for
43    /// infinite retries.
44    pub(crate) reconnect_limit: ReconnectLimit,
45    /// Retry interval in ms. Defaults to 3000.
46    pub(crate) reconnect_interval: u64,
47    /// If `true` the `WebSocket` connection will immediately be opened when calling this function.
48    /// If `false` you have to manually call the `open` function.
49    /// Defaults to `true`.
50    pub(crate) immediate: bool,
51    /// Sub protocols. See [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#protocols).
52    ///
53    /// Can be set as a signal to support protocols only available after the initial render.
54    ///
55    /// Note that protocols are only updated on the next websocket open() call, not whenever the signal is updated.
56    /// Therefore "lazy" protocols should use the `immediate(false)` option and manually call `open()`.
57    #[builder(into)]
58    pub(crate) protocols: Signal<Option<Vec<String>>>,
59}
60
61impl<Rx: ?Sized, E, D, Hb, HbCodec> UseWebSocketOptions<Rx, E, D, Hb, HbCodec>
62where
63    Hb: Default + Send + Sync + 'static,
64    HbCodec: Encoder<Hb>,
65    HbCodec: HybridEncoder<
66            Hb,
67            <HbCodec as Encoder<Hb>>::Encoded,
68            Error = <HbCodec as Encoder<Hb>>::Error,
69        >,
70{
71    /// `WebSocket` error callback.
72    pub fn on_error<F>(self, handler: F) -> Self
73    where
74        F: Fn(UseWebSocketError<E, D>) + Send + Sync + 'static,
75    {
76        Self {
77            on_error: Arc::new(handler),
78            ..self
79        }
80    }
81
82    /// `WebSocket` message callback for typed message decoded by codec.
83    pub fn on_message<F>(self, handler: F) -> Self
84    where
85        F: Fn(&Rx) + Send + Sync + 'static,
86    {
87        Self {
88            on_message: Arc::new(handler),
89            ..self
90        }
91    }
92
93    /// Set the data, codec and interval at which the heartbeat is sent. The heartbeat
94    /// is the default value of the `NewHb` type.
95    pub fn heartbeat<NewHb, NewHbCodec>(
96        self,
97        interval: u64,
98    ) -> UseWebSocketOptions<Rx, E, D, NewHb, NewHbCodec>
99    where
100        NewHb: Default + Send + Sync + 'static,
101        NewHbCodec: Encoder<NewHb>,
102        NewHbCodec: HybridEncoder<
103                NewHb,
104                <NewHbCodec as Encoder<NewHb>>::Encoded,
105                Error = <NewHbCodec as Encoder<NewHb>>::Error,
106            >,
107    {
108        UseWebSocketOptions {
109            heartbeat: Some(HeartbeatOptions {
110                data: PhantomData::<NewHb>,
111                interval,
112                codec: PhantomData::<NewHbCodec>,
113            }),
114            on_open: self.on_open,
115            on_message: self.on_message,
116            on_message_raw: self.on_message_raw,
117            on_message_raw_bytes: self.on_message_raw_bytes,
118            on_close: self.on_close,
119            on_error: self.on_error,
120            reconnect_limit: self.reconnect_limit,
121            reconnect_interval: self.reconnect_interval,
122            immediate: self.immediate,
123            protocols: self.protocols,
124        }
125    }
126}
127
128impl<Rx: ?Sized, E, D> Default for UseWebSocketOptions<Rx, E, D, (), DummyEncoder> {
129    fn default() -> Self {
130        Self {
131            heartbeat: None,
132            on_open: Arc::new(|_| {}),
133            on_message: Arc::new(|_| {}),
134            on_message_raw: Arc::new(|_| {}),
135            on_message_raw_bytes: Arc::new(|_| {}),
136            on_error: Arc::new(|_| {}),
137            on_close: Arc::new(|_| {}),
138            reconnect_limit: ReconnectLimit::default(),
139            reconnect_interval: 3000,
140            immediate: true,
141            protocols: Default::default(),
142        }
143    }
144}
145
146/// Options for heartbeats
147#[cfg_attr(feature = "ssr", allow(dead_code))]
148pub struct HeartbeatOptions<Hb, HbCodec>
149where
150    Hb: Default + Send + Sync + 'static,
151    HbCodec: Encoder<Hb>,
152    HbCodec: HybridEncoder<
153            Hb,
154            <HbCodec as Encoder<Hb>>::Encoded,
155            Error = <HbCodec as Encoder<Hb>>::Error,
156        >,
157{
158    /// Heartbeat data that will be sent to the server
159    data: PhantomData<Hb>,
160    /// Heartbeat interval in ms. A heartbeat will be sent every `interval` ms.
161    pub(crate) interval: u64,
162    /// Codec used to encode the heartbeat data
163    codec: PhantomData<HbCodec>,
164}
165
166impl<Hb, HbCodec> Clone for HeartbeatOptions<Hb, HbCodec>
167where
168    Hb: Default + Send + Sync + 'static,
169    HbCodec: Encoder<Hb>,
170    HbCodec: HybridEncoder<
171            Hb,
172            <HbCodec as Encoder<Hb>>::Encoded,
173            Error = <HbCodec as Encoder<Hb>>::Error,
174        >,
175{
176    fn clone(&self) -> Self {
177        *self
178    }
179}
180
181impl<Hb, HbCodec> Copy for HeartbeatOptions<Hb, HbCodec>
182where
183    Hb: Default + Send + Sync + 'static,
184    HbCodec: Encoder<Hb>,
185    HbCodec: HybridEncoder<
186            Hb,
187            <HbCodec as Encoder<Hb>>::Encoded,
188            Error = <HbCodec as Encoder<Hb>>::Error,
189        >,
190{
191}
192
193#[derive(Error, Debug)]
194pub enum UseWebSocketError<E, D> {
195    #[error("WebSocket error event")]
196    Event(Event),
197    #[error("WebSocket codec error: {0}")]
198    Codec(#[from] CodecError<E, D>),
199    #[error("WebSocket heartbeat codec error: {0}")]
200    HeartbeatCodec(String),
201}