leptos_use/
use_broadcast_channel.rs1use crate::core::OptionLocalRwSignal;
2use crate::{
3 UseEventListenerOptions, core::OptionLocalSignal, js, sendwrap_fn, use_event_listener,
4 use_event_listener_with_options, use_supported,
5};
6use codee::{CodecError, Decoder, Encoder};
7use leptos::ev::messageerror;
8use leptos::prelude::*;
9use thiserror::Error;
10use wasm_bindgen::JsValue;
11
12pub fn use_broadcast_channel<T, C>(
81 name: &str,
82) -> UseBroadcastChannelReturn<
83 T,
84 impl Fn(&T) + Clone + Send + Sync + use<T, C>,
85 impl Fn() + Clone + Send + Sync,
86 C,
87>
88where
89 T: Send + Sync,
90 C: Encoder<T, Encoded = String> + Decoder<T, Encoded = str> + Send + Sync,
91 <C as Encoder<T>>::Error: Send + Sync,
92 <C as Decoder<T>>::Error: Send + Sync,
93{
94 let (is_closed, set_closed) = signal(false);
95 let (message, set_message) = signal(None::<T>);
96 let channel = OptionLocalRwSignal::<web_sys::BroadcastChannel>::new();
97 let error = OptionLocalRwSignal::<
98 UseBroadcastChannelError<<C as Encoder<T>>::Error, <C as Decoder<T>>::Error>,
99 >::new();
100
101 let is_supported = use_supported(|| js!("BroadcastChannel" in &window()));
102
103 let post = {
104 sendwrap_fn!(move |data: &T| {
105 if let Some(channel) = channel.get_untracked() {
106 match C::encode(data) {
107 Ok(msg) => {
108 if let Err(err) = channel.post_message(&msg.into()) {
109 error.set(Some(UseBroadcastChannelError::PostMessage(err)))
110 }
111 }
112 Err(err) => {
113 error.set(Some(UseBroadcastChannelError::Codec(CodecError::Encode(
114 err,
115 ))));
116 }
117 }
118 }
119 })
120 };
121
122 let close = {
123 sendwrap_fn!(move || {
124 if let Some(channel) = channel.get_untracked() {
125 channel.close();
126 }
127 set_closed.set(true);
128 })
129 };
130
131 Effect::new({
134 let name = name.to_string();
135 move |_| {
136 if is_supported.get() {
137 let channel_val = web_sys::BroadcastChannel::new(&name).ok();
138 channel.set(channel_val.clone());
139
140 if let Some(channel) = channel_val {
141 let _ = use_event_listener_with_options(
142 channel.clone(),
143 leptos::ev::message,
144 move |event| {
145 if let Some(data) = event.data().as_string() {
146 match C::decode(&data) {
147 Ok(msg) => {
148 set_message.set(Some(msg));
149 }
150 Err(err) => error.set(Some(UseBroadcastChannelError::Codec(
151 CodecError::Decode(err),
152 ))),
153 }
154 } else {
155 error.set(Some(UseBroadcastChannelError::ValueNotString));
156 }
157 },
158 UseEventListenerOptions::default().passive(true),
159 );
160
161 let _ = use_event_listener_with_options(
162 channel.clone(),
163 messageerror,
164 move |event| {
165 error.set(Some(UseBroadcastChannelError::MessageEvent(event)));
166 },
167 UseEventListenerOptions::default().passive(true),
168 );
169
170 let _ = use_event_listener(channel, leptos::ev::close, move |_| {
171 set_closed.set(true)
172 });
173 }
174 }
175 }
176 });
177
178 on_cleanup({
179 let close = close.clone();
180
181 move || {
182 close();
183 }
184 });
185
186 UseBroadcastChannelReturn {
187 is_supported,
188 channel: channel.read_only(),
189 message: message.into(),
190 post,
191 close,
192 error: error.read_only(),
193 is_closed: is_closed.into(),
194 }
195}
196
197pub struct UseBroadcastChannelReturn<T, PFn, CFn, C>
199where
200 T: Send + Sync + 'static,
201 PFn: Fn(&T) + Clone,
202 CFn: Fn() + Clone,
203 C: Encoder<T> + Decoder<T> + Send + Sync,
204{
205 pub is_supported: Signal<bool>,
207
208 pub channel: OptionLocalSignal<web_sys::BroadcastChannel>,
210
211 pub message: Signal<Option<T>>,
213
214 pub post: PFn,
216
217 pub close: CFn,
219
220 pub error: OptionLocalSignal<ErrorType<T, C>>,
222
223 pub is_closed: Signal<bool>,
225}
226
227type ErrorType<T, C> = UseBroadcastChannelError<<C as Encoder<T>>::Error, <C as Decoder<T>>::Error>;
228
229#[derive(Debug, Error)]
230pub enum UseBroadcastChannelError<E, D> {
231 #[error("failed to post message")]
232 PostMessage(JsValue),
233 #[error("channel message error")]
234 MessageEvent(web_sys::MessageEvent),
235 #[error("failed to (de)encode value")]
236 Codec(CodecError<E, D>),
237 #[error("received value is not a string")]
238 ValueNotString,
239}
240
241impl<E, D> Clone for UseBroadcastChannelError<E, D>
242where
243 E: Clone,
244 D: Clone,
245 CodecError<E, D>: Clone,
246{
247 fn clone(&self) -> Self {
248 match self {
249 UseBroadcastChannelError::PostMessage(v) => {
250 UseBroadcastChannelError::PostMessage(v.clone())
251 }
252 UseBroadcastChannelError::MessageEvent(v) => {
253 UseBroadcastChannelError::MessageEvent(v.clone())
254 }
255 UseBroadcastChannelError::Codec(v) => UseBroadcastChannelError::Codec(v.clone()),
256 UseBroadcastChannelError::ValueNotString => UseBroadcastChannelError::ValueNotString,
257 }
258 }
259}