leptos_use/
use_broadcast_channel.rs1use crate::core::OptionLocalRwSignal;
2use crate::{
3 UseEventListenerOptions, core::OptionLocalSignal, js, sendwrap_fn,
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,
163 messageerror,
164 move |event| {
165 error.set(Some(UseBroadcastChannelError::MessageEvent(event)));
166 },
167 UseEventListenerOptions::default().passive(true),
168 );
169 }
170 }
171 }
172 });
173
174 on_cleanup({
175 let close = close.clone();
176
177 move || {
178 close();
179 }
180 });
181
182 UseBroadcastChannelReturn {
183 is_supported,
184 channel: channel.read_only(),
185 message: message.into(),
186 post,
187 close,
188 error: error.read_only(),
189 is_closed: is_closed.into(),
190 }
191}
192
193pub struct UseBroadcastChannelReturn<T, PFn, CFn, C>
195where
196 T: Send + Sync + 'static,
197 PFn: Fn(&T) + Clone,
198 CFn: Fn() + Clone,
199 C: Encoder<T> + Decoder<T> + Send + Sync,
200{
201 pub is_supported: Signal<bool>,
203
204 pub channel: OptionLocalSignal<web_sys::BroadcastChannel>,
206
207 pub message: Signal<Option<T>>,
209
210 pub post: PFn,
212
213 pub close: CFn,
215
216 pub error: OptionLocalSignal<ErrorType<T, C>>,
218
219 pub is_closed: Signal<bool>,
221}
222
223type ErrorType<T, C> = UseBroadcastChannelError<<C as Encoder<T>>::Error, <C as Decoder<T>>::Error>;
224
225#[derive(Debug, Error)]
226pub enum UseBroadcastChannelError<E, D> {
227 #[error("failed to post message")]
228 PostMessage(JsValue),
229 #[error("channel message error")]
230 MessageEvent(web_sys::MessageEvent),
231 #[error("failed to (de)encode value")]
232 Codec(CodecError<E, D>),
233 #[error("received value is not a string")]
234 ValueNotString,
235}
236
237impl<E, D> Clone for UseBroadcastChannelError<E, D>
238where
239 E: Clone,
240 D: Clone,
241 CodecError<E, D>: Clone,
242{
243 fn clone(&self) -> Self {
244 match self {
245 UseBroadcastChannelError::PostMessage(v) => {
246 UseBroadcastChannelError::PostMessage(v.clone())
247 }
248 UseBroadcastChannelError::MessageEvent(v) => {
249 UseBroadcastChannelError::MessageEvent(v.clone())
250 }
251 UseBroadcastChannelError::Codec(v) => UseBroadcastChannelError::Codec(v.clone()),
252 UseBroadcastChannelError::ValueNotString => UseBroadcastChannelError::ValueNotString,
253 }
254 }
255}