rss-tool/src/ui.rs

288 lines
7.6 KiB
Rust
Raw Normal View History

use crate::db::FeedItem;
use crate::net;
use crate::widgets::content_view;
2025-07-23 12:38:05 -04:00
use crate::widgets::media_view;
use crate::widgets::navbar;
use super::db;
use super::widgets;
use iced::widget::row;
use iced::widget::scrollable;
use iced::widget::text_input;
use iced::Task;
use iced::{
widget::{button, column, container, text},
Element,
Length::Fill,
};
use rss_content::parse_content;
use rss_content::Content;
use url::Url;
const ICON: &[u8] = include_bytes!("../assets/icon_placeholder.png");
pub fn user_interface() -> iced::Result {
//iced::run(update, view)
let icon = iced::window::icon::from_file_data(ICON, None).ok();
let app = iced::application(State::default,update,view)
.title("RSSCar")
.theme(iced::Theme::Dark)
.window(iced::window::Settings{
icon, ..Default::default()
});
app.run()
}
2025-12-04 22:36:36 -05:00
#[derive(Clone, Debug,PartialEq)]
pub enum Page {
Home,
2025-12-05 12:45:45 -05:00
Feeds,
FeedView,
AllItems,
ItemView,
CategoryView,
Testing,
}
pub struct State {
2025-12-04 22:36:36 -05:00
pub page: Page,
pub current_feed: usize,
pub current_item: Option<FeedItem>,
pub item_description: Vec<Content>,
pub item_content: Vec<Content>,
2025-12-04 22:36:36 -05:00
pub feed_input: String,
}
impl Default for State {
fn default() -> Self {
State {
page: Page::Home,
current_feed: 0,
current_item: None,
item_description: Vec::new(),
item_content: Vec::new(),
feed_input: String::from(""),
}
}
}
#[derive(Debug, Clone)]
pub enum Message {
ChangePage(Page),
LoadFeed(usize),
AddFeed(String),
RemoveFeed(usize),
LoadItem(usize),
ProcessOPML(String),
FieldUpdated(AppField, String),
LinkClicked(String),
Done(String),
ResetDB,
}
#[derive(Debug, Clone)]
pub enum AppField {
FeedInput,
OPMLInput,
}
async fn add_multiple_feeds_background(url:String) -> String {
println!("Adding feeds.");
let links = net::retrieve_opml(&url);
for link in links {
add_feed_background(link.to_string()).await;
}
"Done adding feeds.".to_string()
}
async fn add_feed_background(url: String) -> String {
println!("Adding feed!");
db::add_feed(&url);
"Done adding feed".to_string()
}
async fn remove_feed_background(id:usize) -> String {
println!("Removing feed");
db::remove_feed(id);
"Done removing feed".to_owned()
}
fn update(state: &mut State, mes: Message) -> Task<Message> {
match mes {
Message::ChangePage(p) => {
state.page = p;
Task::none()
}
Message::LoadFeed(feed_id) => {
state.current_feed = feed_id;
state.page = Page::FeedView;
Task::none()
}
Message::ProcessOPML(url) => {
state.feed_input = "".to_string();
Task::perform(add_multiple_feeds_background(url), Message::Done)
}
Message::AddFeed(f) => {
state.feed_input = "".to_string();
Task::perform(add_feed_background(f.to_string()), Message::Done)
}
Message::RemoveFeed(id) => {
Task::perform(remove_feed_background(id), Message::Done)
}
Message::LinkClicked(l) => {
println!("Link clicked: {}", l);
Task::none()
}
Message::LoadItem(id) => {
let item = db::get_item(id);
state.item_description = match &item.description {
Some(d) => {
parse_content(&d)
}
None => Vec::new()
};
state.item_content = match &item.content{
Some(c) => {
parse_content(&c)
}
None => Vec::new()
};
state.current_item = Some(item);
state.page = Page::ItemView;
Task::none()
}
Message::Done(_) => Task::none(),
Message::FieldUpdated(field, value) => {
match field {
AppField::FeedInput => {
state.feed_input = value;
},
AppField::OPMLInput => {
state.feed_input = value;
}
}
Task::none()
}
Message::ResetDB => {
db::reset();
Task::none()
}
}
}
fn view(state: &State) -> Element<'_, Message> {
match state.page {
Page::Home => home(&state),
2025-12-05 12:45:45 -05:00
Page::Feeds => feeds(&state),
Page::FeedView => feed_layout(&state),
Page::AllItems => item_list(&state),
Page::ItemView => item_view(&state),
Page::CategoryView => category_view(&state),
Page::Testing => testing(&state),
}
}
fn home(state: &State) -> Element<'_, Message> {
container(column!(
widgets::navbar(state),
2025-12-05 12:45:45 -05:00
text("RSSCar: RSS Crawler-aided reader.")
))
2025-12-04 22:56:33 -05:00
.padding(0)
.height(Fill)
.width(Fill)
.into()
}
2025-12-05 12:45:45 -05:00
fn feeds(state: &State) -> Element<'_,Message> {
container(
column!(
navbar(state),
scrollable(widgets::list_feeds())
.width(iced::Fill)
.height(iced::Fill)
)
).width(iced::Fill)
.height(iced::Fill).into()
}
fn feed_layout(state: &State) -> Element<'_, Message> {
container(column!(
2025-12-04 22:56:33 -05:00
widgets::navbar(state),
scrollable(widgets::list_items(state.current_feed))
.width(iced::Fill)
.height(iced::Fill),
))
.height(Fill)
.width(Fill)
.into()
}
fn item_view(state: &State) -> Element<'_, Message> {
2025-07-23 12:38:05 -04:00
let title = match state.current_item.clone() {
Some(i) => {i.title}
None => {"".to_owned()}
};
container(column!(
2025-12-04 22:56:33 -05:00
widgets::navbar(state),
text(title).size(34),
2025-07-23 12:38:05 -04:00
media_view(state),
content_view(state),
))
.height(Fill)
.width(Fill)
.into()
}
fn item_list(state: &State) -> Element<'_, Message> {
container(
column!(
navbar(state),
scrollable(
column(
db::get_all_items().iter().map(|i|{
widgets::list_item(i.item_id, i.title.clone(), i.description.clone().unwrap_or("".to_owned()))
}).map(iced::Element::from)
2025-12-05 12:45:45 -05:00
).align_x(iced::Alignment::Center).width(Fill)).width(Fill).spacing(5)
),
).width(Fill).height(Fill).align_x(iced::Alignment::Center).into()
}
fn category_view(state: &State) -> Element<'_,Message> {
column!(
navbar(state)
).spacing(5).into()
}
fn testing(state: &State) -> Element<'_, Message> {
column!(
2025-12-04 22:56:33 -05:00
widgets::navbar(state),
text("Dev Panel"),
button("Add gabe.rocks").on_press(Message::AddFeed(String::from("https://gabe.rocks/rss"))),
button("Add LSN").on_press(Message::AddFeed(String::from(
"https://libresolutions.network/archive/index.xml"
))),
row!(
text_input("Add a feed", &state.feed_input)
.on_input(|val| Message::FieldUpdated(AppField::FeedInput, val))
.width(300),
button("Add feed!").on_press(Message::AddFeed(state.feed_input.clone()))
)
.spacing(5)
.padding(10),
row!(
text_input("OPML Url",&state.feed_input)
.on_input(|val| Message::FieldUpdated(AppField::OPMLInput,val))
.width(300),
button("Add feeds from .OPML").on_press(Message::ProcessOPML(state.feed_input.clone()))
),
button("Wipe DB").on_press(Message::ResetDB),
button("go back!").on_press(Message::ChangePage(Page::Home))
)
.spacing(5)
.into()
}