Skip to content

fix(MessageBubble): clamp future-dated timestamps to now#66

Closed
torlando-agent[bot] wants to merge 1 commit into
mainfrom
columba-suite/issue-60-message-timestamp-clamp
Closed

fix(MessageBubble): clamp future-dated timestamps to now#66
torlando-agent[bot] wants to merge 1 commit into
mainfrom
columba-suite/issue-60-message-timestamp-clamp

Conversation

@torlando-agent

@torlando-agent torlando-agent Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Implements the iOS half of the approved plan for #60.

Summary

LXMF wire format puts a single timestamp on each message: the sender's time.time() at pack time (set in LXMessage.swift:171-179, matching RNS/LXMF/LXMessage.py:362). Columba-iOS treats that field as the canonical timestamp for both display and ordering — see MessageBubble.swift:329, 384 for display, and LXMFDatabase.swift:447, 213 for the SQL ORDER BY timestamp DESC that drives the chat ScrollView. Result: any clock skew between two devices (or any clock drift on the local device) leaks straight into the UI as out-of-order messages and "in 5 min" relative-time strings. Both observed symptoms collapse to that one cause.

The fix has two halves: (1) sort the chat scroll by local arrival time (created_at, already populated on every save in MessageRecord.swift:182-184), so a peer with a stale or fast clock can't reorder my own thread; (2) clamp displayed relative time so a future-dated wire timestamp renders as "Just now" instead of "in 5 min".

This PR is half (2) — the iOS clamp. Half (1) lives in LXMF-swift and ships as a separate PR per the plan's split.

Changes

  • Sources/ColumbaApp/Views/Messaging/MessageBubble.swift — clamp Message.formattedTime to min(timestamp, Date()) before handing to RelativeDateTimeFormatter. The bubble's absolute-time formatter at MessageDetailView.swift:527 is intentionally left alone — that view shows raw protocol-level metadata.
  • Tests/ColumbaAppTests/MessageFormattedTimeTests.swift — new test file with two cases: future-dated timestamps render as the formatter's "now" string; past-dated timestamps still render the formatter's relative-past string.
  • Columba.xcodeproj/project.pbxproj — wire the new test file into the ColumbaAppTests target (PBXBuildFile / PBXFileReference / group / sources phase).

Test plan

  • test_formattedTime_clampsFutureTimestampToNow — constructs a 600 s future-dated Message, asserts formattedTime == formatter.localizedString(for: now, relativeTo: now). Captures whatever the formatter actually emits rather than hard-coding "Just now" so locale variation doesn't break it.
  • test_formattedTime_pastTimestampsRenderRelativePast — regression guard that the clamp doesn't affect past timestamps.
  • Full suite: xcodebuild test -only-testing:ColumbaAppTests — 67 passed, 0 failed.
  • xcodebuild build clean (warnings present in the build log are pre-existing Swift 6 sendability warnings unrelated to this change).

Implementer notes

  • The full issue resolution depends on a companion LXMF-swift PR that swaps the chat-scroll ordering from wire timestamp to created_at. This iOS PR alone covers the "in 5 min" symptom only; the out-of-order-messages symptom needs the LXMF-swift PR. Commit message uses Refs #60 (not Closes) for that reason.
  • No Package.swift version bump for LXMF-swift in this PR — the LXMF-swift change isn't released yet, so bumping the pin would break SPM resolution. The bump should land in the LXMF-swift PR's follow-up or a coordination PR once the LXMF-swift release is tagged.

🤖 Generated with Claude Code

A peer with a fast clock (or our own clock corrected backward by NTP
between pack-time and view-time) can stamp a wire timestamp ahead of
local time. Rendering it through RelativeDateTimeFormatter then yields
strings like "in 5 min" on a message that has already arrived. Clamp
the timestamp passed to the formatter to min(timestamp, now) so future
wire timestamps render as the formatter's "now" string instead.

Refs #60

Co-Authored-By: Claude claude-opus-4-7 <noreply@anthropic.com>
@torlando-tech

Copy link
Copy Markdown
Owner

Superseded by #86. The fix (clamp Message.formattedTime to min(timestamp, now)) is still needed — the unclamped line is live on main — but this PR is 78 commits behind and conflicts on both MessageBubble.swift and project.pbxproj (the dual-backend refactor rewrote around formattedTime and restructured the test target). #86 re-applies the same clamp + test cleanly on current main (build-for-testing + both tests pass). Closing in favor of #86.

torlando-tech added a commit that referenced this pull request Jun 1, 2026
fix(MessageBubble): clamp future-dated timestamps to now (supersedes #66)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant