Improve feed list and add feed page

This commit is contained in:
Tyler Hallada 2023-07-07 16:03:12 -04:00
parent 3f028c3088
commit f69d0f2752
8 changed files with 88 additions and 15 deletions

23
src/handlers/feed.rs Normal file
View File

@ -0,0 +1,23 @@
use axum::extract::{Path, State};
use axum::response::Response;
use maud::html;
use sqlx::PgPool;
use crate::error::Result;
use crate::models::entry::get_entries_for_feed;
use crate::models::feed::get_feed;
use crate::partials::{entry_list::entry_list, layout::Layout};
use crate::uuid::Base62Uuid;
pub async fn get(
Path(id): Path<Base62Uuid>,
State(pool): State<PgPool>,
layout: Layout,
) -> Result<Response> {
let feed = get_feed(&pool, id.as_uuid()).await?;
let entries = get_entries_for_feed(&pool, feed.feed_id, Default::default()).await?;
Ok(layout.render(html! {
h1 { (feed.title.unwrap_or_else(|| "Untitled Feed".to_string())) }
(entry_list(entries))
}))
}

View File

@ -6,6 +6,7 @@ use sqlx::PgPool;
use crate::error::Result; use crate::error::Result;
use crate::models::feed::get_feeds; use crate::models::feed::get_feeds;
use crate::partials::layout::Layout; use crate::partials::layout::Layout;
use crate::uuid::Base62Uuid;
pub async fn get(State(pool): State<PgPool>, layout: Layout) -> Result<Response> { pub async fn get(State(pool): State<PgPool>, layout: Layout) -> Result<Response> {
let feeds = get_feeds(&pool).await?; let feeds = get_feeds(&pool).await?;
@ -13,7 +14,8 @@ pub async fn get(State(pool): State<PgPool>, layout: Layout) -> Result<Response>
ul { ul {
@for feed in feeds { @for feed in feeds {
@let title = feed.title.unwrap_or_else(|| "Untitled Feed".to_string()); @let title = feed.title.unwrap_or_else(|| "Untitled Feed".to_string());
li { (title) } @let feed_url = format!("/feed/{}", Base62Uuid::from(feed.feed_id));
li { a href=(feed_url) { (title) } }
} }
} }
})) }))

View File

@ -1,24 +1,12 @@
use axum::extract::State; use axum::extract::State;
use axum::response::Response; use axum::response::Response;
use maud::html;
use sqlx::PgPool; use sqlx::PgPool;
use crate::error::Result; use crate::error::Result;
use crate::models::entry::{get_entries, GetEntriesOptions}; use crate::models::entry::{get_entries, GetEntriesOptions};
use crate::partials::layout::Layout; use crate::partials::{layout::Layout, entry_list::entry_list};
use crate::utils::get_domain;
use crate::uuid::Base62Uuid;
pub async fn get(State(pool): State<PgPool>, layout: Layout) -> Result<Response> { pub async fn get(State(pool): State<PgPool>, layout: Layout) -> Result<Response> {
let entries = get_entries(&pool, GetEntriesOptions::default()).await?; let entries = get_entries(&pool, GetEntriesOptions::default()).await?;
Ok(layout.render(html! { Ok(layout.render(entry_list(entries)))
ul class="entries" {
@for entry in entries {
@let title = entry.title.unwrap_or_else(|| "Untitled".to_string());
@let url = format!("/entry/{}", Base62Uuid::from(entry.entry_id));
@let domain = get_domain(&entry.url).unwrap_or_default();
li { a href=(url) { (title) } em class="domain" { (domain) }}
}
}
}))
} }

View File

@ -1,5 +1,6 @@
pub mod api; pub mod api;
pub mod entry; pub mod entry;
pub mod home; pub mod home;
pub mod feed;
pub mod feeds; pub mod feeds;
pub mod log; pub mod log;

View File

@ -55,6 +55,7 @@ async fn main() -> Result<()> {
.route("/api/v1/entry/:id", get(handlers::api::entry::get)) .route("/api/v1/entry/:id", get(handlers::api::entry::get))
.route("/", get(handlers::home::get)) .route("/", get(handlers::home::get))
.route("/feeds", get(handlers::feeds::get)) .route("/feeds", get(handlers::feeds::get))
.route("/feed/:id", get(handlers::feed::get))
.route("/entry/:id", get(handlers::entry::get)) .route("/entry/:id", get(handlers::entry::get))
.route("/log", get(handlers::log::get)) .route("/log", get(handlers::log::get))
.route("/log/stream", get(handlers::log::stream)) .route("/log/stream", get(handlers::log::stream))

View File

@ -85,6 +85,45 @@ pub async fn get_entries(
} }
} }
pub async fn get_entries_for_feed(
pool: &PgPool,
feed_id: Uuid,
options: GetEntriesOptions,
) -> sqlx::Result<Vec<Entry>> {
if let Some(published_before) = options.published_before {
sqlx::query_as!(
Entry,
"select * from entry
where deleted_at is null
and feed_id = $1
and published_at < $2
order by published_at desc
limit $3
",
feed_id,
published_before,
options.limit.unwrap_or(DEFAULT_ENTRIES_PAGE_SIZE)
)
.fetch_all(pool)
.await
} else {
sqlx::query_as!(
Entry,
"select * from entry
where deleted_at is null
and feed_id = $1
order by published_at desc
limit $2
",
feed_id,
options.limit.unwrap_or(DEFAULT_ENTRIES_PAGE_SIZE)
)
.fetch_all(pool)
.await
}
}
pub async fn create_entry(pool: &PgPool, payload: CreateEntry) -> Result<Entry> { pub async fn create_entry(pool: &PgPool, payload: CreateEntry) -> Result<Entry> {
payload.validate()?; payload.validate()?;
sqlx::query_as!( sqlx::query_as!(

View File

@ -0,0 +1,18 @@
use maud::{html, Markup};
use crate::models::entry::Entry;
use crate::utils::get_domain;
use crate::uuid::Base62Uuid;
pub fn entry_list(entries: Vec<Entry>) -> Markup {
html! {
ul class="entries" {
@for entry in entries {
@let title = entry.title.unwrap_or_else(|| "Untitled".to_string());
@let url = format!("/entry/{}", Base62Uuid::from(entry.entry_id));
@let domain = get_domain(&entry.url).unwrap_or_default();
li { a href=(url) { (title) } em class="domain" { (domain) }}
}
}
}
}

View File

@ -1,2 +1,3 @@
pub mod entry_list;
pub mod header; pub mod header;
pub mod layout; pub mod layout;