use crate::*;
pub(crate) fn use_sse() -> UseSse {
UseSse::new(
use_signal(|| SSE_DEFAULT_URL.to_string()),
use_signal(|| false),
use_signal(|| false),
use_signal(Vec::new),
use_signal(String::new),
)
}
pub(crate) fn sse_on_connect(state: UseSse) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |_event: Event| {
let url: String = state.get_url().get();
if url.is_empty() {
state
.get_error()
.set("Please enter a valid SSE URL".to_string());
return;
}
sse_close_source();
state.get_connecting().set(true);
state.get_error().set(String::new());
state.get_messages().set(Vec::new());
let event_source: EventSource = match EventSource::new(&url) {
Ok(source) => source,
Err(_) => {
state.get_connecting().set(false);
state
.get_error()
.set("Failed to create EventSource".to_string());
return;
}
};
SSE_SOURCE.with(|source: &RefCell<Option<EventSource>>| {
*source.borrow_mut() = Some(event_source.clone());
});
let on_open: Closure<dyn FnMut(JsValue)> = Closure::wrap(Box::new({
let state: UseSse = state;
move |_event: JsValue| {
state.get_connected().set(true);
state.get_connecting().set(false);
}
}));
event_source.set_onopen(Some(on_open.as_ref().unchecked_ref()));
on_open.forget();
let on_message: Closure<dyn FnMut(JsValue)> = Closure::wrap(Box::new({
let state: UseSse = state;
move |event_value: JsValue| {
let message_event: MessageEvent = event_value.unchecked_into();
let js_value: JsValue = message_event.data();
let raw: String = js_value.as_string().unwrap_or_default();
let display: String = JSON::parse(&raw)
.ok()
.and_then(|parsed: JsValue| {
Reflect::get(&parsed, &JsValue::from_str("data")).ok()
})
.and_then(|value: JsValue| value.as_string())
.unwrap_or(raw);
let mut current: Vec<String> = state.get_messages().get();
current.push(display);
if current.len() > SSE_MAX_MESSAGES {
current.drain(0..current.len() - SSE_MAX_MESSAGES);
}
state.get_messages().set(current);
}
}));
event_source.set_onmessage(Some(on_message.as_ref().unchecked_ref()));
on_message.forget();
let on_error: Closure<dyn FnMut(JsValue)> = Closure::wrap(Box::new({
let state: UseSse = state;
move |_event: JsValue| {
state.get_connected().set(false);
state.get_connecting().set(false);
state
.get_error()
.set("SSE connection error or closed".to_string());
}
}));
event_source.set_onerror(Some(on_error.as_ref().unchecked_ref()));
on_error.forget();
}))
}
pub(crate) fn sse_on_disconnect(state: UseSse) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |_event: Event| {
sse_close_source();
state.get_connected().set(false);
state.get_connecting().set(false);
state.get_messages().set(Vec::new());
state.get_error().set(String::new());
}))
}
pub(crate) fn sse_cleanup(state: UseSse) {
use_cleanup(move || {
sse_close_source();
state.get_connected().set(false);
state.get_connecting().set(false);
state.get_error().set(String::new());
});
}
fn sse_close_source() {
SSE_SOURCE.with(|source: &RefCell<Option<EventSource>>| {
if let Some(event_source) = source.borrow_mut().take() {
event_source.close();
}
});
}