fetch newest quote for dashboard, helpers
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use axum::{http::StatusCode, response::IntoResponse};
|
use axum::{http::StatusCode, response::IntoResponse};
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset, Utc};
|
||||||
use rusqlite::{Connection, OptionalExtension};
|
use rusqlite::{Connection, OptionalExtension};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -34,6 +34,15 @@ pub enum QuoteError {
|
|||||||
DatabaseError(#[from] DatabaseError),
|
DatabaseError(#[from] DatabaseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Quote {
|
||||||
|
pub fn get_creation_timestamp(&self) -> DateTime<Utc> {
|
||||||
|
// unwrap here because all IDs use UUIDv7
|
||||||
|
let (s, n) = self.id.get_timestamp().unwrap().to_unix();
|
||||||
|
// unwrap here because timestamps held by UUIDs are valid by spec
|
||||||
|
DateTime::from_timestamp(s as i64, n).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Quote {
|
impl Quote {
|
||||||
pub fn total_count(conn: &Connection) -> Result<i64, QuoteError> {
|
pub fn total_count(conn: &Connection) -> Result<i64, QuoteError> {
|
||||||
Ok(conn.query_row("SELECT COUNT(*) FROM quotes", (), |r| r.get(0))?)
|
Ok(conn.query_row("SELECT COUNT(*) FROM quotes", (), |r| r.get(0))?)
|
||||||
@@ -92,6 +101,18 @@ impl Quote {
|
|||||||
public,
|
public,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn get_newest(conn: &Connection) -> Result<Option<Quote>, QuoteError> {
|
||||||
|
let id: Option<Uuid> = conn
|
||||||
|
.query_row("SELECT id FROM quotes ORDER BY id DESC LIMIT 1", (), |r| {
|
||||||
|
r.get(0)
|
||||||
|
})
|
||||||
|
.optional()?;
|
||||||
|
|
||||||
|
match id {
|
||||||
|
Some(id) => Ok(Some(Self::get_by_id(conn, id)?)),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn create(
|
pub fn create(
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
lines: Vec<(String, Name)>,
|
lines: Vec<(String, Name)>,
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ const LINKS: &[(&str, &str, &str)] = &[
|
|||||||
|
|
||||||
pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
||||||
let u = User::authenticate(req.headers()).ok().flatten();
|
let u = User::authenticate(req.headers()).ok().flatten();
|
||||||
let mut conn = database::conn()?;
|
let conn = database::conn()?;
|
||||||
let tx = conn.transaction()?;
|
|
||||||
|
let newest_quote = Quote::get_newest(&conn)?;
|
||||||
|
|
||||||
Ok(base(
|
Ok(base(
|
||||||
"Dashboard | Mnemosyne",
|
"Dashboard | Mnemosyne",
|
||||||
@@ -35,8 +36,15 @@ pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
|||||||
div class="mx-auto max-w-4xl mt-4 grid grid-cols-1 sm:grid-cols-2 gap-4" {
|
div class="mx-auto max-w-4xl mt-4 grid grid-cols-1 sm:grid-cols-2 gap-4" {
|
||||||
div class="flex flex-col" {
|
div class="flex flex-col" {
|
||||||
p {"Newest Quote"}
|
p {"Newest Quote"}
|
||||||
p class="text-neutral-500 font-light mb-4" {"This just in! This quote was added 15s ago."}
|
@if let Some(q) = newest_quote {
|
||||||
div class="flex-1 [&>div]:h-full" {(quote(&sample_quote_1()))}
|
p class="text-neutral-500 font-light mb-4" {
|
||||||
|
"This just in! This quote was added "
|
||||||
|
(format_time_ago(q.get_creation_timestamp())) " ago."
|
||||||
|
}
|
||||||
|
div class="flex-1 [&>div]:h-full" {(quote(&q))}
|
||||||
|
} @else {
|
||||||
|
p class="text-neutral-500 font-light mb-4" {"No quotes yet."}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
div class="flex flex-col" {
|
div class="flex flex-col" {
|
||||||
p {"Quote of the Day"}
|
p {"Quote of the Day"}
|
||||||
@@ -60,25 +68,25 @@ pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
|||||||
}
|
}
|
||||||
div class="mx-auto max-w-4xl mt-4 flex flex-row gap-2" {
|
div class="mx-auto max-w-4xl mt-4 flex flex-row gap-2" {
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match Quote::total_count(&tx) {
|
@match Quote::total_count(&conn) {
|
||||||
Ok(count) => {(count) " QUOTES TOTAL"},
|
Ok(count) => {(count) " QUOTES TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"QUOTE COUNT ERR"},
|
Err(_) => span class="text-red-400" {"QUOTE COUNT ERR"},
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match Person::total_count(&tx) {
|
@match Person::total_count(&conn) {
|
||||||
Ok(count) => {(count) " PERSONS TOTAL"},
|
Ok(count) => {(count) " PERSONS TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"PERSON COUNT ERR"},
|
Err(_) => span class="text-red-400" {"PERSON COUNT ERR"},
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match Tag::total_count(&tx) {
|
@match Tag::total_count(&conn) {
|
||||||
Ok(count) => {(count) " TAGS TOTAL"},
|
Ok(count) => {(count) " TAGS TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"TAG COUNT ERR"}
|
Err(_) => span class="text-red-400" {"TAG COUNT ERR"}
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match User::total_count(&tx) {
|
@match User::total_count(&conn) {
|
||||||
Ok(count) => {(count) " USERS TOTAL"},
|
Ok(count) => {(count) " USERS TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"USER COUNT ERR"}
|
Err(_) => span class="text-red-400" {"USER COUNT ERR"}
|
||||||
}
|
}
|
||||||
@@ -90,41 +98,6 @@ pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_quote_1() -> Quote {
|
|
||||||
Quote {
|
|
||||||
id: Uuid::now_v7(),
|
|
||||||
public: true,
|
|
||||||
location: Some(String::from("Poznań")),
|
|
||||||
context: Some(String::from("Wykład z językoznawstwa")),
|
|
||||||
created_by: Uuid::max(),
|
|
||||||
timestamp: DateTime::from(Utc::now()),
|
|
||||||
lines: vec![
|
|
||||||
QuoteLine {
|
|
||||||
id: Uuid::now_v7(),
|
|
||||||
content: String::from("Nie wiem, czy są tutaj osoby fanowskie zipline-ów?"),
|
|
||||||
attribution: Name {
|
|
||||||
id: Uuid::nil(),
|
|
||||||
created_by: Uuid::max(),
|
|
||||||
person_id: Uuid::now_v7(),
|
|
||||||
is_primary: true,
|
|
||||||
name: String::from("dr. Barbara Konat"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
QuoteLine {
|
|
||||||
id: Uuid::now_v7(),
|
|
||||||
content: String::from("Taka uprząż co robi pziuuum!"),
|
|
||||||
attribution: Name {
|
|
||||||
id: Uuid::nil(),
|
|
||||||
created_by: Uuid::max(),
|
|
||||||
person_id: Uuid::now_v7(),
|
|
||||||
is_primary: true,
|
|
||||||
name: String::from("dr. Barbara Konat"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_quote_2() -> Quote {
|
fn sample_quote_2() -> Quote {
|
||||||
Quote {
|
Quote {
|
||||||
id: Uuid::now_v7(),
|
id: Uuid::now_v7(),
|
||||||
@@ -159,3 +132,13 @@ fn sample_quote_2() -> Quote {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_time_ago(dt: DateTime<Utc>) -> String {
|
||||||
|
let secs = Utc::now().signed_duration_since(dt).num_seconds();
|
||||||
|
match secs {
|
||||||
|
..60 => format!("{}s", secs),
|
||||||
|
60..3600 => format!("{}m", secs / 60),
|
||||||
|
3600..86400 => format!("{}h", secs / 3600),
|
||||||
|
_ => format!("{}d", secs / 86400),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user