diff --git a/Cargo.toml b/Cargo.toml index b75801e..5e6e6d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,17 +7,13 @@ edition = "2021" iced = { git = "https://github.com/iced-rs/iced", branch = "master", features = ["image", "markdown", "svg"] } reqwest = { version = "0.12", features= ["blocking","socks"]} rss = "2.0" -web-sys = "0.3.70" -mdka = "1.2.10" #Not Sure if this is useful... rusqlite = {version=">=0.34",features=['bundled']} -html2md = "0.2" -regex = "1" +scraper = "0.23.1" directories = "6.0.0" chrono = "0.4.41" rss_content = { git = "https://code.gabe.rocks/gabriel/rss_content", version = "0.1.1" } -bytes = "1.10.1" -scraper = "0.23.1" url = "2.5.4" +#rfd = "0.15.4" (for importing files) [profile.dev] debug=true diff --git a/assets/icon_placeholder.png b/assets/icon_placeholder.png new file mode 100644 index 0000000..be693c7 Binary files /dev/null and b/assets/icon_placeholder.png differ diff --git a/docs/planned-features.md b/docs/planned-features.md index e9a5d42..41cd847 100644 --- a/docs/planned-features.md +++ b/docs/planned-features.md @@ -21,7 +21,7 @@ At minimum I need to build a functionally complete RSS reader which will include ### Darknet Support Censorship resistance matters! -Tor & I2P sites should be first-class citizens and handled accordingly. This is [currently supported](https://code.gabe.rocks/gabriel/rss-tool/commit/240cb19589bc9d42fbae4331562e0f24413c108c) via the system daemons, but in the future will use mechanisms like [arti-client](https://crates.io/crates/arti-client) +Tor & I2P sites should be first-class citizens and handled accordingly. This is [currently supported](https://code.gabe.rocks/gabriel/rss-tool/commit/240cb19589bc9d42fbae4331562e0f24413c108c) via the system-wide daemons, but in the future will use mechanisms like [arti-client](https://crates.io/crates/arti-client) ### Information Management diff --git a/src/db.rs b/src/db.rs index 30f7468..84517bf 100644 --- a/src/db.rs +++ b/src/db.rs @@ -2,6 +2,7 @@ use super::files::*; use super::net::*; use chrono::DateTime; use chrono::Utc; +use rusqlite::Row; //Maybe use a different time? use rusqlite::{Connection, Result}; use std::path::PathBuf; @@ -205,6 +206,7 @@ pub fn store_items(feed: rss::Channel, feed_id: usize) { }; }); conn.close().unwrap(); + println!("Finished storing items") } #[derive(Debug, Clone)] @@ -219,21 +221,40 @@ pub struct FeedItem { pub media: Option, //date missing! needed for ordering!!! } + +fn row2feed_item(row: &Row) -> FeedItem{ + FeedItem { + item_id: row.get(0).unwrap(), + title: row.get(1).unwrap_or("NO TITLE!".to_owned()), + url: row.get(2).unwrap(), + date: row.get(3).unwrap(), + media: row.get(4).unwrap(), + description: row.get(5).unwrap(), + content: row.get(6).unwrap(), + icon: None, + } +} + +pub fn get_all_items() -> Vec { + let conn = get_db(); + let mut stmt = conn.prepare("select itemID,title,url,date,media,description,content from items").unwrap(); + let items: Result> = stmt.query_map([],|row| { + Ok(row2feed_item(row)) + }).unwrap().collect(); + match items { + Ok(i) => i, + Err(_) => { + panic!("No Items found!") + } + } +} + pub fn get_feed_items(feed_id: usize) -> Vec { let conn = get_db(); let mut stmt = conn.prepare("select itemID,title,url,date,media,description,content from items where feedID = ?1 order by date(date)").unwrap(); let items: Result> = stmt .query_map([feed_id], |row| { - Ok(FeedItem { - item_id: row.get(0).unwrap(), - title: row.get(1).unwrap(), - url: row.get(2).unwrap(), - date: row.get(3).unwrap(), - media: row.get(4).unwrap(), - description: row.get(5).unwrap(), - content: row.get(6).unwrap(), - icon: None, - }) + Ok(row2feed_item(row)) }) .unwrap() .collect(); diff --git a/src/html.rs b/src/html.rs deleted file mode 100644 index 8d307fe..0000000 --- a/src/html.rs +++ /dev/null @@ -1,23 +0,0 @@ -use html2md; -use regex::Regex; - - -/* - Content needs to be processed in chunks. - Separate out content into blocks that can be used by the UI framework - p -> text - a -> link (does this exist?) - img - > image widget - - and so on... -*/ - -/* this works, but... -- images are omitted -- Blockquotes don't display -*/ -pub fn process(content: String) -> String { - let re = Regex::new(r"(?s)]*>.*?").unwrap(); - let cleaned = re.replace_all(&content,""); - html2md::parse_html(&cleaned) -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7b03e47..951ca1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ #[allow(unused)] pub mod net; pub mod ui; -pub mod html; pub mod db; pub mod files; pub mod widgets; diff --git a/src/ui.rs b/src/ui.rs index 8b9d073..79189ba 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,10 +1,10 @@ use crate::db::FeedItem; use crate::widgets::content_view; use crate::widgets::media_view; +use crate::widgets::navbar; use super::db; use super::widgets; -use iced::widget::markdown::Url; use iced::widget::row; use iced::widget::scrollable; use iced::widget::text_input; @@ -16,9 +16,18 @@ use iced::{ }; use rss_content::parse_content; use rss_content::Content; +const ICON: &[u8] = include_bytes!("../assets/icon_placeholder.png"); pub fn user_interface() -> iced::Result { - iced::run(update, view) + //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() } #[derive(Clone, Debug)] @@ -27,6 +36,7 @@ pub enum Page { FeedView, AllItems, ItemView, + CategoryView, Testing, } @@ -59,7 +69,7 @@ pub enum Message { RemoveFeed(usize), LoadItem(usize), FieldUpdated(AppField, String), - LinkClicked(Url), + LinkClicked(String), Done(String), ResetDB, } @@ -144,12 +154,14 @@ fn view(state: &State) -> Element<'_, Message> { 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> { +fn home(state: &State) -> Element<'_, Message> { container(column!( + widgets::navbar(state), scrollable(widgets::list_feeds()) .width(iced::Fill) .height(iced::Fill), @@ -193,15 +205,35 @@ fn item_view(state: &State) -> Element<'_, Message> { .into() } -fn item_list(_: &State) -> Element<'_, Message> { - todo!() +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) + ).align_x(iced::Alignment::Center)).width(Fill).spacing(5) + ), + + ).width(Fill).height(Fill).into() } + +fn category_view(state: &State) -> Element<'_,Message> { + column!( + navbar(state) + + ).spacing(5).into() + +} + fn testing(state: &State) -> Element<'_, Message> { column!( 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/rss" + "https://libresolutions.network/archive/index.xml" ))), row!( text_input("Add a feed", &state.feed_input) diff --git a/src/widgets.rs b/src/widgets.rs index 5e645b6..9b3b730 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,7 +1,9 @@ use crate::net; +use crate::ui::Page; use super::db; use super::ui; +use iced::widget::Text; use iced::widget::markdown; use iced::widget::row; use iced::widget::scrollable; @@ -98,6 +100,23 @@ pub fn media_view(state: &'_ ui::State) -> Element<'_, Message> { } } +pub fn navbar (state: &'_ ui::State) -> Element<'_,Message>{ + row!( + button("Home").on_press(Message::ChangePage(Page::Home)), + button("All Items").on_press(Message::ChangePage(Page::AllItems)), + button("Categories").on_press(Message::ChangePage(Page::CategoryView)) + ).spacing(5).padding(5).width(iced::Fill).into() + +} + +pub fn list_item(id: usize,title: String, description: String) -> Column<'static,Message>{ + Column::new() + .push(button(Text::new(title)).on_press(Message::LoadItem(id))) + .push(text(description)) + .align_x(iced::Alignment::Center) + .spacing(5) +} + pub fn content_view(state: &'_ ui::State) -> iced::widget::Scrollable<'_, Message> { let item = state.current_item.clone().unwrap();