From ca139525a27d8ce2aa7dad551f28e662aca9c258 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 10 Jan 2025 07:32:12 -0500 Subject: [PATCH] Successfully loaded a feed and items into the db, able to parse and display as markdown... kinda --- .gitignore | 1 + Cargo.toml | 11 ++++- src/db.rs | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/html.rs | 17 +++++++- src/main.rs | 61 +++++++++++++++++++++++--- src/net.rs | 23 +++++++++- src/ui.rs | 13 ++++-- 7 files changed, 234 insertions(+), 14 deletions(-) create mode 100644 src/db.rs diff --git a/.gitignore b/.gitignore index a9d37c5..7a6a9d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +*.db \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index b773cd7..9d8c493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,13 @@ iced = {git = "https://github.com/iced-rs/iced", branch = "master", features = [ reqwest = { version = "0.12", features= ["blocking"]} rss = "2.0" web-sys = "0.3.70" -mdka = "1.2.10" \ No newline at end of file +mdka = "1.2.10" #Not Sure if this is useful... +rusqlite = {version="0.32",features=['bundled']} +html2md = "0.2" +regex = "1" + +[profile.dev] +opt-level = 3 + +[profile.release] +opt-level = 3 \ No newline at end of file diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..2e40dfd --- /dev/null +++ b/src/db.rs @@ -0,0 +1,122 @@ +use rusqlite::{params, Connection, Result}; +use super::net::{*}; + + +/* +Cache is the in-memory database +Any changes/updates are written to file database +*/ + +struct Item{ + title: String, + content: Option +} + +const DB_LOCATION: &str = "rsscar.db"; + + +const feeds_table_create: &str = "CREATE TABLE IF NOT EXISTS 'feeds' ( + 'feedID' INTEGER NOT NULL, + 'title' TEXT NOT NULL, + 'description' TEXT, + 'icon' BLOB, + 'url' text not null unique on conflict replace, + 'subscribed' INTEGER, + 'last_updated' TEXT , + PRIMARY KEY('feedID') + );"; +const feeds_index_create: &str = "CREATE INDEX IF NOT EXISTS 'subscribed_feeds_idx' ON 'feeds' ( + 'feedID' ASC + ) WHERE 'subscribed' = 1;"; +const items_table_create: &str = "CREATE TABLE IF NOT EXISTS 'items' ( + 'itemID' INTEGER NOT NULL, + 'title' TEXT NOT NULL, + 'icon' BLOB, + 'url' text not null unique on conflict replace, + 'description' TEXT, + 'content' TEXT, + 'read' INTEGER DEFAULT 0, + PRIMARY KEY('itemID') +);"; +const items_index_create: &str = "CREATE INDEX IF NOT EXISTS 'items_idx' on 'items'('itemID' ASC);"; + +pub fn initialize() { + let conn = Connection::open(DB_LOCATION).unwrap(); + conn.execute(feeds_table_create,[]).unwrap(); + conn.execute(feeds_index_create,[]).unwrap(); + conn.execute(items_table_create,[]).unwrap(); + conn.execute(items_index_create,[]).unwrap(); + conn.close(); + println!("Database Initialized.") + +} + +pub fn add_feed(url: &str) { + let conn = Connection::open(DB_LOCATION).unwrap(); + let feed = load_rss(url).unwrap(); + let new_feed = feed.clone(); + conn.execute("insert into feeds(title,url,description) values(?1,?2,?3)", + [feed.title,url.to_owned(),feed.description]).unwrap(); + conn.close(); + store_items(new_feed); + //defaulting to Libre Solutions +} + + +pub fn store_items(feed: rss::Channel) { + let conn = Connection::open(DB_LOCATION).unwrap(); + feed.items.iter().for_each(|i: &rss::Item|{ + conn.execute("insert into items(url,title,description,content) values(?1,?2,?3,?4)",[ + i.link.clone(), + i.title.clone(), + i.description.clone(), + i.content.clone()] + ).unwrap(); + }); + conn.close(); +} + +pub fn return_item() -> String{ + let conn = Connection::open(DB_LOCATION).unwrap(); + let item = conn.query_row("select title,content from items where rowid=?1",[1],|row|{ + Ok( + Item { title: row.get(0).unwrap(), content: row.get(1).unwrap() } + ) + }).unwrap(); + match item.content { + Some(content) => content, + None => panic!() + } +} + +struct ReturnedFeedURLs{ + url: String +} +pub fn update_feeds() { + //get feeds + let conn = Connection::open(DB_LOCATION).unwrap(); + let mut stmt = conn.prepare("select url from feeds").unwrap(); + let rows = stmt.query_map([],|row| { + Ok(ReturnedFeedURLs{ + url:row.get(0).unwrap() + }) + }).unwrap(); + let mut urls: Vec = Vec::new(); + for feed in rows{ + let url = feed.unwrap().url.clone(); + urls.push(url); + } + stmt.finalize(); + conn.close(); + + for u in urls { + store_items(load_rss(&u).unwrap()); + } + + + + //for each feed + // insert items into database + + //close out +} \ No newline at end of file diff --git a/src/html.rs b/src/html.rs index 2e25b66..505ba98 100644 --- a/src/html.rs +++ b/src/html.rs @@ -1,3 +1,16 @@ -pub fn process_html(content: String) -> String { - content +use html2md; +use regex::Regex; + + +/* 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 f013d1d..ec65926 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,68 @@ mod net; -use iced::{application, widget::{self, button, column, row, text}, Settings, Theme}; +use db::initialize; +use iced::{application, widget::{self, button, column, markdown, row, text}, Settings, Theme}; use net::load_rss; use rss::Channel; mod ui; mod html; +mod db; -pub fn main() -> iced::Result { - iced::application("Really Sweet Stuff",State::update,State::view) + + +/*pub fn main() -> iced::Result { + db::initialize(); +iced::application("Really Sweet Stuff",State::update,State::view) .theme(theme) .run() +}*/ +pub fn main() -> iced::Result{ + db::initialize(); + db::add_feed("https://libresolutions.network/archive/index.xml"); + iced::application("Really Sweet Stuff",Viewer::update,Viewer::view) + .run() + + } + +#[derive(Clone,Debug)] +struct Viewer { + content: Vec +} +impl Default for Viewer{ + fn default() -> Self { + Self { content: markdown::parse(&html::process(db::return_item())).collect(), + } + } + } +#[derive(Clone,Debug)] +enum Changes{ + Nullchange(markdown::Url) +} + +impl Viewer{ + fn update(&mut self, _mes: Changes) { + + } + fn view(&self) -> iced::Element<'_, Changes> { + widget::container( + widget::scrollable( + markdown::view( + &self.content, + markdown::Settings::default(), + markdown::Style::from_palette(iced::Theme::Dark.palette()) + ).map(Changes::Nullchange)) + ) + .align_x(iced::alignment::Horizontal::Center) + .align_y(iced::alignment::Vertical::Center) + .width(iced::Length::Fill) + .height(iced::Length::Fill) + .padding(5) + .into() + + + } +} #[derive(Clone,Debug)] struct State { scene: Scene, @@ -52,9 +104,8 @@ impl Default for State { fn default() -> Self { let main = net::load_rss("https://libresolutions.network/rss").unwrap(); let small = net::load_rss("https://libresolutions.network/about/index.xml").unwrap(); - let test = net::load_rss("http://localhost:1313/about/index.xml").unwrap(); let gabefeed = net::load_rss("https://gabe.rocks/rss").unwrap(); - let channels = vec![test]; + let channels = vec![small]; Self { scene: Scene::ItemView, channels, diff --git a/src/net.rs b/src/net.rs index af1f8bd..42f9c70 100644 --- a/src/net.rs +++ b/src/net.rs @@ -23,10 +23,29 @@ pub fn load_rss(url: &str) -> Option{ Err(e) => { panic!("Empty response")} } }, - Err(err) => {panic!("Error loading feed.")} + Err(err) => {panic!("Error loading feed.:{}",err)} } } - +pub fn get_item_image(item: &rss::Item) -> Option<&str> { + // Only bother with itunes:image + print!("{} \n", item.title().unwrap()); + match item.itunes_ext() { + Some(e) => match e.image() { + Some(img) => { + println!("Image found: {}", img); + Some(img) + } + None => { + println!("Itunes extension found, but image was not.."); + None + } + }, + None => { + println!("found no extensions"); + None + } + } +} pub fn download_image(url: &str) -> Option{ match reqwest::blocking::get(url) { Ok(r) => { diff --git a/src/ui.rs b/src/ui.rs index c550dcf..fd26b61 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use crate::net::download_image; - +use super::html; use super::net; use iced::{ self, @@ -115,7 +115,7 @@ pub fn item_view(item: &rss::Item) -> Container { text(title).size(50), text(date).size(25), text(desc).size(35), - iced::widget::scrollable(text(super::html::process_html(content)).size(25)), + iced::widget::scrollable(text(html::process(content)).size(25)), ] .spacing(10) .align_x(iced::Alignment::Start); @@ -124,6 +124,11 @@ pub fn item_view(item: &rss::Item) -> Container { .height(Fill) } +//Display Story +pub fn content_view<'a>(content: String) -> Container<'a, Message>{ + todo!() +} + pub fn fancy_button<'a>( icon: iced::widget::Image, title: &'a str, @@ -136,7 +141,7 @@ pub fn fancy_button<'a>( .align_x(iced::Alignment::Start) .spacing(5) ] - .spacing(5) + .spacing(15) .align_y(iced::Alignment::Center), ) .align_x(iced::alignment::Horizontal::Center); @@ -145,7 +150,7 @@ pub fn fancy_button<'a>( .width(Fill) } -fn get_item_image(item: &rss::Item) -> Option<&str> { +pub fn get_item_image(item: &rss::Item) -> Option<&str> { // Only bother with itunes:image print!("{} \n", item.title().unwrap()); match item.itunes_ext() {