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