From d2acdf07dba9c6a8e05681137945515fba441368 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:01:56 +0200 Subject: [PATCH] Comments: browser-created comments and replies get a creation timestamp (#72) --- Cargo.lock | 1 + crates/engine/Cargo.toml | 4 ++++ crates/engine/src/cmd/review.rs | 38 +++++++++++++++++++++++++++------ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e50d814e..f31cb6c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,6 +951,7 @@ dependencies = [ "deckcraft-text", "i_overlay", "image", + "js-sys", "log", "serde", "serde_json", diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 1f4dbfcd..14071145 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -25,3 +25,7 @@ i_overlay = { workspace = true } thiserror = { workspace = true } log = { workspace = true } image = { workspace = true } + +# The browser clock for comment timestamps (wasm has no std::time::SystemTime). +[target.'cfg(target_arch = "wasm32")'.dependencies] +js-sys = "0.3" diff --git a/crates/engine/src/cmd/review.rs b/crates/engine/src/cmd/review.rs index 8e3a532c..856aa128 100644 --- a/crates/engine/src/cmd/review.rs +++ b/crates/engine/src/cmd/review.rs @@ -43,20 +43,29 @@ pub fn specs() -> Vec { ] } -fn now() -> String { +/// Seconds since the Unix epoch: the system clock, or the browser's on wasm (which has no `SystemTime`). +fn now_secs() -> u64 { #[cfg(not(target_arch = "wasm32"))] { - let secs = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0); - let (y, m, d) = super::design::civil((secs / 86_400) as i64); - let t = secs % 86_400; - format!("{y:04}-{m:02}-{d:02}T{:02}:{:02}:{:02}Z", t / 3600, t / 60 % 60, t % 60) + std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0) } #[cfg(target_arch = "wasm32")] { - String::new() + (js_sys::Date::now() / 1000.0).max(0.0) as u64 } } +/// A UTC timestamp as saved on comments: `2026-10-10T13:10:35Z`. +fn iso_utc(secs: u64) -> String { + let (y, m, d) = super::design::civil((secs / 86_400) as i64); + let t = secs % 86_400; + format!("{y:04}-{m:02}-{d:02}T{:02}:{:02}:{:02}Z", t / 3600, t / 60 % 60, t % 60) +} + +fn now() -> String { + iso_utc(now_secs()) +} + fn add(s: &mut Session, p: &Value) -> Result { let text = str_param(p, "text").ok_or_else(|| bad("comment.add", "missing `text`"))?.to_string(); let author = s.prefs.author.clone(); @@ -264,3 +273,20 @@ fn custom_show(s: &mut Session, p: &Value) -> Result { })?; ok() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn timestamps_are_iso_utc() { + assert_eq!(iso_utc(0), "1970-01-01T00:00:00Z"); + assert_eq!(iso_utc(1_791_637_835), "2026-10-10T13:10:35Z"); + } + + #[test] + fn the_clock_is_not_empty() { + // On wasm this used to return an empty string, so browser comments were saved undated. + assert!(now().starts_with("20"), "{}", now()); + } +}