Starting to support fancier elements...

This commit is contained in:
Gabriel 2025-07-22 17:32:48 -04:00
parent 56a62e96fd
commit a4756b8ec4
2 changed files with 109 additions and 81 deletions

135
src/ui.rs
View file

@ -1,17 +1,20 @@
use crate::db::FeedItem;
use crate::widgets::content_view; use crate::widgets::content_view;
use super::db; use super::db;
use super::widgets; use super::widgets;
use iced::widget::markdown::Url;
use iced::widget::row; use iced::widget::row;
use iced::widget::scrollable; use iced::widget::scrollable;
use iced::widget::text_input; use iced::widget::text_input;
use iced::widget::markdown::Url;
use iced::Task; use iced::Task;
use iced::{ use iced::{
widget::{button, column, container, text}, widget::{button, column, container, text},
Element, Element,
Length::Fill, Length::Fill,
}; };
use rss_content::parse_content;
use rss_content::Content;
pub fn user_interface() -> iced::Result { pub fn user_interface() -> iced::Result {
iced::run(update, view) iced::run(update, view)
@ -26,20 +29,24 @@ pub enum Page {
Testing, Testing,
} }
struct State { pub struct State {
page: Page, page: Page,
current_feed: usize, current_feed: usize,
current_item: usize, pub current_item: Option<FeedItem>,
feed_input: String pub item_description: Vec<Content>,
pub item_content: Vec<Content>,
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,
current_item: 0, current_item: None,
feed_input: String::from("") item_description: Vec::new(),
} item_content: Vec::new(),
feed_input: String::from(""),
}
} }
} }
@ -49,15 +56,15 @@ pub enum Message {
LoadFeed(usize), LoadFeed(usize),
AddFeed(String), AddFeed(String),
LoadItem(usize), LoadItem(usize),
FieldUpdated(AppField,String), FieldUpdated(AppField, String),
LinkClicked(Url), LinkClicked(Url),
Done(String), Done(String),
ResetDB ResetDB,
} }
#[derive(Debug,Clone)] #[derive(Debug, Clone)]
pub enum AppField { pub enum AppField {
FeedInput FeedInput,
} }
async fn add_feed_background(url: String) -> String { async fn add_feed_background(url: String) -> String {
@ -66,51 +73,58 @@ async fn add_feed_background(url: String) -> String {
"Done adding feed".to_string() "Done adding feed".to_string()
} }
fn update(state: &mut State, mes: Message) -> Task<Message>{ fn update(state: &mut State, mes: Message) -> Task<Message> {
match mes { match mes {
Message::ChangePage(p) => { Message::ChangePage(p) => {
state.page=p; state.page = p;
Task::none() Task::none()
}, }
Message::LoadFeed(feed_id) => { Message::LoadFeed(feed_id) => {
state.current_feed = feed_id; state.current_feed = feed_id;
state.page=Page::FeedView; state.page = Page::FeedView;
Task::none() Task::none()
}, }
Message::AddFeed(f) => { Message::AddFeed(f) => {
state.feed_input = "".to_string(); state.feed_input = "".to_string();
Task::perform( Task::perform(add_feed_background(f.to_string()), Message::Done)
add_feed_background(f.to_string()), }
Message::Done
)
},
Message::LinkClicked(l) => { Message::LinkClicked(l) => {
println!("Link clicked: {}",l); println!("Link clicked: {}", l);
Task::none() Task::none()
} }
Message::LoadItem(id) => { Message::LoadItem(id) => {
state.current_item = id; let item = db::get_item(id);
state.item_description = match &item.description {
Some(d) => {
parse_content(&d)
}
None => Vec::new()
};
state.item_content = match &item.content{
Some(c) => {
parse_content(&c)
}
None => Vec::new()
};
state.current_item = Some(item);
state.page = Page::ItemView; state.page = Page::ItemView;
Task::none() Task::none()
} }
Message::Done(_) => {Task::none()}, Message::Done(_) => Task::none(),
Message::FieldUpdated(field, value) => { Message::FieldUpdated(field, value) => {
match field { match field {
AppField::FeedInput => { AppField::FeedInput => {
state.feed_input=value; state.feed_input = value;
} }
} }
Task::none() Task::none()
}, }
Message::ResetDB => { Message::ResetDB => {
db::reset(); db::reset();
Task::none() Task::none()
} }
} }
} }
fn view(state: &State) -> Element<'_, Message> { fn view(state: &State) -> Element<'_, Message> {
@ -125,7 +139,9 @@ fn view(state: &State) -> Element<'_, Message> {
fn home(_state: &State) -> Element<'_, Message> { fn home(_state: &State) -> Element<'_, Message> {
container(column!( container(column!(
scrollable(widgets::list_feeds()).width(iced::Fill).height(iced::Fill), scrollable(widgets::list_feeds())
.width(iced::Fill)
.height(iced::Fill),
button("Go to test!").on_press(Message::ChangePage(Page::Testing)) button("Go to test!").on_press(Message::ChangePage(Page::Testing))
)) ))
.padding(15) .padding(15)
@ -134,47 +150,50 @@ fn home(_state: &State) -> Element<'_, Message> {
.into() .into()
} }
fn feed_layout(state: &State) -> Element<'_, Message> { fn feed_layout(state: &State) -> Element<'_, Message> {
container( container(column!(
column!( button(text("Home")).on_press(Message::ChangePage(Page::Home)),
button(text("Home")).on_press(Message::ChangePage(Page::Home)), scrollable(widgets::list_items(state.current_feed))
scrollable(widgets::list_items(state.current_feed)).width(iced::Fill).height(iced::Fill), .width(iced::Fill)
) .height(iced::Fill),
) ))
.height(Fill) .height(Fill)
.width(Fill) .width(Fill)
.into() .into()
} }
fn item_view(state: &State) -> Element<'_, Message> { fn item_view(state: &State) -> Element<'_, Message> {
let item = db::get_item(state.current_item); container(column!(
container( row!(
column!( button(text("Home")).on_press(Message::ChangePage(Page::Home)),
row!( button(text("Feed")).on_press(Message::ChangePage(Page::FeedView))
button(text("Home")).on_press(Message::ChangePage(Page::Home)),
button(text("Feed")).on_press(Message::ChangePage(Page::FeedView))
).spacing(10),
content_view(item),
) )
) .spacing(10),
.height(Fill) content_view(state),
.width(Fill) ))
.into() .height(Fill)
.width(Fill)
.into()
} }
fn item_list(_: &State) -> Element<'_,Message>{ fn item_list(_: &State) -> Element<'_, Message> {
todo!() todo!()
} }
fn testing(state: &State) -> Element<'_, Message> { fn testing(state: &State) -> Element<'_, Message> {
column!( column!(
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!( row!(
text_input("Add a feed",&state.feed_input).on_input(|val| Message::FieldUpdated(AppField::FeedInput,val)).width(300), text_input("Add a feed", &state.feed_input)
.on_input(|val| Message::FieldUpdated(AppField::FeedInput, val))
.width(300),
button("Add feed!").on_press(Message::AddFeed(state.feed_input.clone())) button("Add feed!").on_press(Message::AddFeed(state.feed_input.clone()))
).spacing(5).padding(10), )
.spacing(5)
.padding(10),
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

@ -1,8 +1,9 @@
use crate::db::FeedItem;
use super::db; use super::db;
use super::ui; use super::ui;
use iced::widget::markdown;
use iced::widget::scrollable; use iced::widget::scrollable;
use iced::widget::Column;
use iced::Theme;
use rss_content::Content; use rss_content::Content;
use ui::Message; use ui::Message;
use rss_content; use rss_content;
@ -40,44 +41,52 @@ pub fn list_items(feed_id: usize) -> iced::widget::Column<'static,Message> {
.padding(15) .padding(15)
} }
pub fn content_area(cnt: String) -> iced::widget::Container<'static,Message>{ pub fn content_area(content: &Vec<Content>) -> iced::widget::Container<Message> {
let content = rss_content::parse_content(&cnt); let mut children: Vec<Element<Message>> = Vec::new();
container( for c in content {
column( match c {
content.into_iter().map(|c: Content|{ Content::MarkdownParsed(p) => {
match c { children.push(
Content::Markdown(md) => { markdown(p, Theme::Dark).map(Message::LinkClicked)
text(md) );
}, }
Content::Image(_) => {text("Image goes here")}, Content::Image(_) => {children.push(text("<Image goes here>")
Content::Audio(_) => {text("Audio widget here")}, .size(32)
Content::Video(_) => {text("video player here")}, .into());},
_ => {text("")} Content::Audio(_) => {children.push(text("<Audio widget here>")
.size(32)
.into());},
Content::Video(_) => {children.push(text("<Video goes here>")
.size(32)
.into());},
_ => {}
} }
}).map(Element::from) }
) container(
Column::with_children(children)
) )
} }
pub fn content_view(item: FeedItem) -> iced::widget::Scrollable<'static, Message> { pub fn content_view(state: &super::ui::State) -> iced::widget::Scrollable<Message> {
let item = state.current_item.clone().unwrap();
scrollable( scrollable(
column!( column!(
text(item.title).size(34), text(item.title).size(34),
match item.description { match item.description {
Some(d) => { Some(_d) => {
content_area(d) content_area(&state.item_description)
}, },
None => {container(text("No description found"))} None => {container(text("No description found"))}
}, },
match item.content { match item.content {
Some(c) => { Some(_c) => {
content_area(c) content_area(&state.item_content)
} }
None => {container(text("No content found"))} None => {container(text("No content found"))}
} }
) )
) ).width(iced::Fill)
} }