Fix: prevent published date reset when editing videos and show edited status

Prioritize `published_at` metadata over event `created_at` timestamp.
Backfill `published_at` during edit if missing to preserve original date.
Add "(edited)" label to video cards for modified content.
Display "Last Edited: [Date]" on video player modal.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-12 15:35:23 +00:00
1 parent 1ef8459fd6
commit 1d3c827f62
4 files changed
+112 -6

No files matched your search

+18 -1
View File
@@ -76,6 +76,7 @@ import {
import { splitAndZap as splitAndZapDefault } from "./payments/zapSplit.js";
import {
formatAbsoluteTimestamp as formatAbsoluteTimestampUtil,
formatAbsoluteDateWithOrdinal as formatAbsoluteDateWithOrdinalUtil,
formatTimeAgo as formatTimeAgoUtil,
truncateMiddle,
formatShortNpub,
@@ -8761,7 +8762,9 @@ class Application {
(normalizedPostedAt === null || normalizedEditedAt - normalizedPostedAt >= 60);
if (shouldShowEdited) {
payload.edited = `Last edited ${this.formatTimeAgo(normalizedEditedAt)}`;
payload.edited = `Last Edited: ${formatAbsoluteDateWithOrdinalUtil(
normalizedEditedAt
)}`;
}
return payload;
@@ -8846,6 +8849,20 @@ class Application {
return null;
}
// Prioritize NIP-71 published_at metadata if available
const nip71PublishedAt = Number.isFinite(video?.nip71?.publishedAt)
? Math.floor(video.nip71.publishedAt)
: Number.isFinite(video?.nip71?.published_at)
? Math.floor(video.nip71.published_at)
: Number.isFinite(video?.nip71?.["published-at"])
? Math.floor(video.nip71["published-at"])
: null;
if (nip71PublishedAt !== null) {
this.cacheVideoRootCreatedAt(video, nip71PublishedAt);
return nip71PublishedAt;
}
const cached = this.getKnownVideoPostedAt(video);
if (cached !== null) {
return cached;
+48 -5
View File
@@ -438,9 +438,38 @@ export class EditModal {
this.applyVideoToForm(editContext);
this.nip71FormManager.resetSection(this.nip71SectionKey);
if (video.nip71 && typeof video.nip71 === "object") {
this.nip71FormManager.hydrateSection(this.nip71SectionKey, video.nip71);
let nip71Data = video.nip71 && typeof video.nip71 === "object" ? { ...video.nip71 } : {};
// Backfill published_at if missing, using the original root creation time
if (
!nip71Data.publishedAt &&
!nip71Data.published_at &&
!nip71Data["published-at"]
) {
const rootCreated = video.rootCreatedAt || video.created_at;
if (rootCreated) {
nip71Data.publishedAt = rootCreated;
}
}
// Format publishedAt for datetime-local input (YYYY-MM-DDThh:mm)
const rawPublishedAt =
nip71Data.publishedAt || nip71Data.published_at || nip71Data["published-at"];
if (rawPublishedAt) {
const numeric = Number(rawPublishedAt);
if (Number.isFinite(numeric)) {
const dt = new Date(numeric * 1000);
const pad = (n) => String(n).padStart(2, "0");
const formatted = `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(
dt.getDate()
)}T${pad(dt.getHours())}:${pad(dt.getMinutes())}`;
nip71Data.publishedAt = formatted;
}
}
this.nip71FormManager.hydrateSection(this.nip71SectionKey, nip71Data);
const initialNip71 = this.nip71FormManager.collectSection(this.nip71SectionKey);
this.originalNip71Metadata = this.cloneNip71Metadata(initialNip71);
this.originalNip71MetadataJson = JSON.stringify(this.originalNip71Metadata);
@@ -926,9 +955,23 @@ export class EditModal {
const originalIsNsfw = original.isNsfw === true;
const originalIsForKids = original.isForKids === true;
const currentNip71 = this.cloneNip71Metadata(
this.nip71FormManager.collectSection(this.nip71SectionKey)
);
const rawNip71 = this.nip71FormManager.collectSection(this.nip71SectionKey);
const currentNip71 = this.cloneNip71Metadata(rawNip71);
// Convert publishedAt back to Unix timestamp (seconds)
if (currentNip71 && typeof currentNip71.publishedAt === "string") {
const dateStr = currentNip71.publishedAt;
if (dateStr) {
const dt = new Date(dateStr);
if (!isNaN(dt.getTime())) {
currentNip71.publishedAt = Math.floor(dt.getTime() / 1000);
} else {
delete currentNip71.publishedAt;
}
} else {
delete currentNip71.publishedAt;
}
}
const hasImetaSource = Array.isArray(currentNip71?.imeta)
? currentNip71.imeta.some((variant) => {
if (!variant || typeof variant !== "object") {
+12
View File
@@ -951,6 +951,18 @@ export class VideoCard {
this.viewCountEl = view;
}
if (
this.postedAt !== null &&
Number.isFinite(this.video?.created_at) &&
this.video.created_at > this.postedAt + 60
) {
const edited = this.createElement("span", {
classNames: ["ml-1"],
textContent: "(edited)",
});
metadata.appendChild(edited);
}
authorMeta.appendChild(authorName);
authorMeta.appendChild(metadata);
+34
View File
@@ -90,3 +90,37 @@ export function formatTimeAgo(timestamp) {
}
return "just now";
}
/**
* Formats a timestamp (in seconds) to a long absolute date with ordinal day
* (e.g. "January 6th, 2026").
*/
export function formatAbsoluteDateWithOrdinal(timestamp) {
if (!Number.isFinite(timestamp)) {
return "";
}
const date = new Date(timestamp * 1000);
if (Number.isNaN(date.getTime())) {
return "";
}
// Helper for English ordinal suffixes
const getOrdinal = (n) => {
const s = ["th", "st", "nd", "rd"];
const v = n % 100;
return s[(v - 20) % 10] || s[v] || s[0];
};
try {
const month = date.toLocaleString("en-US", { month: "long" });
const day = date.getDate();
const year = date.getFullYear();
const suffix = getOrdinal(day);
return `${month} ${day}${suffix}, ${year}`;
} catch (err) {
// Fallback if locale fails
return date.toDateString();
}
}