loading arbitrary feeds via the UI works!

This commit is contained in:
Gabriel 2025-07-04 11:38:49 -04:00
parent 03084ac15f
commit 2604306d67
3 changed files with 44 additions and 38 deletions

View file

@ -25,12 +25,13 @@ fn get_db_path() -> PathBuf {
fn get_db() -> Connection{ fn get_db() -> Connection{
Connection::open(get_db_path()).unwrap() Connection::open(get_db_path()).unwrap()
} }
//url needs to be from the feed URL NOT the url in the channel itself!!
const FEEDS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'feeds' ( const FEEDS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'feeds' (
'feedID' INTEGER NOT NULL, 'feedID' INTEGER NOT NULL,
'title' TEXT NOT NULL, 'title' TEXT NOT NULL,
'description' TEXT, 'description' TEXT,
'icon' BLOB, 'icon' BLOB,
'url' text not null unique on conflict replace, 'url' text,
'subscribed' INTEGER NOT NULL default 0, 'subscribed' INTEGER NOT NULL default 0,
'last_updated' TEXT , 'last_updated' TEXT ,
PRIMARY KEY('feedID') PRIMARY KEY('feedID')
@ -43,19 +44,21 @@ const FEEDS_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'subscribed_feeds_
/* */ /* */
const ITEMS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'items' ( const ITEMS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'items' (
'itemID' INTEGER NOT NULL, 'itemID' INTEGER NOT NULL,
'feedID' INTEGER NOT NULL,
'title' TEXT NOT NULL, 'title' TEXT NOT NULL,
'icon' BLOB, 'icon' BLOB,
'url' text not null unique on conflict replace, 'url' text not null unique on conflict replace,
'description' TEXT, 'description' TEXT,
'content' TEXT, 'content' TEXT,
'read' INTEGER DEFAULT 0, 'read' INTEGER DEFAULT 0,
PRIMARY KEY('itemID') PRIMARY KEY('itemID'),
FOREIGN KEY('feedID') REFERENCES 'feeds'('feedID')
);"; );";
const ITEMS_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'items_idx' on 'items'('itemID' ASC);"; const ITEMS_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'items_idx' on 'items'('itemID' ASC);";
const DB_RESET: &str = " const DB_RESET: &str = "
drop table feeds;
drop table items; drop table items;
drop table feeds;
"; ";
pub fn reset(){ pub fn reset(){
@ -94,24 +97,29 @@ pub fn add_feed(url: &str) {
[feed.title, url.to_owned(), feed.description, time], [feed.title, url.to_owned(), feed.description, time],
) )
.unwrap(); .unwrap();
conn.close().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 //need to get the feed_id from the DB and then make sure items are mapped to feed
store_items(new_feed); store_items(new_feed,id);
} }
pub fn store_items(feed: rss::Channel) { pub fn store_items(feed: rss::Channel,feed_id: usize) {
let conn = Connection::open(get_db_path()).unwrap(); let conn = Connection::open(get_db_path()).unwrap();
feed.items.iter().for_each(|i: &rss::Item| { feed.items.iter().for_each(|i: &rss::Item| {
conn.execute( conn.execute(
"insert into items(url,title,description,content) values(?1,?2,?3,?4)", "insert into items(url,title,description,content,feedID)
values(?1,?2,?3,?4,?5)",
[ [
i.link.clone(), i.link.clone(),
i.title.clone(), i.title.clone(),
i.description.clone(), i.description.clone(),
i.content.clone(), i.content.clone(),
Some(feed_id.to_string())
], ],
) )
.unwrap(); .ok();
}); });
conn.close().unwrap(); conn.close().unwrap();
} }
@ -149,8 +157,8 @@ pub struct FeedItem {
pub fn get_feed_items(id: usize) -> Vec<FeedItem>{ pub fn get_feed_items(id: usize) -> Vec<FeedItem>{
let conn = get_db(); let conn = get_db();
let mut stmt = conn.prepare("select itemID,title,url,icon,description,content from items").unwrap(); let mut stmt = conn.prepare("select itemID,title,url,icon,description,content from items where feedID = ?1").unwrap();
let items:Result<Vec<FeedItem>> = stmt.query_map([], |row| { let items:Result<Vec<FeedItem>> = stmt.query_map([id], |row| {
Ok(FeedItem{ Ok(FeedItem{
item_id: row.get(0).unwrap(), item_id: row.get(0).unwrap(),
title: row.get(1).unwrap(), title: row.get(1).unwrap(),
@ -209,30 +217,5 @@ struct ReturnedFeedURLs {
url: String, url: String,
} }
pub fn update_feeds() { pub fn update_feeds() {
//get feeds todo!()
let conn = Connection::open(get_db_path()).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<String> = Vec::new();
for feed in rows {
let url = feed.unwrap().url.clone();
urls.push(url);
}
stmt.finalize().unwrap();
conn.close().unwrap();
for u in urls {
store_items(load_rss(&u).unwrap());
}
//for each feed
// insert items into database
//close out
} }

View file

@ -2,7 +2,9 @@ use crate::db::add_feed;
use super::db; use super::db;
use super::widgets; use super::widgets;
use iced::widget::row;
use iced::widget::scrollable; use iced::widget::scrollable;
use iced::widget::text_input;
use iced::{ use iced::{
widget::{button, column, container, text}, widget::{button, column, container, text},
Element, Element,
@ -23,13 +25,15 @@ enum Page {
struct State { struct State {
page: Page, page: Page,
current_feed: usize current_feed: usize,
feed_input: String
} }
impl Default for State { impl Default for State {
fn default() -> Self { fn default() -> Self {
State { State {
page: Page::home, page: Page::home,
current_feed: 0 current_feed: 0,
feed_input: String::from("")
} }
} }
} }
@ -39,9 +43,15 @@ pub enum Message {
ChangePage(Page), ChangePage(Page),
LoadFeed(usize), LoadFeed(usize),
AddFeed(String), AddFeed(String),
FieldUpdated(AppField,String),
ResetDB ResetDB
} }
#[derive(Debug,Clone)]
enum AppField {
FeedInput
}
fn update(state: &mut State, mes: Message) { fn update(state: &mut State, mes: Message) {
match mes { match mes {
Message::ChangePage(p) => state.page=p, Message::ChangePage(p) => state.page=p,
@ -53,6 +63,13 @@ fn update(state: &mut State, mes: Message) {
Message::AddFeed(f) => { Message::AddFeed(f) => {
db::add_feed(&f); db::add_feed(&f);
}, },
Message::FieldUpdated(field, value) => {
match field {
AppField::FeedInput => {
state.feed_input=value;
}
}
},
Message::ResetDB => { Message::ResetDB => {
db::reset(); db::reset();
} }
@ -109,6 +126,10 @@ fn testing(state: &State) -> Element<'_, Message> {
text("Dev Panel"), text("Dev Panel"),
button("Add gabe.rocks").on_press(Message::AddFeed(String::from("https://gabe.rocks/rss"))), 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"))), button("Add LSN").on_press(Message::AddFeed(String::from("https://libresolutions.network/rss"))),
row!(
text_input("Add a feed",&state.feed_input).on_input(|val| Message::FieldUpdated(AppField::FeedInput,val)),
button("Add feed!").on_press(Message::AddFeed(state.feed_input.clone()))
),
button("Wipe DB").on_press(Message::ResetDB), button("Wipe DB").on_press(Message::ResetDB),
button("go back!").on_press(Message::ChangePage(Page::home)) button("go back!").on_press(Message::ChangePage(Page::home))
) )

View file

@ -19,6 +19,7 @@ pub fn list_feeds() -> iced::widget::Column<'static, Message> {
) )
.align_x(iced::Alignment::Start) .align_x(iced::Alignment::Start)
.spacing(5) .spacing(5)
.padding(15)
} }
pub fn list_items(feed_id: usize) -> iced::widget::Column<'static,Message> { pub fn list_items(feed_id: usize) -> iced::widget::Column<'static,Message> {
@ -32,4 +33,5 @@ pub fn list_items(feed_id: usize) -> iced::widget::Column<'static,Message> {
) )
.align_x(iced::Alignment::Start) .align_x(iced::Alignment::Start)
.spacing(5) .spacing(5)
.padding(15)
} }