add a sse demo
This commit is contained in:
@ -10,6 +10,8 @@ pub enum Route {
|
||||
Home,
|
||||
#[at("/counter")]
|
||||
Counter,
|
||||
#[at("/echo")]
|
||||
Echo,
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
@ -22,6 +24,7 @@ fn switch(routes: Route) -> Html {
|
||||
Route::Home => html! { <IndexPage /> },
|
||||
Route::Counter => html! { <CounterPage /> },
|
||||
Route::NotFound => html! { <NotFound /> },
|
||||
Route::Echo => html! { <EchoPage /> },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ pub fn nav_bar() -> Html {
|
||||
for (route, title) in [
|
||||
(Route::Home, "Home"),
|
||||
(Route::Counter, "Counter"),
|
||||
(Route::Echo, "Echo"),
|
||||
] {
|
||||
let active_item = if let Some(ref location) = maybe_location {
|
||||
let path = location.path();
|
||||
|
@ -1,7 +1,9 @@
|
||||
pub mod index;
|
||||
pub mod counter;
|
||||
pub mod echo;
|
||||
pub mod _404;
|
||||
|
||||
pub use counter::CounterPage;
|
||||
pub use echo::EchoPage;
|
||||
pub use index::IndexPage;
|
||||
pub use _404::NotFound;
|
114
src/frontend/pages/echo.rs
Normal file
114
src/frontend/pages/echo.rs
Normal file
@ -0,0 +1,114 @@
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::prelude::*;
|
||||
use yew::{function_component, html, use_state_eq, Callback, Html, InputEvent};
|
||||
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
struct ReducibleString(String);
|
||||
enum StringReducer {
|
||||
Append(String),
|
||||
}
|
||||
|
||||
impl Reducible for ReducibleString {
|
||||
type Action = StringReducer;
|
||||
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
|
||||
match action {
|
||||
StringReducer::Append(string) => Rc::new(Self(format!("{}{}", self.0, string))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn EchoPage() -> Html {
|
||||
let response = use_reducer(ReducibleString::default);
|
||||
|
||||
let name = use_state_eq(|| String::new());
|
||||
let name_setter_for_input = {
|
||||
let name = name.clone();
|
||||
Callback::from(move |ev: InputEvent| {
|
||||
let name_writer = name.setter();
|
||||
let Some(target) = ev.target() else {
|
||||
return;
|
||||
};
|
||||
// Events can bubble so this listener might catch events from child
|
||||
// elements which are not of type HtmlInputElement
|
||||
let input = target.dyn_into::<HtmlInputElement>().ok();
|
||||
|
||||
if let Some(input) = input {
|
||||
name_writer.set(input.value());
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let start_sse = {
|
||||
let name = name.clone();
|
||||
let response = response.clone();
|
||||
Callback::from(move |_| {
|
||||
let response = response.clone();
|
||||
let url = format!("/sse/echo?s={}", *name);
|
||||
let mut eventsource = match gloo::net::eventsource::futures::EventSource::new(&url) {
|
||||
Ok(eventsource) => eventsource,
|
||||
Err(err) => {
|
||||
gloo::console::error!(format!("failed to open event source: {}", err));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut subscription = match eventsource.subscribe("message") {
|
||||
Ok(subscription) => subscription,
|
||||
Err(err) => {
|
||||
gloo::console::error!(format!("failed to subscribe on event source: {}", err));
|
||||
return;
|
||||
}
|
||||
};
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
loop {
|
||||
futures::select_biased! {
|
||||
message = subscription.next().fuse() => {
|
||||
let Some(data) = message else {
|
||||
continue
|
||||
};
|
||||
let (_, event) = match data {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
gloo::console::error!(format!("failed to fetch data from event source: {}", err));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(data) = event.data().as_string() else {
|
||||
continue
|
||||
};
|
||||
|
||||
response.dispatch(StringReducer::Append(data));
|
||||
}, // subscription.next
|
||||
_ = gloo::timers::future::sleep(Duration::from_secs(5)).fuse() => {
|
||||
break
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
gloo::console::log!("eventsource 5: ", format!("{:?}", eventsource.state()));
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
if (*response).0.len() > 0 {
|
||||
let response = (*response).clone().0;
|
||||
html! {
|
||||
<>
|
||||
{response}
|
||||
</>
|
||||
}
|
||||
} else {
|
||||
let name = (*name).clone();
|
||||
html! {
|
||||
<>
|
||||
<input value={name} oninput={name_setter_for_input} />
|
||||
<button onclick={start_sse}>{"Hi"}</button>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user