2026-09-03 10:03:15 +00:00
|
|
|
mod article_row;
|
|
|
|
|
mod subscribe_form;
|
|
|
|
|
|
|
|
|
|
use crate::api;
|
2026-09-03 14:28:22 +00:00
|
|
|
use crate::components::scroll_area::ScrollArea;
|
|
|
|
|
use crate::components::sidebar::{
|
|
|
|
|
Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader,
|
|
|
|
|
SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger,
|
|
|
|
|
};
|
2026-09-03 10:03:15 +00:00
|
|
|
use article_row::ArticleRow;
|
|
|
|
|
use dioxus::prelude::*;
|
|
|
|
|
use subscribe_form::SubscribeForm;
|
|
|
|
|
|
|
|
|
|
#[component]
|
|
|
|
|
pub fn App() -> Element {
|
2026-09-03 14:28:22 +00:00
|
|
|
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();
|
|
|
|
|
};
|
2026-09-03 10:03:15 +00:00
|
|
|
|
|
|
|
|
rsx! {
|
|
|
|
|
Stylesheet { href: asset!("/assets/app.css") }
|
|
|
|
|
Stylesheet { href: asset!("/assets/dx-components-theme.css") }
|
2026-09-03 14:28:22 +00:00
|
|
|
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..." } },
|
|
|
|
|
}
|
2026-09-03 10:03:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-09-03 14:28:22 +00:00
|
|
|
}
|
2026-09-03 10:03:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|