mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-02-08 21:33:09 -05:00
client opened bidir streams work
This commit is contained in:
parent
9493b690ff
commit
d2a91b4d6d
5 changed files with 145 additions and 15 deletions
12
examples/use_webtransport/src/log_display.rs
Normal file
12
examples/use_webtransport/src/log_display.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use leptos::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn LogDisplay(#[prop(into)] log: Signal<Vec<String>>) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
{move || log().iter().map(|l| view! { <li>{l}</li> }).collect::<Vec<_>>()}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,10 +3,20 @@ use leptos_use::core::ConnectionReadyState;
|
||||||
use leptos_use::docs::demo_or_body;
|
use leptos_use::docs::demo_or_body;
|
||||||
use leptos_use::{use_webtransport_with_options, UseWebTransportOptions};
|
use leptos_use::{use_webtransport_with_options, UseWebTransportOptions};
|
||||||
|
|
||||||
|
mod log_display;
|
||||||
|
mod stream_bidir;
|
||||||
|
mod stream_send;
|
||||||
|
|
||||||
|
use log_display::*;
|
||||||
|
use stream_bidir::*;
|
||||||
|
use stream_send::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Demo() -> impl IntoView {
|
fn Demo() -> impl IntoView {
|
||||||
let (datagrams_log, set_datagrams_log) = create_signal(vec![]);
|
let (datagrams_log, set_datagrams_log) = create_signal(vec![]);
|
||||||
|
|
||||||
|
let id = store_value(0);
|
||||||
|
|
||||||
let transport = use_webtransport_with_options(
|
let transport = use_webtransport_with_options(
|
||||||
"https://echo.webtransport.day",
|
"https://echo.webtransport.day",
|
||||||
UseWebTransportOptions::default()
|
UseWebTransportOptions::default()
|
||||||
|
@ -15,16 +25,15 @@ fn Demo() -> impl IntoView {
|
||||||
})
|
})
|
||||||
.on_close(move || {
|
.on_close(move || {
|
||||||
set_datagrams_log.update(|log| log.push("Connection closed".to_string()))
|
set_datagrams_log.update(|log| log.push("Connection closed".to_string()))
|
||||||
})
|
}),
|
||||||
.on_error(move |e| set_datagrams_log.update(|log| log.push(format!("Error: {:?}", e)))),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let (text, set_text) = create_signal("".to_string());
|
let (text, set_text) = create_signal("".to_string());
|
||||||
|
|
||||||
let on_send = {
|
let on_send_datagrams = {
|
||||||
let transport = transport.clone();
|
let transport = transport.clone();
|
||||||
|
|
||||||
move |e| {
|
move |_| {
|
||||||
set_datagrams_log.update(|log| log.push(format!("Sent datagram: '{}'", text())));
|
set_datagrams_log.update(|log| log.push(format!("Sent datagram: '{}'", text())));
|
||||||
|
|
||||||
transport.send_datagrams(text().as_bytes());
|
transport.send_datagrams(text().as_bytes());
|
||||||
|
@ -47,18 +56,48 @@ fn Demo() -> impl IntoView {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let (bidir_streams, set_bidir_streams) = create_signal(vec![]);
|
||||||
|
|
||||||
|
let on_open_bidir_stream = {
|
||||||
|
let transport = transport.clone();
|
||||||
|
|
||||||
|
move |_| {
|
||||||
|
let transport = transport.clone();
|
||||||
|
|
||||||
|
spawn_local(async move {
|
||||||
|
match transport.create_bidir_stream().await {
|
||||||
|
Ok(bidir_stream) => {
|
||||||
|
let i = id.get_value();
|
||||||
|
id.set_value(i + 1);
|
||||||
|
|
||||||
|
set_bidir_streams.update(|s| s.push((i, bidir_stream)));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
set_datagrams_log.update(|log| {
|
||||||
|
log.push(format!("Failed to open bidir stream: {:?}", e))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let ready_state = transport.ready_state;
|
let ready_state = transport.ready_state;
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
|
<button on:click=on_open_bidir_stream>"Open Bidir Stream"</button>
|
||||||
<h2>Datagrams</h2>
|
<h2>Datagrams</h2>
|
||||||
<textarea on:change=move |e| set_text(event_target_value(&e)) prop:value=text />
|
<textarea on:change=move |e| set_text(event_target_value(&e)) prop:value=text />
|
||||||
<button on:click=on_send disabled=move || ready_state() != ConnectionReadyState::Open>"Send"</button>
|
<button on:click=on_send_datagrams disabled=move || ready_state() != ConnectionReadyState::Open>"Send"</button>
|
||||||
|
|
||||||
<div>
|
<LogDisplay log=datagrams_log />
|
||||||
<ul>
|
|
||||||
{move || datagrams_log().iter().map(|l| view! { <li>{l}</li> }).collect::<Vec<_>>()}
|
<h2>Bidir Streams</h2>
|
||||||
</ul>
|
<For
|
||||||
</div>
|
each=bidir_streams
|
||||||
|
key=|(i, _)| *i
|
||||||
|
view=move |(_, bidir_stream)| view! { <StreamBidir ready_state=ready_state stream=bidir_stream.clone() /> }
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
examples/use_webtransport/src/stream_bidir.rs
Normal file
36
examples/use_webtransport/src/stream_bidir.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use crate::{LogDisplay, StreamSend};
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_use::core::ConnectionReadyState;
|
||||||
|
use leptos_use::BidirStream;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn StreamBidir(
|
||||||
|
#[prop(into)] ready_state: Signal<ConnectionReadyState>,
|
||||||
|
stream: BidirStream,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let (log, set_log) = create_signal(vec![]);
|
||||||
|
|
||||||
|
let on_send = move |msg| {
|
||||||
|
set_log.update(|log| log.push(format!("Sent: '{}'", msg)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = watch(
|
||||||
|
stream.bytes,
|
||||||
|
move |bytes, _, _| {
|
||||||
|
if let Some(bytes) = bytes {
|
||||||
|
set_log.update(|log| {
|
||||||
|
log.push(format!(
|
||||||
|
"Received datagrams: '{}'",
|
||||||
|
String::from_utf8(bytes.clone()).expect("valid utf8")
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<StreamSend ready_state=ready_state send_stream=stream.clone() on_send=on_send />
|
||||||
|
<LogDisplay log=log />
|
||||||
|
}
|
||||||
|
}
|
29
examples/use_webtransport/src/stream_send.rs
Normal file
29
examples/use_webtransport/src/stream_send.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_use::core::ConnectionReadyState;
|
||||||
|
use leptos_use::SendableStream;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn StreamSend<S, F>(
|
||||||
|
#[prop(into)] ready_state: Signal<ConnectionReadyState>,
|
||||||
|
send_stream: S,
|
||||||
|
on_send: F,
|
||||||
|
) -> impl IntoView
|
||||||
|
where
|
||||||
|
S: SendableStream + 'static,
|
||||||
|
F: Fn(String) + 'static,
|
||||||
|
{
|
||||||
|
let (text, set_text) = create_signal("".to_string());
|
||||||
|
|
||||||
|
let on_send = {
|
||||||
|
move |_| {
|
||||||
|
send_stream.send_bytes(text().as_bytes());
|
||||||
|
on_send(text());
|
||||||
|
set_text("".to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<textarea on:change=move |e| set_text(event_target_value(&e)) prop:value=text />
|
||||||
|
<button on:click=on_send disabled=move || ready_state() != ConnectionReadyState::Open>"Send"</button>
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ use js_sys::Reflect;
|
||||||
use leptos::leptos_dom::helpers::TimeoutHandle;
|
use leptos::leptos_dom::helpers::TimeoutHandle;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::future::Future;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
@ -52,7 +51,7 @@ pub fn use_webtransport_with_options(
|
||||||
) -> UseWebTransportReturn {
|
) -> UseWebTransportReturn {
|
||||||
let UseWebTransportOptions {
|
let UseWebTransportOptions {
|
||||||
on_open,
|
on_open,
|
||||||
on_error,
|
// on_error,
|
||||||
on_close,
|
on_close,
|
||||||
on_receive_stream,
|
on_receive_stream,
|
||||||
on_bidir_stream,
|
on_bidir_stream,
|
||||||
|
@ -344,7 +343,7 @@ pub struct UseWebTransportOptions {
|
||||||
on_open: Rc<dyn Fn()>,
|
on_open: Rc<dyn Fn()>,
|
||||||
|
|
||||||
/// Error callback.
|
/// Error callback.
|
||||||
on_error: Rc<dyn Fn(WebTransportError)>,
|
// TODO : ? on_error: Rc<dyn Fn(WebTransportError)>,
|
||||||
|
|
||||||
/// Callback when `WebTransport` is closed.
|
/// Callback when `WebTransport` is closed.
|
||||||
on_close: Rc<dyn Fn()>,
|
on_close: Rc<dyn Fn()>,
|
||||||
|
@ -371,7 +370,7 @@ impl Default for UseWebTransportOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
on_open: Rc::new(|| {}),
|
on_open: Rc::new(|| {}),
|
||||||
on_error: Rc::new(|_| {}),
|
// on_error: Rc::new(|_| {}),
|
||||||
on_close: Rc::new(|| {}),
|
on_close: Rc::new(|| {}),
|
||||||
on_receive_stream: Rc::new(|_| {}),
|
on_receive_stream: Rc::new(|_| {}),
|
||||||
on_bidir_stream: Rc::new(|_| {}),
|
on_bidir_stream: Rc::new(|_| {}),
|
||||||
|
@ -392,6 +391,7 @@ pub trait CloseableStream {
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait SendableStream {
|
pub trait SendableStream {
|
||||||
fn writer(&self) -> &web_sys::WritableStreamDefaultWriter;
|
fn writer(&self) -> &web_sys::WritableStreamDefaultWriter;
|
||||||
|
|
||||||
fn send_bytes(&self, data: &[u8]) {
|
fn send_bytes(&self, data: &[u8]) {
|
||||||
let arr = js_sys::Uint8Array::from(data);
|
let arr = js_sys::Uint8Array::from(data);
|
||||||
let _ = self.writer().write_with_chunk(&arr);
|
let _ = self.writer().write_with_chunk(&arr);
|
||||||
|
@ -422,6 +422,7 @@ pub trait SendableStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct SendStream {
|
pub struct SendStream {
|
||||||
pub writer: web_sys::WritableStreamDefaultWriter,
|
pub writer: web_sys::WritableStreamDefaultWriter,
|
||||||
}
|
}
|
||||||
|
@ -449,12 +450,16 @@ impl CloseableStream for SendStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct ReceiveStream {
|
pub struct ReceiveStream {
|
||||||
pub reader: web_sys::ReadableStreamDefaultReader,
|
pub reader: web_sys::ReadableStreamDefaultReader,
|
||||||
pub message: Signal<Option<Vec<u8>>>,
|
pub message: Signal<Option<Vec<u8>>>,
|
||||||
pub close: Rc<dyn Fn()>,
|
// pub close: Rc<dyn Fn()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO : implement ReceiveStream
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct BidirStream {
|
pub struct BidirStream {
|
||||||
pub writer: web_sys::WritableStreamDefaultWriter,
|
pub writer: web_sys::WritableStreamDefaultWriter,
|
||||||
pub bytes: Signal<Option<Vec<u8>>>,
|
pub bytes: Signal<Option<Vec<u8>>>,
|
||||||
|
@ -495,6 +500,14 @@ impl CloseableStream for BidirStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl SendableStream for BidirStream {
|
||||||
|
#[inline(always)]
|
||||||
|
fn writer(&self) -> &web_sys::WritableStreamDefaultWriter {
|
||||||
|
&self.writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return type of [`use_webtransport`].
|
/// Return type of [`use_webtransport`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UseWebTransportReturn {
|
pub struct UseWebTransportReturn {
|
||||||
|
@ -595,6 +608,7 @@ pub enum WebTransportError {
|
||||||
OnCloseReader(JsValue),
|
OnCloseReader(JsValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error enum for [`SendStream::send`]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SendError {
|
pub enum SendError {
|
||||||
#[error("Failed to write to stream")]
|
#[error("Failed to write to stream")]
|
||||||
|
|
Loading…
Add table
Reference in a new issue