Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Columba.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
T001 /* AudioRingBufferTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FT01 /* AudioRingBufferTests.swift */; };
T002 /* AudioManagerConfigChangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FT02 /* AudioManagerConfigChangeTests.swift */; };
T004 /* CallManagerCallKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FT04 /* CallManagerCallKitTests.swift */; };
T005 /* MessageFormattedTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FT05 /* MessageFormattedTimeTests.swift */; };
001 /* ColumbaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F001 /* ColumbaApp.swift */; };
002 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = F002 /* Theme.swift */; };
003 /* ChatsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F003 /* ChatsViewModel.swift */; };
Expand Down Expand Up @@ -139,6 +140,7 @@
FT02 /* AudioManagerConfigChangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioManagerConfigChangeTests.swift; sourceTree = "<group>"; };
FT04 /* CallManagerCallKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallManagerCallKitTests.swift; sourceTree = "<group>"; };
FT03 /* MicronParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicronParserTests.swift; sourceTree = "<group>"; };
FT05 /* MessageFormattedTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageFormattedTimeTests.swift; sourceTree = "<group>"; };
TPROD /* ColumbaAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ColumbaAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
EPROD /* ColumbaNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ColumbaNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
F001 /* ColumbaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumbaApp.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -555,6 +557,7 @@
FT02 /* AudioManagerConfigChangeTests.swift */,
FT03 /* MicronParserTests.swift */,
FT04 /* CallManagerCallKitTests.swift */,
FT05 /* MessageFormattedTimeTests.swift */,
);
path = Tests/ColumbaAppTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -722,6 +725,7 @@
T002 /* AudioManagerConfigChangeTests.swift in Sources */,
T003 /* MicronParserTests.swift in Sources */,
T004 /* CallManagerCallKitTests.swift in Sources */,
T005 /* MessageFormattedTimeTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
7 changes: 6 additions & 1 deletion Sources/ColumbaApp/Views/Messaging/MessageBubble.swift
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,12 @@ public struct Message: Identifiable, Equatable {

/// Formatted time string (e.g., "5 min ago", "Just now")
public var formattedTime: String {
Self.relativeFormatter.localizedString(for: timestamp, relativeTo: Date())
// Clamp future-dated timestamps to now: a peer (or our own past clock)
// can stamp a wire ts ahead of local time, and we must never render
// "in 5 min" on a message that has already arrived.
let now = Date()
let display = min(timestamp, now)
return Self.relativeFormatter.localizedString(for: display, relativeTo: now)
}

/// True if message has no visible content (telemetry-only messages).
Expand Down
28 changes: 28 additions & 0 deletions Tests/ColumbaAppTests/MessageFormattedTimeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import XCTest
@testable import ColumbaApp

final class MessageFormattedTimeTests: XCTestCase {

func test_formattedTime_clampsFutureTimestampToNow() {
let future = Date().addingTimeInterval(600)
let message = Message(content: "hi", timestamp: future, isFromMe: true)

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .abbreviated
let referenceNow = Date()
let expected = formatter.localizedString(for: referenceNow, relativeTo: referenceNow)

XCTAssertEqual(message.formattedTime, expected)
}

func test_formattedTime_pastTimestampsRenderRelativePast() {
let oneHourAgo = Date().addingTimeInterval(-3600)
let message = Message(content: "hi", timestamp: oneHourAgo, isFromMe: false)

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .abbreviated
let expected = formatter.localizedString(for: oneHourAgo, relativeTo: Date())

XCTAssertEqual(message.formattedTime, expected)
}
}
Loading