feedsignal/crates/web/src/app/mod.rs

126 lines
6.3 KiB
Rust
Raw Normal View History

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::<String>::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<Attribute>| 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<Attribute>| {
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..." } },
}
}
}
}
}
}
}
}