Skip to content

Commit d8862c8

Browse files
authored
Change the gallery header view to show the message timestamp instead of online status (#3818)
* Change gallery header view to show message timestamp instead of online status * Update CHANGELOG.md
1 parent 3a7d104 commit d8862c8

File tree

8 files changed

+111
-9
lines changed

8 files changed

+111
-9
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
99
### 🐞 Fixed
1010
- Fix querying threads by disabled channels crashing [#3813](https://github.com/GetStream/stream-chat-swift/pull/3813)
1111

12+
## StreamChatUI
13+
### 🔄 Changed
14+
- Change gallery header view to show message timestamp instead of online status [#3818](https://github.com/GetStream/stream-chat-swift/pull/3818)
15+
1216
# [4.88.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.88.0)
1317
_September 09, 2025_
1418

Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public extension Appearance {
4545
/// A formatter that provides a name for a recording based on its position in a list of recordings.
4646
public var audioRecordingNameFormatter: AudioRecordingNameFormatter = DefaultAudioRecordingNameFormatter()
4747

48+
/// A formatter that converts the message timestamp to textual representation for the gallery header view.
49+
public var galleryHeaderViewDateFormatter: GalleryHeaderViewDateFormatter = DefaultGalleryHeaderViewDateFormatter()
50+
4851
/// A boolean value that determines whether Markdown is active for messages to be formatted.
4952
public var isMarkdownEnabled = true
5053
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
7+
/// A formatter that converts the message timestamp to textual representation for the gallery header view.
8+
public protocol GalleryHeaderViewDateFormatter {
9+
func format(_ date: Date) -> String
10+
}
11+
12+
/// The default gallery header view date formatter.
13+
open class DefaultGalleryHeaderViewDateFormatter: GalleryHeaderViewDateFormatter {
14+
public var dateFormatter: DateFormatter = {
15+
let formatter = DateFormatter()
16+
formatter.locale = .autoupdatingCurrent
17+
formatter.dateStyle = .short
18+
formatter.timeStyle = .none
19+
return formatter
20+
}()
21+
22+
let dayFormatter: DateFormatter = {
23+
let formatter = DateFormatter()
24+
formatter.locale = .autoupdatingCurrent
25+
formatter.dateStyle = .short
26+
formatter.timeStyle = .none
27+
formatter.doesRelativeDateFormatting = true
28+
return formatter
29+
}()
30+
31+
var calendar: StreamCalendar = Calendar.current
32+
33+
public init() {}
34+
35+
open func format(_ date: Date) -> String {
36+
if calendar.isDateInToday(date) {
37+
return dayFormatter.string(from: date)
38+
}
39+
40+
if calendar.isDateInYesterday(date) {
41+
return dayFormatter.string(from: date)
42+
}
43+
44+
return dateFormatter.string(from: date)
45+
}
46+
}

Sources/StreamChatUI/Gallery/GalleryVC.swift

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ open class GalleryVC: _ViewController,
4848
/// Returns the date formatter function used to represent when the user was last seen online.
4949
open var lastSeenDateFormatter: (Date) -> String? { appearance.formatters.userLastActivity.format }
5050

51+
/// A formatter that converts the message timestamp to textual representation for the gallery header view.
52+
open var messageTimestampFormatter: (Date) -> String? { appearance.formatters.galleryHeaderViewDateFormatter.format }
53+
54+
/// A boolean value indicating if the subtitle of the header should show the message timestamp.
55+
public var showMessageTimestamp: Bool = true
56+
5157
/// Controller for handling the transition for dismissal
5258
open var transitionController: ZoomTransitionController!
5359

@@ -267,15 +273,18 @@ open class GalleryVC: _ViewController,
267273
override open func updateContent() {
268274
super.updateContent()
269275

270-
if content.message.author.isOnline {
271-
dateLabel.text = L10n.Message.Title.online
276+
if showMessageTimestamp {
277+
dateLabel.text = messageTimestampFormatter(content.message.createdAt)
272278
} else {
273-
if
274-
let lastActive = content.message.author.lastActiveAt,
275-
let timeAgo = lastSeenDateFormatter(lastActive) {
276-
dateLabel.text = timeAgo
279+
if content.message.author.isOnline {
280+
dateLabel.text = L10n.Message.Title.online
277281
} else {
278-
dateLabel.text = L10n.Message.Title.offline
282+
if let lastActive = content.message.author.lastActiveAt,
283+
let timeAgo = lastSeenDateFormatter(lastActive) {
284+
dateLabel.text = timeAgo
285+
} else {
286+
dateLabel.text = L10n.Message.Title.offline
287+
}
279288
}
280289
}
281290

StreamChat.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,8 @@
14561456
AD3D0CC026A8727800A6D813 /* SlackChatChannelHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3D0CBF26A8727800A6D813 /* SlackChatChannelHeaderView.swift */; };
14571457
AD3D0CC226A88E5100A6D813 /* MessengerChatChannelHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3D0CC126A88E5100A6D813 /* MessengerChatChannelHeaderView.swift */; };
14581458
AD3D0CC426A89E6300A6D813 /* iMessageChatChannelHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3D0CC326A89E6300A6D813 /* iMessageChatChannelHeaderView.swift */; };
1459+
AD3DB8312E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3DB8302E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift */; };
1460+
AD3DB8322E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD3DB8302E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift */; };
14591461
AD3EE5442832921400ACEFD9 /* VirtualTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3D15D8527E9D4B5006B34D7 /* VirtualTime.swift */; };
14601462
AD4118832D5E1368000EF88E /* UILabel+highlightText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD4118822D5E135D000EF88E /* UILabel+highlightText.swift */; };
14611463
AD4118842D5E1368000EF88E /* UILabel+highlightText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD4118822D5E135D000EF88E /* UILabel+highlightText.swift */; };
@@ -4318,6 +4320,7 @@
43184320
AD3D0CBF26A8727800A6D813 /* SlackChatChannelHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlackChatChannelHeaderView.swift; sourceTree = "<group>"; };
43194321
AD3D0CC126A88E5100A6D813 /* MessengerChatChannelHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessengerChatChannelHeaderView.swift; sourceTree = "<group>"; };
43204322
AD3D0CC326A89E6300A6D813 /* iMessageChatChannelHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iMessageChatChannelHeaderView.swift; sourceTree = "<group>"; };
4323+
AD3DB8302E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryHeaderViewDateFormatter.swift; sourceTree = "<group>"; };
43214324
AD4118822D5E135D000EF88E /* UILabel+highlightText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+highlightText.swift"; sourceTree = "<group>"; };
43224325
AD43DE6C2A712B0F0040C0FD /* ChatChannelListSearchVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatChannelListSearchVC.swift; sourceTree = "<group>"; };
43234326
AD43F90826153BAD00F2D4BB /* QuotedChatMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotedChatMessageView.swift; sourceTree = "<group>"; };
@@ -8949,6 +8952,7 @@
89498952
AD99C901279B06E9009DD9C5 /* Appearance+Formatters */ = {
89508953
isa = PBXGroup;
89518954
children = (
8955+
AD3DB8302E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift */,
89528956
ADC4AAAF2788C8850004BB35 /* Appearance+Formatters.swift */,
89538957
40D396232A0905560020DDC9 /* AudioPlaybackRateFormatter.swift */,
89548958
40D396242A0905560020DDC9 /* AudioRecordingNameFormatter.swift */,
@@ -10931,6 +10935,7 @@
1093110935
AD81AF0525ED141800F17F8F /* CellSeparatorView.swift in Sources */,
1093210936
790882FD25486BFD00896F03 /* ChatChannelListCollectionViewCell.swift in Sources */,
1093310937
88CABC4525933EE70061BB67 /* ChatMessageReactionsView.swift in Sources */,
10938+
AD3DB8312E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift in Sources */,
1093410939
ADD2A99028FF0CD300A83305 /* ImageSizeCalculator.swift in Sources */,
1093510940
AD7EFDAA2C78C0AF00625FC5 /* PollCommentListItemCell.swift in Sources */,
1093610941
E7166CE225BEE20600B03B07 /* Appearance+Images.swift in Sources */,
@@ -13077,6 +13082,7 @@
1307713082
C121EBA82746A1E800023E4C /* ChatChannelAvatarView.swift in Sources */,
1307813083
C121EBA92746A1E800023E4C /* ChatChannelAvatarView+SwiftUI.swift in Sources */,
1307913084
C121EBAA2746A1E800023E4C /* ChatUserAvatarView.swift in Sources */,
13085+
AD3DB8322E7C48BF0023D377 /* GalleryHeaderViewDateFormatter.swift in Sources */,
1308013086
40824D0F2A1270CB003B61FD /* ChatMessageVoiceRecordingAttachmentListView.swift in Sources */,
1308113087
C121EBAB2746A1E800023E4C /* CurrentChatUserAvatarView.swift in Sources */,
1308213088
C121EBAC2746A1E800023E4C /* InputChatMessageView.swift in Sources */,

Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ final class GalleryVC_Tests: XCTestCase {
3030
)
3131

3232
vc = makeGalleryVC(content: content)
33+
vc.showMessageTimestamp = false
3334
}
3435

3536
override func tearDown() {
@@ -125,10 +126,43 @@ final class GalleryVC_Tests: XCTestCase {
125126
let vc = TestView()
126127
vc.components = .mock
127128
vc.content = content
129+
vc.showMessageTimestamp = false
128130

129131
AssertSnapshot(vc)
130132
}
131133

134+
func test_snapshotWithMessageTimestampToday() {
135+
let today = Date()
136+
let message = makeMessage(with: [
137+
ChatMessageImageAttachment.mock(
138+
id: .unique,
139+
imageURL: TestImages.yoda.url
140+
).asAnyAttachment
141+
], createdAt: today)
142+
143+
let content = GalleryVC.Content(message: message, currentPage: 0)
144+
let vc = makeGalleryVC(content: content)
145+
vc.showMessageTimestamp = true
146+
147+
AssertSnapshot(vc, variants: [.defaultLight])
148+
}
149+
150+
func test_snapshotWithMessageTimestampOlderDate() {
151+
let olderDate = Date(timeIntervalSince1970: 1_577_836_800)
152+
let message = makeMessage(with: [
153+
ChatMessageImageAttachment.mock(
154+
id: .unique,
155+
imageURL: TestImages.yoda.url
156+
).asAnyAttachment
157+
], createdAt: olderDate)
158+
159+
let content = GalleryVC.Content(message: message, currentPage: 0)
160+
let vc = makeGalleryVC(content: content)
161+
vc.showMessageTimestamp = true
162+
163+
AssertSnapshot(vc, variants: [.defaultLight])
164+
}
165+
132166
private func makeGalleryVC(content: GalleryVC.Content, components: Components = .mock) -> GalleryVC {
133167
let vc = GalleryVC()
134168
vc.components = components
@@ -137,7 +171,7 @@ final class GalleryVC_Tests: XCTestCase {
137171
return vc
138172
}
139173

140-
private func makeMessage(with attachments: [AnyChatMessageAttachment]) -> ChatMessage {
174+
private func makeMessage(with attachments: [AnyChatMessageAttachment], createdAt: Date = Date(timeIntervalSinceReferenceDate: 0)) -> ChatMessage {
141175
.mock(
142176
id: .unique,
143177
cid: .unique,
@@ -146,7 +180,7 @@ final class GalleryVC_Tests: XCTestCase {
146180
id: .unique,
147181
name: "Author"
148182
),
149-
createdAt: Date(timeIntervalSinceReferenceDate: 0),
183+
createdAt: createdAt,
150184
attachments: attachments
151185
)
152186
}
Loading
Loading

0 commit comments

Comments
 (0)