From afd34b416a3a063a71d05c61afa7f69b7c5404fb Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 25 Jul 2025 19:03:50 -0400 Subject: [PATCH] Absolute basic feed management: Removing feeds! --- src/db.rs | 162 ++++++++++++++++++++++++++++--------------------- src/ui.rs | 10 +++ src/widgets.rs | 6 +- 3 files changed, 107 insertions(+), 71 deletions(-) diff --git a/src/db.rs b/src/db.rs index 47a21a9..f0e5a45 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,22 +1,22 @@ -use std::path::PathBuf; -use rusqlite::{ Connection, Result}; use super::files::*; use super::net::*; -use chrono::Utc; //Maybe use a different time? use chrono::DateTime; +use chrono::Utc; +//Maybe use a different time? +use rusqlite::{Connection, Result}; +use std::path::PathBuf; /* Cache is the in-memory database Any changes/updates are written to file database */ - const DB_LOCATION: &str = "rsscar.db"; fn get_db_path() -> PathBuf { get_data_directory().join(DB_LOCATION) } -fn get_db() -> Connection{ +fn get_db() -> Connection { Connection::open(get_db_path()).unwrap() } //url needs to be from the feed URL NOT the url in the channel itself!! @@ -52,26 +52,29 @@ const ITEMS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'items' ( );"; const ITEMS_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'items_idx' on 'items'('itemID' ASC);"; -const ITEMS_FEED_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'item_feed_idx' on 'items'('feedID' ASC);"; +const ITEMS_FEED_INDEX_CREATE: &str = + "CREATE INDEX IF NOT EXISTS 'item_feed_idx' on 'items'('feedID' ASC);"; const DB_RESET: &str = " drop table items; drop table feeds; "; -pub fn reset(){ +pub fn reset() { println!("⚠️WARNING⚠️\nResetting Database"); let conn = get_db(); match conn.execute_batch(DB_RESET) { - Ok(_) => {println!("Database successfully wiped.")} - Err(e) => {panic!("Error erasing database.\nError: {0}",e)} + Ok(_) => { + println!("Database successfully wiped.") + } + Err(e) => { + panic!("Error erasing database.\nError: {0}", e) + } } conn.close().unwrap(); initialize(); - } - pub fn initialize() { let path = get_db_path(); println!("Database at {} initialized", path.to_string_lossy()); @@ -80,40 +83,54 @@ pub fn initialize() { conn.execute(FEEDS_INDEX_CREATE, []).unwrap(); conn.execute(ITEMS_TABLE_CREATE, []).unwrap(); conn.execute(ITEMS_INDEX_CREATE, []).unwrap(); - conn.execute(ITEMS_FEED_INDEX_CREATE,[]).unwrap(); + conn.execute(ITEMS_FEED_INDEX_CREATE, []).unwrap(); conn.close().unwrap(); println!("Database Initialized.") } - pub fn add_feed(url: &str) -> Option { - let conn = Connection::open(get_db_path()).unwrap(); + let conn = get_db(); let feed = load_rss(url).unwrap(); - let new_feed = feed.clone(); + let new_feed = feed.clone(); let time = Utc::now().to_rfc2822(); match conn.execute( "insert into feeds(title,url,description,last_updated) values(?1,?2,?3,?4)", [feed.title, url.to_string(), feed.description, time], ) { - Ok(_) => { - - } + Ok(_) => {} Err(e) => { - println!("Couldn't add feed:{}\nError:{}",url,e); + println!("Couldn't add feed:{}\nError:{}", url, e); return None; } } - - let mut stmt = conn.prepare("select feedID from feeds where url=?1").unwrap(); - let id: usize = stmt.query_row([url],|row| { - row.get(0) - }).unwrap(); + + let mut stmt = conn + .prepare("select feedID from feeds where url=?1") + .unwrap(); + let id: usize = stmt.query_row([url], |row| row.get(0)).unwrap(); //need to get the feed_id from the DB and then make sure items are mapped to feed - store_items(new_feed,id); + store_items(new_feed, id); Some(id) } -pub fn store_items(feed: rss::Channel,feed_id: usize) { +fn remove_items(feed_id: usize) -> Result{ + let conn = get_db(); + conn.execute("delete from items where feedID = ?1", [feed_id]) +} + +pub fn remove_feed(feed_id: usize) { + let _ = remove_items(feed_id); + let conn = get_db(); + match conn.execute("delete from feeds where feedID = ?1",[feed_id]) { + Ok(_) => {} + Err(e) => { + println!("Failed to delete feed by id: {}\nError:{}",feed_id,e); + } + } + +} + +pub fn store_items(feed: rss::Channel, feed_id: usize) { let conn = Connection::open(get_db_path()).unwrap(); feed.items.into_iter().for_each(|i: rss::Item| { let t = i.clone(); @@ -129,10 +146,10 @@ pub fn store_items(feed: rss::Channel,feed_id: usize) { i.pub_date.clone(), { match t.enclosure() { - Some(e) => {Some(e.url().to_owned())} - None => {Some("".to_owned())} + Some(e) => Some(e.url().to_owned()), + None => Some("".to_owned()), } - } + }, ], ) .ok(); @@ -140,8 +157,7 @@ pub fn store_items(feed: rss::Channel,feed_id: usize) { conn.close().unwrap(); } - -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] pub struct FeedItem { pub item_id: usize, pub title: String, @@ -150,48 +166,52 @@ pub struct FeedItem { pub description: Option, pub content: Option, pub date: Option, - pub media: Option - //date missing! needed for ordering!!! - + pub media: Option, //date missing! needed for ordering!!! } -pub fn get_feed_items(feed_id: usize) -> Vec{ +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, + 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, + }) }) - }).unwrap().collect(); + .unwrap() + .collect(); match items { - Ok(i) => {i}, - Err(_) => {panic!("No items for this feed\nFeedID:{}",feed_id)} + Ok(i) => i, + Err(_) => { + panic!("No items for this feed\nFeedID:{}", feed_id) + } } } pub fn get_item(item_id: usize) -> FeedItem { let conn = get_db(); let mut stmt = conn.prepare("select itemID,title,url,icon,date,media,description,content from items where itemID = ?1").unwrap(); - let item:FeedItem = stmt.query_one([item_id],|row| { - Ok( - FeedItem{ - item_id: row.get(0).unwrap(), - title: row.get(1).unwrap(), - url: row.get(2).unwrap(), - icon: row.get(3).unwrap(), - date: row.get(4).unwrap(), - media: row.get(5).unwrap(), - description: row.get(6).unwrap(), - content: row.get(7).unwrap(), + let item: FeedItem = stmt + .query_one([item_id], |row| { + Ok(FeedItem { + item_id: row.get(0).unwrap(), + title: row.get(1).unwrap(), + url: row.get(2).unwrap(), + icon: row.get(3).unwrap(), + date: row.get(4).unwrap(), + media: row.get(5).unwrap(), + description: row.get(6).unwrap(), + content: row.get(7).unwrap(), + }) }) - }).unwrap(); + .unwrap(); item } @@ -205,11 +225,11 @@ pub struct Feed { pub last_updated: Option>, } -fn time_string_conversion(str: String) -> Option>{ +fn time_string_conversion(str: String) -> Option> { match DateTime::parse_from_rfc2822(&str) { - Ok(dt) => {Some(dt.to_utc())}, - Err(_) => {None} - } + Ok(dt) => Some(dt.to_utc()), + Err(_) => None, + } } pub fn get_feeds() -> Vec { @@ -223,15 +243,17 @@ pub fn get_feeds() -> Vec { description: row.get(2).unwrap(), icon: row.get(3).unwrap(), url: row.get(4).unwrap(), - subscribed: row.get::<_,bool>(5).unwrap(), - last_updated:time_string_conversion(row.get(6).unwrap()), + subscribed: row.get::<_, bool>(5).unwrap(), + last_updated: time_string_conversion(row.get(6).unwrap()), }) - }).unwrap().collect(); + }) + .unwrap() + .collect(); match rows { - Ok(r) => { - r + Ok(r) => r, + Err(_) => { + panic!("No idea what causes this") } - Err(_) => {panic!("No idea what causes this")} } } pub fn update_feeds() { diff --git a/src/ui.rs b/src/ui.rs index d94e815..8b9d073 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -56,6 +56,7 @@ pub enum Message { ChangePage(Page), LoadFeed(usize), AddFeed(String), + RemoveFeed(usize), LoadItem(usize), FieldUpdated(AppField, String), LinkClicked(Url), @@ -73,6 +74,11 @@ async fn add_feed_background(url: String) -> String { 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 { match mes { @@ -85,10 +91,14 @@ fn update(state: &mut State, mes: Message) -> Task { state.page = Page::FeedView; Task::none() } + 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); diff --git a/src/widgets.rs b/src/widgets.rs index b896594..211968c 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,6 +1,7 @@ use super::db; use super::ui; use iced::widget::markdown; +use iced::widget::row; use iced::widget::scrollable; use iced::widget::Column; use iced::Theme; @@ -18,7 +19,10 @@ pub fn list_feeds() -> iced::widget::Column<'static, Message> { feeds .iter() .map(|f| { - button(text(f.title.clone())).on_press(Message::LoadFeed(f.feed_id)) + row!( + button(text(f.title.clone())).on_press(Message::LoadFeed(f.feed_id)), + button(text("Remove feed")).on_press(Message::RemoveFeed(f.feed_id)) + ) }) .map(Element::from), )