Update more examples, add css for more examples
This commit is contained in:
19 files changed
+228
-101
No files matched your search
@@ -0,0 +1,43 @@
|
|||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pause {
|
||||||
|
background-color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#play {
|
||||||
|
background-color: #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.bounce {
|
||||||
|
animation: boomBox 0.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes boomBox {
|
||||||
|
0% {
|
||||||
|
transform: scale(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#navbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blog-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
#![allow(clippy::await_holding_refcell_ref)]
|
//! Read the size of elements using the MountedData struct.
|
||||||
|
//!
|
||||||
|
//! Whenever an Element is finally mounted to the Dom, its data is avaiable to be read.
|
||||||
|
//! These fields can typically only be read asynchronously, since various renderers need to release the main thread to
|
||||||
|
//! perform layout and painting.
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dioxus::{html::geometry::euclid::Rect, prelude::*};
|
use dioxus::{html::geometry::euclid::Rect, prelude::*};
|
||||||
@@ -33,6 +38,7 @@ fn app() -> Element {
|
|||||||
let read_dims = move |_| async move {
|
let read_dims = move |_| async move {
|
||||||
let read = div_element.read();
|
let read = div_element.read();
|
||||||
let client_rect = read.as_ref().map(|el| el.get_client_rect());
|
let client_rect = read.as_ref().map(|el| el.get_client_rect());
|
||||||
|
|
||||||
if let Some(client_rect) = client_rect {
|
if let Some(client_rect) = client_rect {
|
||||||
if let Ok(rect) = client_rect.await {
|
if let Ok(rect) = client_rect.await {
|
||||||
dimensions.set(rect);
|
dimensions.set(rect);
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
//! The example from the readme!
|
||||||
|
//!
|
||||||
|
//! This example demonstrates how to create a simple counter app with dioxus. The `Signal` type wraps inner values,
|
||||||
|
//! making them `Copy`, allowing them to be freely used in closures and and async functions. `Signal` also provides
|
||||||
|
//! helper methods like AddAssign, SubAssign, toggle, etc, to make it easy to update the value without running
|
||||||
|
//! into lock issues.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
+9
-5
@@ -15,12 +15,16 @@ fn app() -> Element {
|
|||||||
let mut state = use_signal(|| PlayerState { is_playing: false });
|
let mut state = use_signal(|| PlayerState { is_playing: false });
|
||||||
|
|
||||||
rsx!(
|
rsx!(
|
||||||
div {
|
style { {include_str!("./assets/radio.css")} }
|
||||||
h1 {"Select an option"}
|
h1 {"Select an option"}
|
||||||
h3 { "The radio is... ", {state.read().is_playing()}, "!" }
|
|
||||||
button { onclick: move |_| state.write().reduce(PlayerAction::Pause), "Pause" }
|
// Add some cute animations if the radio is playing!
|
||||||
button { onclick: move |_| state.write().reduce(PlayerAction::Play), "Play" }
|
div { class: if state.read().is_playing { "bounce" },
|
||||||
|
"The radio is... ", {state.read().is_playing()}, "!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button { id: "play", onclick: move |_| state.write().reduce(PlayerAction::Pause), "Pause" }
|
||||||
|
button { id: "pause", onclick: move |_| state.write().reduce(PlayerAction::Play), "Play" }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+53
-31
@@ -1,32 +1,59 @@
|
|||||||
|
//! An advanced usage of the router with nested routes and redirects.
|
||||||
|
//!
|
||||||
|
//! Dioxus implements an enum-based router, which allows you to define your routes in a type-safe way.
|
||||||
|
//! However, since we need to bake quite a bit of logic into the enum, we have to add some extra syntax.
|
||||||
|
//!
|
||||||
|
//! Note that you don't need to use advanced features like nest, redirect, etc, since these can all be implemented
|
||||||
|
//! manually, but they are provided as a convenience.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
launch_desktop(|| {
|
launch_desktop(|| {
|
||||||
rsx! {
|
rsx! {
|
||||||
|
style { {include_str!("./assets/router.css")} }
|
||||||
Router::<Route> {}
|
Router::<Route> {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Turn off rustfmt since we're doing layouts and routes in the same enum
|
||||||
#[derive(Routable, Clone, Debug, PartialEq)]
|
#[derive(Routable, Clone, Debug, PartialEq)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
enum Route {
|
enum Route {
|
||||||
|
// Wrap Home in a Navbar Layout
|
||||||
#[layout(NavBar)]
|
#[layout(NavBar)]
|
||||||
|
// The default route is always "/" unless otherwise specified
|
||||||
#[route("/")]
|
#[route("/")]
|
||||||
Home {},
|
Home {},
|
||||||
|
|
||||||
|
// Wrap the next routes in a layout and a nest
|
||||||
#[nest("/blog")]
|
#[nest("/blog")]
|
||||||
#[layout(Blog)]
|
#[layout(Blog)]
|
||||||
#[route("/")]
|
// At "/blog", we want to show a list of blog posts
|
||||||
BlogList {},
|
#[route("/")]
|
||||||
#[route("/:name")]
|
BlogList {},
|
||||||
BlogPost { name: String },
|
|
||||||
#[end_layout]
|
// At "/blog/:name", we want to show a specific blog post, using the name slug
|
||||||
|
#[route("/:name")]
|
||||||
|
BlogPost { name: String },
|
||||||
|
|
||||||
|
// We need to end the blog layout and nest
|
||||||
|
// Note we don't need either - we could've just done `/blog/` and `/blog/:name` without nesting,
|
||||||
|
// but it's a bit cleaner this way
|
||||||
|
#[end_layout]
|
||||||
#[end_nest]
|
#[end_nest]
|
||||||
|
|
||||||
|
// And the regular page layout
|
||||||
#[end_layout]
|
#[end_layout]
|
||||||
|
|
||||||
|
// Add some redirects for the `/myblog` route
|
||||||
#[nest("/myblog")]
|
#[nest("/myblog")]
|
||||||
#[redirect("/", || Route::BlogList {})]
|
#[redirect("/", || Route::BlogList {})]
|
||||||
#[redirect("/:name", |name: String| Route::BlogPost { name })]
|
#[redirect("/:name", |name: String| Route::BlogPost { name })]
|
||||||
#[end_nest]
|
#[end_nest]
|
||||||
|
|
||||||
|
// Finally, we need to handle the 404 page
|
||||||
#[route("/:..route")]
|
#[route("/:..route")]
|
||||||
PageNotFound {
|
PageNotFound {
|
||||||
route: Vec<String>,
|
route: Vec<String>,
|
||||||
@@ -36,15 +63,9 @@ enum Route {
|
|||||||
#[component]
|
#[component]
|
||||||
fn NavBar() -> Element {
|
fn NavBar() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
nav {
|
nav { id: "navbar",
|
||||||
ul {
|
Link { to: Route::Home {}, "Home" }
|
||||||
li {
|
Link { to: Route::BlogList {}, "Blog" }
|
||||||
Link { to: Route::Home {}, "Home" }
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
Link { to: Route::BlogList {}, "Blog" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Outlet::<Route> {}
|
Outlet::<Route> {}
|
||||||
}
|
}
|
||||||
@@ -67,30 +88,31 @@ fn Blog() -> Element {
|
|||||||
fn BlogList() -> Element {
|
fn BlogList() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
h2 { "Choose a post" }
|
h2 { "Choose a post" }
|
||||||
ul {
|
div { id: "blog-list",
|
||||||
li {
|
Link { to: Route::BlogPost { name: "Blog post 1".into() },
|
||||||
Link {
|
"Read the first blog post"
|
||||||
to: Route::BlogPost {
|
|
||||||
name: "Blog post 1".into(),
|
|
||||||
},
|
|
||||||
"Read the first blog post"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
li {
|
Link { to: Route::BlogPost { name: "Blog post 2".into() },
|
||||||
Link {
|
"Read the second blog post"
|
||||||
to: Route::BlogPost {
|
|
||||||
name: "Blog post 2".into(),
|
|
||||||
},
|
|
||||||
"Read the second blog post"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can use the `name` slug to show a specific blog post
|
||||||
|
// In theory we could read from the filesystem or a database here
|
||||||
#[component]
|
#[component]
|
||||||
fn BlogPost(name: String) -> Element {
|
fn BlogPost(name: String) -> Element {
|
||||||
rsx! { h2 { "Blog Post: {name}" } }
|
let contents = match name.as_str() {
|
||||||
|
"Blog post 1" => "This is the first blog post. It's not very interesting.",
|
||||||
|
"Blog post 2" => "This is the second blog post. It's not very interesting either.",
|
||||||
|
_ => "This blog post doesn't exist.",
|
||||||
|
};
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
h2 { "{name}" }
|
||||||
|
p { "{contents}" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
//! Scroll elements using their MountedData
|
||||||
|
//!
|
||||||
|
//! Dioxus exposes a few helpful APIs around elements (mimicking the DOM APIs) to allow you to interact with elements
|
||||||
|
//! across the renderers. This includes scrolling, reading dimensions, and more.
|
||||||
|
//!
|
||||||
|
//! In this example we demonstrate how to scroll to the top of the page using the `scroll_to` method on the `MountedData`
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
//! Add global shortcuts to your app while a component is active
|
||||||
|
//!
|
||||||
|
//! This demo shows how to add a global shortcut to your app that toggles a signal. You could use this to implement
|
||||||
|
//! a raycast-type app, or to add a global shortcut to your app that toggles a component on and off.
|
||||||
|
//!
|
||||||
|
//! These are *global* shortcuts, so they will work even if your app is not in focus.
|
||||||
|
|
||||||
use dioxus::desktop::use_global_shortcut;
|
use dioxus::desktop::use_global_shortcut;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Dioxus supports shorthand syntax for creating elements and components.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
//! A simple example demonstrating how to use signals to modify state from several different places.
|
||||||
|
//!
|
||||||
|
//! This simlpe example implements a counter that can be incremented, decremented, and paused. It also demonstrates
|
||||||
|
//! that background tasks in use_futures can modify the value as well.
|
||||||
|
//!
|
||||||
|
//! Most signals implement Into<ReadOnlySignal<T>>, making ReadOnlySignal a good default type when building new
|
||||||
|
//! library components that don't need to modify their values.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
//! A few ways of mapping elements into rsx! syntax
|
||||||
|
//!
|
||||||
|
//! Rsx allows anything that's an iterator where the output type implements Into<Element>, so you can use any of the following:
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
+21
-10
@@ -1,20 +1,32 @@
|
|||||||
#![allow(non_snake_case)]
|
//! A simple example of a router with a few routes and a nav bar.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Launch the router, using our `Route` component as the generic type
|
||||||
|
// This will automatically boot the app to "/" unless otherwise specified
|
||||||
|
launch(|| rsx! { Router::<Route> {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// By default, the Routable derive will use the name of the variant as the route
|
||||||
|
/// You can also specify a specific component by adding the Component name to the `#[route]` attribute
|
||||||
|
#[rustfmt::skip]
|
||||||
#[derive(Routable, Clone, PartialEq)]
|
#[derive(Routable, Clone, PartialEq)]
|
||||||
enum Route {
|
enum Route {
|
||||||
|
// Wrap the app in a Nav layout
|
||||||
#[layout(Nav)]
|
#[layout(Nav)]
|
||||||
#[route("/")]
|
#[route("/")]
|
||||||
Homepage {},
|
Homepage {},
|
||||||
|
|
||||||
#[route("/blog/:id")]
|
#[route("/blog/:id")]
|
||||||
Blog { id: String },
|
Blog { id: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Homepage() -> Element {
|
fn Homepage() -> Element {
|
||||||
rsx! { h1 { "Welcome home" } }
|
rsx! {
|
||||||
|
h1 { "Welcome home" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@@ -25,6 +37,9 @@ fn Blog(id: String) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A simple nav bar that links to the homepage and blog pages
|
||||||
|
///
|
||||||
|
/// The `Route` enum gives up typesafe routes, allowing us to rename routes and serialize them automatically
|
||||||
#[component]
|
#[component]
|
||||||
fn Nav() -> Element {
|
fn Nav() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
@@ -52,7 +67,3 @@ fn Nav() -> Element {
|
|||||||
div { Outlet::<Route> {} }
|
div { Outlet::<Route> {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
launch_desktop(|| rsx! { Router::<Route> {} });
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
//! This example demonstrates how to use the spread operator to pass attributes to child components.
|
||||||
|
//!
|
||||||
|
//! This lets components like the `Link` allow the user to extend the attributes of the underlying `a` tag.
|
||||||
|
//! These attributes are bundled into a `Vec<Attribute>` which can be spread into the child component using the `..` operator.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
//! Example: SSR
|
//! Example: SSR
|
||||||
//!
|
//!
|
||||||
//! This example shows how we can render the Dioxus Virtualdom using SSR.
|
//! This example shows how we can render the Dioxus Virtualdom using SSR.
|
||||||
|
//! Dioxus' SSR is quite comprehensive and can generate a number of utility markers for things like hydration.
|
||||||
|
//!
|
||||||
|
//! You can also render without any markers to get a clean HTML output.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
launch_desktop(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn app() -> Element {
|
|
||||||
let mut state = use_signal(|| 0);
|
|
||||||
let mut depth = use_signal(|| 1_usize);
|
|
||||||
|
|
||||||
if depth() == 5 {
|
|
||||||
return rsx! {
|
|
||||||
div { "Max depth reached" }
|
|
||||||
button { onclick: move |_| depth -= 1, "Remove depth" }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let items = use_memo(move || (0..depth()).map(|f| f as _).collect::<Vec<isize>>());
|
|
||||||
|
|
||||||
rsx! {
|
|
||||||
button { onclick: move |_| state += 1, "Increment" }
|
|
||||||
button { onclick: move |_| depth += 1, "Add depth" }
|
|
||||||
button {
|
|
||||||
onclick: move |_| async move {
|
|
||||||
depth += 1;
|
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
|
||||||
dbg!(items.read());
|
|
||||||
// if depth() is 5, this will be the old since the memo hasn't been re-computed
|
|
||||||
// use_memos are only re-computed when the signals they capture change
|
|
||||||
// *and* they are used in the current render
|
|
||||||
// If the use_memo isn't used, it can't be re-computed!
|
|
||||||
},
|
|
||||||
"Add depth with sleep"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Handle async streams using use_future and awaiting the next value.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use futures_util::{future, stream, Stream, StreamExt};
|
use futures_util::{future, stream, Stream, StreamExt};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -10,8 +12,11 @@ fn app() -> Element {
|
|||||||
let mut count = use_signal(|| 10);
|
let mut count = use_signal(|| 10);
|
||||||
|
|
||||||
use_future(move || async move {
|
use_future(move || async move {
|
||||||
|
// Create the stream.
|
||||||
|
// This could be a network request, a file read, or any other async operation.
|
||||||
let mut stream = some_stream();
|
let mut stream = some_stream();
|
||||||
|
|
||||||
|
// Await the next value from the stream.
|
||||||
while let Some(second) = stream.next().await {
|
while let Some(second) = stream.next().await {
|
||||||
count.set(second);
|
count.set(second);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
//! Suspense in Dioxus
|
//! Suspense in Dioxus
|
||||||
//!
|
//!
|
||||||
//! Currently, `rsx!` does not accept futures as values. To achieve the functionality
|
//! Currently, `rsx!` does not accept futures as values. To achieve the functionality
|
||||||
@@ -49,6 +47,7 @@ fn app() -> Element {
|
|||||||
/// This component will re-render when the future has finished
|
/// This component will re-render when the future has finished
|
||||||
/// Suspense is achieved my moving the future into only the component that
|
/// Suspense is achieved my moving the future into only the component that
|
||||||
/// actually renders the data.
|
/// actually renders the data.
|
||||||
|
#[component]
|
||||||
fn Doggo() -> Element {
|
fn Doggo() -> Element {
|
||||||
let mut fut = use_resource(move || async move {
|
let mut fut = use_resource(move || async move {
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
|||||||
+7
-1
@@ -1,4 +1,10 @@
|
|||||||
// Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
|
//! Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
|
||||||
|
//!
|
||||||
|
//! This example shows how to create a simple dice rolling app using SVG and Dioxus.
|
||||||
|
//! The `svg` element and its children have a custom namespace, and are attached using different methods than regular
|
||||||
|
//! HTML elements. Any element can specify a custom namespace by using the `namespace` meta attribute.
|
||||||
|
//!
|
||||||
|
//! If you `go-to-definition` on the `svg` element, you'll see its custom namespace.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
|||||||
+26
-15
@@ -1,3 +1,7 @@
|
|||||||
|
//! Using `wry`'s http module, we can stream a video file from the local file system.
|
||||||
|
//!
|
||||||
|
//! You could load in any file type, but this example uses a video file.
|
||||||
|
|
||||||
use dioxus::desktop::wry::http;
|
use dioxus::desktop::wry::http;
|
||||||
use dioxus::desktop::wry::http::Response;
|
use dioxus::desktop::wry::http::Response;
|
||||||
use dioxus::desktop::{use_asset_handler, AssetRequest};
|
use dioxus::desktop::{use_asset_handler, AssetRequest};
|
||||||
@@ -9,25 +13,14 @@ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
|||||||
const VIDEO_PATH: &str = "./examples/assets/test_video.mp4";
|
const VIDEO_PATH: &str = "./examples/assets/test_video.mp4";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let video_file = PathBuf::from(VIDEO_PATH);
|
// For the sake of this example, we will download the video file if it doesn't exist
|
||||||
if !video_file.exists() {
|
ensure_video_is_loaded();
|
||||||
tokio::runtime::Runtime::new()
|
|
||||||
.unwrap()
|
|
||||||
.block_on(async move {
|
|
||||||
println!("Downloading video file...");
|
|
||||||
let video_url =
|
|
||||||
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
|
|
||||||
let mut response = reqwest::get(video_url).await.unwrap();
|
|
||||||
let mut file = tokio::fs::File::create(&video_file).await.unwrap();
|
|
||||||
while let Some(chunk) = response.chunk().await.unwrap() {
|
|
||||||
file.write_all(&chunk).await.unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
launch_desktop(app);
|
launch_desktop(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
|
// Any request to /videos will be handled by this handler
|
||||||
use_asset_handler("videos", move |request, responder| {
|
use_asset_handler("videos", move |request, responder| {
|
||||||
// Using dioxus::spawn works, but is slower than a dedicated thread
|
// Using dioxus::spawn works, but is slower than a dedicated thread
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
@@ -184,3 +177,21 @@ async fn get_stream_response(
|
|||||||
|
|
||||||
http_response.map_err(Into::into)
|
http_response.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_video_is_loaded() {
|
||||||
|
let video_file = PathBuf::from(VIDEO_PATH);
|
||||||
|
if !video_file.exists() {
|
||||||
|
tokio::runtime::Runtime::new()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(async move {
|
||||||
|
println!("Downloading video file...");
|
||||||
|
let video_url =
|
||||||
|
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
|
||||||
|
let mut response = reqwest::get(video_url).await.unwrap();
|
||||||
|
let mut file = tokio::fs::File::create(&video_file).await.unwrap();
|
||||||
|
while let Some(chunk) = response.chunk().await.unwrap() {
|
||||||
|
file.write_all(&chunk).await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user