Successfully loaded a feed and items into the db, able to parse and display as markdown... kinda

This commit is contained in:
Gabriel 2025-01-10 07:32:12 -05:00
parent 7f95347330
commit ca139525a2
7 changed files with 234 additions and 14 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
target
Cargo.lock
*.db

View file

@ -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"
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

122
src/db.rs Normal file
View file

@ -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<String>
}
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<String> = 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
}

View file

@ -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)<iframe[^>]*>.*?</iframe>").unwrap();
let cleaned = re.replace_all(&content,"");
html2md::parse_html(&cleaned)
}

View file

@ -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<markdown::Item>
}
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,

View file

@ -23,10 +23,29 @@ pub fn load_rss(url: &str) -> Option<Channel>{
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<iced::widget::image::Handle>{
match reqwest::blocking::get(url) {
Ok(r) => {

View file

@ -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<Message> {
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<Message> {
.height(Fill)
}
//Display Story
pub fn content_view<'a>(content: String) -> Container<'a, Message>{
todo!()
}
pub fn fancy_button<'a>(
icon: iced::widget::Image<Handle>,
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() {