mod article_row; mod subscribe_form; use crate::api; use crate::components::scroll_area::ScrollArea; use crate::components::sidebar::{ Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, }; use article_row::ArticleRow; use dioxus::prelude::*; use subscribe_form::SubscribeForm; #[component] pub fn App() -> Element { let mut selected_feed = use_signal(|| Option::::None); let mut feeds = use_server_future(api::feeds::list_feeds)?; let mut articles = use_server_future(move || api::articles::list_ranked_articles(selected_feed()))?; let on_subscribed = move |_| { feeds.restart(); articles.restart(); }; rsx! { Stylesheet { href: asset!("/assets/app.css") } Stylesheet { href: asset!("/assets/dx-components-theme.css") } SidebarProvider { div { class: "app-shell", Sidebar { SidebarHeader { h1 { "feedsignal" } SubscribeForm { on_subscribed } } SidebarContent { SidebarGroup { SidebarGroupLabel { "Feeds" } SidebarGroupContent { SidebarMenu { SidebarMenuItem { SidebarMenuButton { is_active: selected_feed().is_none(), r#as: move |attrs: Vec| rsx! { button { onclick: move |_| selected_feed.set(None), ..attrs, "All" } }, } } match feeds.read().as_ref() { Some(Ok(feeds)) => rsx! { for feed in feeds.iter() { { let feed_id = feed.id.clone(); let feed_title = feed.title.clone(); let is_active = selected_feed.read().as_deref() == Some(feed.id.as_str()); rsx! { SidebarMenuItem { SidebarMenuButton { is_active, r#as: move |attrs: Vec| { let feed_id = feed_id.clone(); let feed_title = feed_title.clone(); rsx! { button { onclick: move |_| selected_feed.set(Some(feed_id.clone())), ..attrs, "{feed_title}" } } }, } } } } } }, Some(Err(err)) => rsx! { p { class: "error", "Failed to load feeds: {err}" } }, None => rsx! { p { "Loading feeds..." } }, } } } } } } SidebarInset { main { div { class: "content-header", SidebarTrigger {} h2 { { let heading = match selected_feed.read().as_ref() { None => "All articles".to_string(), Some(id) => feeds .read() .as_ref() .and_then(|r| r.as_ref().ok()) .and_then(|feeds| feeds.iter().find(|f| &f.id == id)) .map(|f| f.title.clone()) .unwrap_or_else(|| "Feed".to_string()), }; rsx! { "{heading}" } } } } ScrollArea { class: "article-scroll-area", match articles.read().as_ref() { Some(Ok(articles)) => rsx! { ul { class: "article-list", for article in articles.iter() { ArticleRow { article: article.clone() } } } }, Some(Err(err)) => rsx! { p { class: "error", "Failed to load articles: {err}" } }, None => rsx! { p { "Loading..." } }, } } } } } } } }