use ruin_app::prelude::*; #[ruin_runtime::async_main] async fn main() -> ruin_app::Result<()> { App::new() .window( Window::new() .title("RUIN Async Data and Effects") .app_id("dev.ruin.async-data") .size(1080.0, 760.0), ) .mount(view! { AsyncDataAndEffectsDemo() {} }) .run() .await } #[component] fn AsyncDataAndEffectsDemo() -> impl IntoView { let selected_path = use_signal(|| "Cargo.toml".to_string()); let reload_nonce = use_signal(|| 0_u64); let preview_scroll = use_signal(|| 0.0_f32); let file_contents = use_resource({ let selected_path = selected_path.clone(); let reload_nonce = reload_nonce.clone(); move || { let path = selected_path.get(); let _ = reload_nonce.get(); async move { ruin_runtime::fs::read_to_string(&path) .await .map_err(|error| error.to_string()) } } }); use_effect({ let selected_path = selected_path.clone(); let reload_nonce = reload_nonce.clone(); move || { eprintln!( "async resource dependencies changed: path={}, reload={}", selected_path.get(), reload_nonce.get() ); } }); use_effect({ let selected_path = selected_path.clone(); let reload_nonce = reload_nonce.clone(); let preview_scroll = preview_scroll.clone(); move || { let _ = selected_path.get(); let _ = reload_nonce.get(); let _ = preview_scroll.set(0.0); } }); use_window_title({ let selected_path = selected_path.clone(); move || format!("RUIN Async Demo ({})", selected_path.get()) }); view! { column(gap = 16.0, padding = 24.0) { text(role = TextRole::Heading(1), size = 32.0, weight = FontWeight::Semibold) { "Async data and effects" } text(color = colors::muted(), wrap = TextWrap::Word) { "This is a smaller truthful slice of example 01: local signals drive a real async \ resource, and an effect logs the dependency changes." } FileControls(selected_path = selected_path.clone(), reload_nonce = reload_nonce.clone()) {} block( padding = 16.0, gap = 10.0, background = surfaces::raised(), border_radius = 12.0, ) { text(size = 18.0) { "selected = "; selected_path.clone() } match file_contents.read() { Pending => view! { text(color = colors::muted()) { "Loading file contents..." } }, Ready(Ok(contents)) => view! { column(background = surfaces::raised(), gap = 10.0) { text() { "bytes = "; contents.len() } scroll_box( height = 420.0, offset_y = preview_scroll.clone(), padding = 12.0, background = surfaces::canvas(), border_radius = 10.0, border = (2.0, colors::muted()), ) { text( color = colors::muted(), font_family = TextFontFamily::Monospace, ) { contents } } } }, Ready(Err(error)) => view! { text(color = colors::danger()) { error.to_string() } }, } } } } } #[component] fn FileControls(selected_path: Signal, reload_nonce: Signal) -> impl IntoView { view! { row(gap = 8.0) { button( on_press = { let selected_path = selected_path.clone(); move |_| { let _ = selected_path.set("Cargo.toml".to_string()); } }, ) { "Cargo.toml" } button( on_press = { let selected_path = selected_path.clone(); move |_| { let _ = selected_path.set("Cargo.lock".to_string()); } }, ) { "Cargo.lock" } button( on_press = { let reload_nonce = reload_nonce.clone(); move |_| { reload_nonce.update(|value| *value += 1); } }, ) { "Reload" } } } }