use any_spawner::Executor;
use futures::StreamExt;
use leptos::prelude::*;
use super::FnError;
use super::live::StreamEvent;
use super::live::client_if_any;
use crate::StreamOperation;
use crate::live::EndReason;
const WINDOW: u32 = 64;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StreamState {
Opening,
Open,
Done,
Failed(FnError),
Interrupted,
}
impl StreamState {
#[must_use]
pub fn is_finished(&self) -> bool {
matches!(self, Self::Done | Self::Failed(_) | Self::Interrupted)
}
}
pub struct StreamHandle<T: Send + Sync + 'static> {
items: RwSignal<Vec<T>>,
state: RwSignal<StreamState>,
}
impl<T: Send + Sync + 'static> Clone for StreamHandle<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Send + Sync + 'static> Copy for StreamHandle<T> {}
impl<T: Clone + Send + Sync + 'static> StreamHandle<T> {
pub fn items(&self) -> ReadSignal<Vec<T>> {
self.items.read_only()
}
pub fn state(&self) -> ReadSignal<StreamState> {
self.state.read_only()
}
pub fn clear(&self) {
self.items.set(Vec::new());
}
}
#[track_caller]
pub fn use_stream<S>(args: impl Fn() -> S + Send + Sync + 'static) -> StreamHandle<S::Item>
where
S: StreamOperation,
S::Item: Clone + Send + Sync,
{
let items = RwSignal::new(Vec::new());
let state = RwSignal::new(StreamState::Opening);
let open = StoredValue::new(None::<futures::channel::oneshot::Sender<()>>);
Effect::new_isomorphic(move |_| {
let args = args();
let Some(live) = client_if_any() else {
return;
};
open.set_value(None);
let Ok(value) = serde_json::to_value(&args) else {
state.set(StreamState::Failed(FnError::Invalid(
"the stream's arguments aren't JSON".to_owned(),
)));
return;
};
items.set(Vec::new());
state.set(StreamState::Opening);
let stream = live.stream(S::NAME, value, WINDOW, S::RESUMABLE);
let (stop, stopped) = futures::channel::oneshot::channel();
open.set_value(Some(stop));
Executor::spawn_local(follow::<S::Item>(stream, stopped, items, state));
});
on_cleanup(move || {
let _ = open.try_set_value(None);
});
StreamHandle { items, state }
}
async fn follow<T: serde::de::DeserializeOwned + Send + Sync + 'static>(
mut stream: super::live::LiveStream,
mut stopped: futures::channel::oneshot::Receiver<()>,
items: RwSignal<Vec<T>>,
state: RwSignal<StreamState>,
) {
loop {
let event = match futures::future::select(stream.next(), &mut stopped).await {
futures::future::Either::Left((event, _)) => event,
futures::future::Either::Right(_) => return,
};
let shown = match event {
Some(StreamEvent::Item(value)) => {
if let Ok(item) = serde_json::from_value::<T>(value) {
let _ = items.try_update(|items| items.push(item));
}
state.try_update(opened).is_some()
}
Some(StreamEvent::End(reason)) => {
let _ = state.try_set(match reason {
EndReason::Done => StreamState::Done,
EndReason::Interrupted => StreamState::Interrupted,
EndReason::Failed(error) => StreamState::Failed(super::live::fn_error(error)),
});
false
}
None => false,
};
if !shown {
return;
}
}
}
fn opened(state: &mut StreamState) {
if *state == StreamState::Opening {
*state = StreamState::Open;
}
}