Add last/next crawl times to feeds, improve local time rendering
This commit is contained in:
@@ -11,6 +11,7 @@ use crate::error::Result;
|
||||
use crate::htmx::HXTarget;
|
||||
use crate::models::entry::Entry;
|
||||
use crate::partials::layout::Layout;
|
||||
use crate::partials::time::date_time;
|
||||
use crate::uuid::Base62Uuid;
|
||||
|
||||
pub async fn get(
|
||||
@@ -24,9 +25,6 @@ pub async fn get(
|
||||
let content_dir = std::path::Path::new(&config.content_dir);
|
||||
let content_path = content_dir.join(format!("{}.html", entry.entry_id));
|
||||
let title = entry.title.unwrap_or_else(|| "Untitled Entry".to_string());
|
||||
let published_at = entry
|
||||
.published_at
|
||||
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
|
||||
let content = fs::read_to_string(content_path).unwrap_or_else(|_| "No content".to_string());
|
||||
Ok(layout
|
||||
.with_subtitle(&title)
|
||||
@@ -41,9 +39,7 @@ pub async fn get(
|
||||
div {
|
||||
span class="text-sm text-gray-600" {
|
||||
strong { "Published: " }
|
||||
time datetime=(published_at) class="local-time" {
|
||||
(published_at)
|
||||
}
|
||||
(date_time(entry.published_at))
|
||||
}
|
||||
}
|
||||
(PreEscaped(content))
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::{collections::HashMap, net::SocketAddr, path::Path, sync::Arc};
|
||||
use anyhow::Result;
|
||||
use axum::{
|
||||
error_handling::HandleErrorLayer,
|
||||
response::Response,
|
||||
routing::{get, post},
|
||||
BoxError, Router,
|
||||
};
|
||||
|
||||
@@ -127,6 +127,12 @@ pub struct GetFeedsOptions {
|
||||
}
|
||||
|
||||
impl Feed {
|
||||
pub fn next_crawl_time(&self) -> Option<DateTime<Utc>> {
|
||||
self.last_crawled_at.map(|last_crawled_at| {
|
||||
last_crawled_at + chrono::Duration::minutes(self.crawl_interval_minutes as i64)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get(db: impl Executor<'_, Database = Postgres>, feed_id: Uuid) -> Result<Feed> {
|
||||
sqlx::query_as!(
|
||||
Feed,
|
||||
|
||||
@@ -2,12 +2,14 @@ use maud::{html, Markup};
|
||||
|
||||
use crate::models::feed::Feed;
|
||||
use crate::partials::link::{link, LinkProps};
|
||||
use crate::partials::time::relative_time;
|
||||
use crate::uuid::Base62Uuid;
|
||||
|
||||
pub struct FeedLink<'a> {
|
||||
pub feed: &'a Feed,
|
||||
pub pending_crawl: bool,
|
||||
pub reset_htmx_target: bool,
|
||||
pub show_next_crawl_time: bool,
|
||||
}
|
||||
|
||||
impl FeedLink<'_> {
|
||||
@@ -16,6 +18,7 @@ impl FeedLink<'_> {
|
||||
feed,
|
||||
pending_crawl: false,
|
||||
reset_htmx_target: false,
|
||||
show_next_crawl_time: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +32,11 @@ impl FeedLink<'_> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn show_next_crawl_time(&mut self) -> &mut Self {
|
||||
self.show_next_crawl_time = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn render(&self) -> Markup {
|
||||
let title = self.feed.title.clone().unwrap_or_else(|| {
|
||||
if self.pending_crawl {
|
||||
@@ -39,7 +47,21 @@ impl FeedLink<'_> {
|
||||
});
|
||||
let feed_url = format!("/feed/{}", Base62Uuid::from(self.feed.feed_id));
|
||||
html! {
|
||||
(link(LinkProps { destination: &feed_url, title: &title, reset_htmx_target: self.reset_htmx_target }))
|
||||
div class="flex flex-row gap-4 items-baseline" {
|
||||
(link(LinkProps { destination: &feed_url, title: &title, reset_htmx_target: self.reset_htmx_target }))
|
||||
@if let Some(last_crawl) = self.feed.last_crawled_at {
|
||||
span class="text-sm text-gray-600" {
|
||||
span class="font-semibold" { "last crawl: " }
|
||||
(relative_time(last_crawl))
|
||||
}
|
||||
}
|
||||
@if let Some(next_crawl) = self.feed.next_crawl_time() {
|
||||
span class="text-sm text-gray-600" {
|
||||
span class="font-semibold" { "next crawl: " }
|
||||
(relative_time(next_crawl))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,5 @@ pub mod login_form;
|
||||
pub mod opml_import_form;
|
||||
pub mod register_form;
|
||||
pub mod reset_password_form;
|
||||
pub mod time;
|
||||
pub mod user_name;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use maud::{html, Markup, PreEscaped};
|
||||
use maud::{html, Markup};
|
||||
|
||||
pub fn opml_import_form() -> Markup {
|
||||
html! {
|
||||
|
||||
59
src/partials/time.rs
Normal file
59
src/partials/time.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use std::fmt;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use maud::{html, Markup};
|
||||
|
||||
use crate::utils::FormattedUtcTimestamp;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LocalTimeType {
|
||||
Date,
|
||||
Relative,
|
||||
}
|
||||
|
||||
impl fmt::Display for LocalTimeType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
LocalTimeType::Date => write!(f, "date"),
|
||||
LocalTimeType::Relative => write!(f, "relative"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TimeProps {
|
||||
pub timestamp: DateTime<Utc>,
|
||||
pub local_time_type: Option<LocalTimeType>,
|
||||
}
|
||||
|
||||
pub fn time(
|
||||
TimeProps {
|
||||
timestamp,
|
||||
local_time_type,
|
||||
}: TimeProps,
|
||||
) -> Markup {
|
||||
let time = FormattedUtcTimestamp::from(timestamp);
|
||||
html! {
|
||||
time
|
||||
datetime=(time.rfc3339)
|
||||
data-local-time=(local_time_type.unwrap_or(LocalTimeType::Date))
|
||||
title=(time.human_readable)
|
||||
{
|
||||
(time.rfc3339)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn date_time(timestamp: DateTime<Utc>) -> Markup {
|
||||
time(TimeProps {
|
||||
timestamp,
|
||||
local_time_type: Some(LocalTimeType::Date),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn relative_time(timestamp: DateTime<Utc>) -> Markup {
|
||||
time(TimeProps {
|
||||
timestamp,
|
||||
local_time_type: Some(LocalTimeType::Relative),
|
||||
})
|
||||
}
|
||||
15
src/utils.rs
15
src/utils.rs
@@ -1,3 +1,4 @@
|
||||
use chrono::{DateTime, SecondsFormat, Utc};
|
||||
use url::Url;
|
||||
|
||||
pub fn get_domain(url: &str) -> Option<String> {
|
||||
@@ -12,3 +13,17 @@ pub fn get_domain(url: &str) -> Option<String> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub struct FormattedUtcTimestamp {
|
||||
pub rfc3339: String,
|
||||
pub human_readable: String,
|
||||
}
|
||||
|
||||
impl From<DateTime<Utc>> for FormattedUtcTimestamp {
|
||||
fn from(dt: DateTime<Utc>) -> Self {
|
||||
FormattedUtcTimestamp {
|
||||
rfc3339: dt.to_rfc3339_opts(SecondsFormat::Millis, true),
|
||||
human_readable: dt.format("%Y-%m-%d %H:%M:%S %Z").to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user