started working on use_webtransport (#9)

This commit is contained in:
Maccesch 2023-07-28 21:04:44 +01:00
parent 35528c434b
commit c0c03a94f0
17 changed files with 525 additions and 3 deletions

View file

@ -1,3 +1,3 @@
[unstable] [build]
rustflags = ["--cfg=web_sys_unstable_apis"] rustflags = ["--cfg=web_sys_unstable_apis"]
rustdocflags = ["--cfg=web_sys_unstable_apis"] rustdocflags = ["--cfg=web_sys_unstable_apis"]

View file

@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### New Functions 🚀 ### New Functions 🚀
- `use_webtransport`
- `signal_debounced` - `signal_debounced`
- `signal_throttled` - `signal_throttled`
@ -15,8 +16,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Leptos version is now 0.5 - Leptos version is now 0.5
- No `cx: Scope` params are supported/needed anymore because of the changes in Leptos. - No `cx: Scope` params are supported/needed anymore because of the changes in Leptos.
Please check the release notes of Leptos 0.5 for how to upgrade. Please check the release notes of Leptos 0.5 for how to upgrade.
- `watch` is now deprecated in favor of `leptos::watch`. `watch_with_options`
will continue to exist.
- `watch` is now removed in favor of `leptos::watch` and will be removed in a future release. - `watch` is now removed in favor of `leptos::watch` and will be removed in a future release.
`watch_with_options` will continue to exist. `watch_with_options` will continue to exist.
- `use_websocket`: - `use_websocket`:

View file

@ -23,6 +23,7 @@ serde_json = { version = "1", optional = true }
paste = "1" paste = "1"
lazy_static = "1" lazy_static = "1"
cfg-if = "1" cfg-if = "1"
wasm-bindgen-futures = "0.4"
[dependencies.web-sys] [dependencies.web-sys]
version = "0.3" version = "0.3"
@ -66,6 +67,8 @@ features = [
"TouchList", "TouchList",
"VisibilityState", "VisibilityState",
"WebSocket", "WebSocket",
"WebTransport",
"WebTransportOptions",
"Window", "Window",
] ]

View file

@ -47,6 +47,7 @@
# Network # Network
- [use_websocket](network/use_websocket.md) - [use_websocket](network/use_websocket.md)
- [use_webtransport](network/use_webtransport.md)
# Animation # Animation

View file

@ -0,0 +1,3 @@
# use_webtransport
<!-- cmdrun python3 ../extract_doc_comment.py use_webtransport -->

View file

@ -36,6 +36,7 @@ members = [
"use_storage", "use_storage",
"use_throttle_fn", "use_throttle_fn",
"use_websocket", "use_websocket",
"use_webtransport",
"use_window_focus", "use_window_focus",
"use_window_scroll", "use_window_scroll",
"watch_debounced", "watch_debounced",

View file

@ -0,0 +1,16 @@
[package]
name = "use_webtransport"
version = "0.1.0"
edition = "2021"
[dependencies]
leptos = { version = "0.4", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
leptos-use = { path = "../..", features = ["docs"] }
web-sys = "0.3"
[dev-dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.0"

View file

@ -0,0 +1,23 @@
A simple example for `use_webtransport`.
If you don't have it installed already, install [Trunk](https://trunkrs.dev/) and [Tailwind](https://tailwindcss.com/docs/installation)
as well as the nightly toolchain for Rust and the wasm32-unknown-unknown target:
```bash
cargo install trunk
npm install -D tailwindcss @tailwindcss/forms
rustup toolchain install nightly
rustup target add wasm32-unknown-unknown
```
Then, open two terminals. In the first one, run:
```
npx tailwindcss -i ./input.css -o ./style/output.css --watch
```
In the second one, run:
```bash
trunk serve --open
```

View file

@ -0,0 +1,2 @@
[build]
public_url = "/demo/"

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link data-trunk rel="css" href="style/output.css">
</head>
<body></body>
</html>

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View file

@ -0,0 +1,20 @@
use leptos::*;
use leptos_use::docs::demo_or_body;
use leptos_use::use_webtransport;
#[component]
fn Demo() -> impl IntoView {
use_webtransport();
view! { }
}
fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to(demo_or_body(), || {
view! { <Demo/> }
})
}

View file

@ -0,0 +1,289 @@
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0px;
padding-top: 0.5rem;
padding-right: 0.75rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
font-size: 1rem;
line-height: 1.5rem;
--tw-shadow: 0 0 #0000;
}
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
border-color: #2563eb;
}
input::-moz-placeholder, textarea::-moz-placeholder {
color: #6b7280;
opacity: 1;
}
input::placeholder,textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
padding-top: 0;
padding-bottom: 0;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: 0.75rem;
-webkit-print-color-adjust: unset;
print-color-adjust: unset;
}
[type='checkbox'],[type='radio'] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
--tw-shadow: 0 0 #0000;
}
[type='checkbox'] {
border-radius: 0px;
}
[type='radio'] {
border-radius: 100%;
}
[type='checkbox']:focus,[type='radio']:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
[type='checkbox']:checked,[type='radio']:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type='radio']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
border-color: transparent;
background-color: currentColor;
}
[type='checkbox']:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
border-color: transparent;
background-color: currentColor;
}
[type='file'] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type='file']:focus {
outline: 1px solid ButtonText;
outline: 1px auto -webkit-focus-ring-color;
}
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
.block {
display: block;
}
.text-\[--brand-color\] {
color: var(--brand-color);
}
.text-green-600 {
--tw-text-opacity: 1;
color: rgb(22 163 74 / var(--tw-text-opacity));
}
.opacity-75 {
opacity: 0.75;
}
@media (prefers-color-scheme: dark) {
.dark\:text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity));
}
}

View file

@ -0,0 +1,15 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: {
files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"],
},
theme: {
extend: {},
},
corePlugins: {
preflight: false,
},
plugins: [
require('@tailwindcss/forms'),
],
}

View file

@ -55,6 +55,7 @@ mod use_supported;
mod use_throttle_fn; mod use_throttle_fn;
mod use_to_string; mod use_to_string;
mod use_websocket; mod use_websocket;
mod use_webtransport;
mod use_window_focus; mod use_window_focus;
mod use_window_scroll; mod use_window_scroll;
mod watch_debounced; mod watch_debounced;
@ -98,6 +99,7 @@ pub use use_supported::*;
pub use use_throttle_fn::*; pub use use_throttle_fn::*;
pub use use_to_string::*; pub use use_to_string::*;
pub use use_websocket::*; pub use use_websocket::*;
pub use use_webtransport::*;
pub use use_window_focus::*; pub use use_window_focus::*;
pub use use_window_scroll::*; pub use use_window_scroll::*;
pub use watch_debounced::*; pub use watch_debounced::*;

136
src/use_webtransport.rs Normal file
View file

@ -0,0 +1,136 @@
use crate::core::ConnectionReadyState;
use crate::utils::{CloneableFn, CloneableFnWithArg};
use default_struct_builder::DefaultBuilder;
use leptos::leptos_dom::helpers::TimeoutHandle;
use leptos::*;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
///
///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_webtransport)
///
/// ## Usage
///
/// ```
/// # use leptos::*;
/// # use leptos_use::use_webtransport;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// use_webtransport();
/// #
/// # view! { }
/// # }
/// ```
pub fn use_webtransport(url: &str) -> UseWebtransportReturn {
use_webtransport_with_options(url, UseWebTransportOptions::default())
}
/// Version of [`use_webtransport`] that takes a `UseWebtransportOptions`. See [`use_webtransport`] for how to use.
pub fn use_webtransport_with_options(
url: &str,
options: UseWebTransportOptions,
) -> UseWebtransportReturn {
let UseWebTransportOptions {
on_open,
on_error,
on_close,
reconnect_limit,
reconnect_interval,
immediate,
} = options;
let (ready_state, set_ready_state) = create_signal(ConnectionReadyState::Closed);
let transport_ref = store_value(None::<web_sys::WebTransport>);
let reconnect_timer_ref = store_value(None::<TimeoutHandle>);
let reconnect_count_ref = store_value(0_u64);
let connect = move || {
let transport = transport_ref.get_value();
reconnect_timer_ref.set_value(None);
if let Some(transport) = transport.as_ref() {
transport.close();
}
let transport =
web_sys::WebTransport::new_with_options(url, &options.into()).unwrap_throw();
set_ready_state.set(ConnectionReadyState::Connecting);
spawn_local(async move {
match JsFuture::from(transport.ready()).await {
Ok(_) => {
set_ready_state.set(ConnectionReadyState::Open);
on_open();
}
Err(e) => {
// TODO : handle error?
set_ready_state.set(ConnectionReadyState::Closed);
}
}
});
};
let open = {
move || {
reconnect_count_ref.set_value(0);
}
};
UseWebtransportReturn {}
}
/// Options for [`use_webtransport_with_options`].
#[derive(DefaultBuilder)]
pub struct UseWebTransportOptions {
/// Callback when `WebTransport` is ready.
on_open: Box<dyn CloneableFn>,
/// Error callback.
on_error: Box<dyn CloneableFnWithArg<WebTransportError>>,
/// Callback when `WebTransport` is closed.
on_close: Box<dyn CloneableFn>,
/// Retry times. Defaults to 3.
reconnect_limit: u64,
/// Retry interval in ms. Defaults to 3000.
reconnect_interval: u64,
/// If `true` the `WebSocket` connection will immediately be opened when calling this function.
/// If `false` you have to manually call the `open` function.
/// Defaults to `true`.
immediate: bool,
}
impl Default for UseWebTransportOptions {
fn default() -> Self {
Self {
on_open: Box::new(|| {}),
on_error: Box::new(|_| {}),
on_close: Box::new(|| {}),
reconnect_limit: 3,
reconnect_interval: 3000,
immediate: true,
}
}
}
impl From<UseWebTransportOptions> for web_sys::WebTransportOptions {
fn from(options: UseWebTransportOptions) -> Self {
web_sys::WebTransportOptions::new()
}
}
/// Return type of [`use_webtransport`].
pub struct UseWebtransportReturn {
/// The current state of the `WebTransport` connection.
pub ready_state: Signal<ConnectionReadyState>,
}
/// Error enum for [`UseWebTransportOptions::on_error`]
pub enum WebTransportError {}