Add last/next crawl times to feeds, improve local time rendering

This commit is contained in:
2024-02-01 00:13:09 -05:00
parent 457aafbfe3
commit 2fab68241e
9 changed files with 174 additions and 19 deletions

View File

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

View File

@@ -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,
};

View File

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

View File

@@ -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))
}
}
}
}
}
}

View File

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

View File

@@ -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
View 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),
})
}

View File

@@ -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(),
}
}
}