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