From cef93744163bf1e0d4bd5629b244c75530878a35 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Mon, 29 Jun 2026 11:45:51 +0200 Subject: [PATCH] fix: show date in timestamps; fix history divider appearing immediately - MessagePane: show date in timestamps (Yesterday/MMM D) for messages not from today; divider now appears at the top of history on load rather than waiting for a live message to create the boundary - TUI: same date-aware timestamp formatting (Yesterday / Jan 2) Co-Authored-By: Claude Sonnet 4.6 --- cmd/tui/main.go | 13 +++++++++- web/src/components/MessagePane.tsx | 38 +++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/cmd/tui/main.go b/cmd/tui/main.go index 83eaf0a..69d609d 100644 --- a/cmd/tui/main.go +++ b/cmd/tui/main.go @@ -383,7 +383,7 @@ func (m model) refreshViewport() model { w := m.vpContentWidth() var sb strings.Builder for _, e := range m.messages[room] { - ts := styleMsgTime.Render(e.at.Format("15:04")) + ts := styleMsgTime.Render(formatMsgTime(e.at)) var from string if e.fromMe { from = styleMsgMe.Render(e.from) @@ -614,6 +614,17 @@ func filterIDs(ids []proto.PeerID, remove proto.PeerID) []proto.PeerID { return out } +func formatMsgTime(t time.Time) string { + now := time.Now() + if t.Year() == now.Year() && t.YearDay() == now.YearDay() { + return t.Format("15:04") + } + if t.Year() == now.Year() && t.YearDay() == now.YearDay()-1 { + return "Yesterday " + t.Format("15:04") + } + return t.Format("Jan 2 15:04") +} + func min(a, b int) int { if a < b { return a diff --git a/web/src/components/MessagePane.tsx b/web/src/components/MessagePane.tsx index 7cd56a4..b845887 100644 --- a/web/src/components/MessagePane.tsx +++ b/web/src/components/MessagePane.tsx @@ -1,6 +1,18 @@ import { useEffect, useRef, useState } from 'react' import { useWaste } from '../store' +const today = new Date() +today.setHours(0, 0, 0, 0) +const todayMs = today.getTime() +const yesterdayMs = todayMs - 86400000 + +function formatTs(ts: number): string { + const time = new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }) + if (ts >= todayMs) return time + if (ts >= yesterdayMs) return `Yesterday ${time}` + return new Date(ts).toLocaleDateString([], { month: 'short', day: 'numeric' }) + ' ' + time +} + export function MessagePane() { const { messages, historyCutoff, activeRoom, activeNetworkId, localPeer, connectedPeers, send } = useWaste() const [draft, setDraft] = useState('') @@ -8,6 +20,16 @@ export function MessagePane() { const roomMessages = messages[activeRoom] ?? [] const cutoff = historyCutoff[activeRoom] ?? 0 + // Find the index of the first live message (ts > cutoff). + // The divider appears just before this index, or at the top if all are history. + const firstLiveIdx = cutoff > 0 + ? roomMessages.findIndex(m => m.ts > cutoff) + : -1 + // If all messages are history (no live yet), put divider at the start. + const dividerIdx = cutoff > 0 + ? (firstLiveIdx === -1 ? 0 : firstLiveIdx) + : -1 + useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [roomMessages.length]) @@ -43,20 +65,20 @@ export function MessagePane() {
{roomLabel}
+ {dividerIdx === 0 && ( +
earlier messages
+ )} {roomMessages.map((msg, i) => { const mine = msg.from === localPeer?.id - const alias = aliasFor(msg.from) - const time = new Date(msg.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }) - const showDivider = cutoff > 0 && i > 0 && roomMessages[i - 1].ts <= cutoff && msg.ts > cutoff + const alias = aliasFor(String(msg.from)) + const ts = formatTs(msg.ts) return (
- {showDivider && ( -
- earlier messages -
+ {i === dividerIdx && dividerIdx > 0 && ( +
earlier messages
)}
- {time} + {ts} {alias} {msg.text}