Skip to content

Conversation

nuno-vieira
Copy link
Member

@nuno-vieira nuno-vieira commented Sep 18, 2025

🔗 Issue Links

https://linear.app/stream/issue/IOS-1059/deputy-replace-a-user-status-with-a-message-date-in-the-galary

Same as GetStream/stream-chat-swiftui#962 but for UIKit

🎯 Goal

Change the gallery header view to show the message timestamp instead of online status.
This is to make it aligned with the other SDKs.

🧪 Manual Testing Notes

  1. Open a message with an image
  2. The header should display timestamp instead of the online status

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • Changelog is updated with new localization keys
  • New code is covered by unit tests
  • Documentation has been updated in the docs-content repo

Summary by CodeRabbit

  • New Features
    • Gallery header now displays the message timestamp by default, with an option to revert to the previous status display.
    • Timestamp formatting is customizable and shows “Today/Yesterday” for recent messages, or a short date for older ones.
  • Documentation
    • Changelog updated to document the gallery header change.
  • Tests
    • Added snapshot tests for timestamp display (today vs. older messages).

@nuno-vieira nuno-vieira requested a review from a team as a code owner September 18, 2025 14:41
Copy link

coderabbitai bot commented Sep 18, 2025

Walkthrough

Adds a timestamp-focused header in GalleryVC. Introduces a new gallery header date formatter protocol and default implementation, wires it via Appearance.Formatters, and updates GalleryVC to optionally show message timestamps instead of user status. Updates project references, changelog entry, and adds snapshot tests for today/older dates.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Documents upcoming UI change: gallery header shows message timestamp instead of online status; references PR #3818.
Appearance formatters API
Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift
Adds public galleryHeaderViewDateFormatter property to Appearance.Formatters, defaulting to DefaultGalleryHeaderViewDateFormatter().
Gallery header date formatting
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift
Adds GalleryHeaderViewDateFormatter protocol and DefaultGalleryHeaderViewDateFormatter implementation; formats dates as Today/Yesterday or short date.
GalleryVC header behavior
Sources/StreamChatUI/Gallery/GalleryVC.swift
Adds messageTimestampFormatter and showMessageTimestamp (default true). Updates content to display message timestamp when enabled; otherwise uses previous online/last seen logic.
Project integration
StreamChat.xcodeproj/project.pbxproj
Registers GalleryHeaderViewDateFormatter.swift in project, groups it under Appearance+Formatters, adds to sources build phases.
Snapshot tests
Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
Adds tests for timestamp display (today and older). Updates helpers to support createdAt, reloads collection view, and controls showMessageTimestamp in scenarios.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant GalleryVC
  participant Appearance.Formatters as Formatters
  participant HeaderView as Header

  User->>GalleryVC: Open gallery (message with createdAt)
  GalleryVC->>GalleryVC: updateContent()
  alt showMessageTimestamp == true
    GalleryVC->>Formatters: format(createdAt)
    Formatters-->>GalleryVC: "Today"/"Yesterday"/short date
    GalleryVC->>Header: Set subtitle = formatted date
  else
    GalleryVC->>GalleryVC: Determine online / last seen
    GalleryVC->>Header: Set subtitle = status/last seen/offline
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

🌐 SDK: StreamChat (LLC), ✅ Feature, 🤞 Ready For QA

Suggested reviewers

  • laevandus
  • testableapple
  • martinmitrevski

Poem

I nibble code like clover leaves—so neat!
A timestamp blooms where statuses used to meet.
Today, Yesterday—hop-hop, clear and bright,
The gallery’s header now tells time just right.
With whiskers twitching, I approve this view—
A hare of dates, and snapshots too! 🐇⌚️

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning Most modifications are scoped to the gallery-timestamp feature (formatters, GalleryVC updates, tests, changelog), however the project.pbxproj changes show the new Swift file referenced in multiple Groups/Sources with duplicate PBXBuildFile entries which may unintentionally include the file in multiple targets, and the addition of a public Appearance.Formatters property is a public API change that should be intentional and documented. Request the author to verify and, if unintended, remove duplicate PBXBuildFile entries or restrict the new file to the intended target(s), and confirm/document that the new public formatter property is an intended public API addition (update API docs/changelog entries as needed) before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title accurately and concisely summarizes the primary change: replacing the gallery header's online status with the message timestamp, matching the diff and PR objectives; it is a single clear sentence suitable for history scanning.
Linked Issues Check ✅ Passed The changes implement IOS-1059's coding objective: GalleryVC now selects a message-timestamp path (messageTimestampFormatter and showMessageTimestamp), a GalleryHeaderViewDateFormatter/DefaultGalleryHeaderViewDateFormatter were added, snapshot tests exercise the behavior, and the changelog documents the client-facing change, so the PR meets the linked issue's requirements for replacing online status with a message date.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/gallery-header-view-subtitle-with-timestamp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Public Interface

+ open class DefaultGalleryHeaderViewDateFormatter: GalleryHeaderViewDateFormatter  
+ 
+   public var dateFormatter: DateFormatter
+   
+ 
+   public init()
+   
+ 
+   open func format(_ date: Date)-> String

+ public protocol GalleryHeaderViewDateFormatter



 open class GalleryVC: _ViewController, UIGestureRecognizerDelegate, AppearanceProvider, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, ComponentsProvider  
-   open var transitionController: ZoomTransitionController!
+   open var messageTimestampFormatter: (Date) -> String?
-   open private lazy var attachmentsFlowLayout: UICollectionViewFlowLayout
+   public var showMessageTimestamp: Bool
-   open private lazy var attachmentsCollectionView: UICollectionView
+   open var transitionController: ZoomTransitionController!
-   open private lazy var topBarView
+   open private lazy var attachmentsFlowLayout: UICollectionViewFlowLayout
-   open private lazy var topBarContainerStackView
+   open private lazy var attachmentsCollectionView: UICollectionView
-   open private lazy var infoContainerStackView
+   open private lazy var topBarView
-   open private lazy var userLabel
+   open private lazy var topBarContainerStackView
-   open private lazy var dateLabel
+   open private lazy var infoContainerStackView
-   open private lazy var bottomBarView
+   open private lazy var userLabel
-   open private lazy var bottomBarContainerStackView
+   open private lazy var dateLabel
-   open private lazy var currentPhotoLabel
+   open private lazy var bottomBarView
-   open private lazy var closeButton
+   open private lazy var bottomBarContainerStackView
-   open private lazy var videoPlaybackBar: VideoPlaybackControlView
+   open private lazy var currentPhotoLabel
-   open private lazy var shareButton
+   open private lazy var closeButton
-   open private var topBarTopConstraint: NSLayoutConstraint?
+   open private lazy var videoPlaybackBar: VideoPlaybackControlView
-   open private var bottomBarBottomConstraint: NSLayoutConstraint?
+   open private lazy var shareButton
-   open var currentItemIndexPath: IndexPath
+   open private var topBarTopConstraint: NSLayoutConstraint?
-   open var currentItem: AnyChatMessageAttachment
+   open private var bottomBarBottomConstraint: NSLayoutConstraint?
-   open var imageViewToAnimateWhenDismissing: UIImageView?
+   open var currentItemIndexPath: IndexPath
-   
+   open var currentItem: AnyChatMessageAttachment
- 
+   open var imageViewToAnimateWhenDismissing: UIImageView?
-   override open func setUpAppearance()
+   
-   override open func setUp()
+ 
-   override open func setUpLayout()
+   override open func setUpAppearance()
-   override open func viewDidLoad()
+   override open func setUp()
-   override open func viewDidAppear(_ animated: Bool)
+   override open func setUpLayout()
-   override open func viewWillDisappear(_ animated: Bool)
+   override open func viewDidLoad()
-   override open func updateContent()
+   override open func viewDidAppear(_ animated: Bool)
-   @objc open func handlePan(with gestureRecognizer: UIPanGestureRecognizer)
+   override open func viewWillDisappear(_ animated: Bool)
-   @objc open func closeButtonTapped()
+   override open func updateContent()
-   @objc open func shareButtonTapped()
+   @objc open func handlePan(with gestureRecognizer: UIPanGestureRecognizer)
-   open func updateCurrentPage()
+   @objc open func closeButtonTapped()
-   open func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int)-> Int
+   @objc open func shareButtonTapped()
-   open func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath)-> UICollectionViewCell
+   open func updateCurrentPage()
-   open func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath)-> CGSize
+   open func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int)-> Int
-   open func collectionView(_ collectionView: UICollectionView,targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint)-> CGPoint
+   open func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath)-> UICollectionViewCell
-   open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
+   open func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath)-> CGSize
-   open func scrollViewDidScroll(_ scrollView: UIScrollView)
+   open func collectionView(_ collectionView: UICollectionView,targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint)-> CGPoint
-   override open func viewWillTransition(to size: CGSize,with coordinator: UIViewControllerTransitionCoordinator)
+   open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
-   open func shareItem(at indexPath: IndexPath)-> Any?
+   open func scrollViewDidScroll(_ scrollView: UIScrollView)
-   open func cellReuseIdentifierForItem(at indexPath: IndexPath)-> String?
+   override open func viewWillTransition(to size: CGSize,with coordinator: UIViewControllerTransitionCoordinator)
-   open func handleSingleTapOnCell(at indexPath: IndexPath)
+   open func shareItem(at indexPath: IndexPath)-> Any?
-   
+   open func cellReuseIdentifierForItem(at indexPath: IndexPath)-> String?
- 
+   open func handleSingleTapOnCell(at indexPath: IndexPath)
-   public struct Content  
+   
-   
+ 
-     public var message: ChatMessage
+   public struct Content  
-     public var currentPage: Int
+   
-     
+     public var message: ChatMessage
-   
+     public var currentPage: Int
-     public init(message: ChatMessage,currentPage: Int = 0)
+     
+   
+     public init(message: ChatMessage,currentPage: Int = 0)

@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamChat 8.13 MB 8.13 MB 0 KB 🟢
StreamChatUI 4.88 MB 4.88 MB +2 KB 🟢

@Stream-SDK-Bot
Copy link
Collaborator

SDK Performance

target metric benchmark branch performance status
MessageList Hitches total duration 10 ms 5.01 ms 49.9% 🔼 🟢
Duration 2.6 s 2.54 s 2.31% 🔼 🟢
Hitch time ratio 4 ms per s 1.97 ms per s 50.75% 🔼 🟢
Frame rate 75 fps 77.9 fps 3.87% 🔼 🟢
Number of hitches 1 0.6 40.0% 🔼 🟢

@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamChat 8.13 MB 8.13 MB 0 KB 🟢
StreamChatUI 4.88 MB 4.88 MB +2 KB 🟢

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (10)
CHANGELOG.md (1)

12-15: Link this entry to the current PR (and optionally reference the SwiftUI one).

Replace the SwiftUI PR link with this PR, and keep the SwiftUI reference as context.

Apply this diff:

-## StreamChatUI
-### 🔄 Changed
-- Change gallery header view to show message timestamp instead of online status [#962](https://github.com/GetStream/stream-chat-swiftui/pull/962)
+## StreamChatUI
+### 🔄 Changed
+- Change gallery header view to show message timestamp instead of online status [#3818](https://github.com/GetStream/stream-chat-swift/pull/3818) (mirrors SwiftUI [#962](https://github.com/GetStream/stream-chat-swiftui/pull/962))
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (4)

14-20: Make the formatter lazy and document it; set autoupdating time zone.

Avoid unnecessary instantiation, explicitly track TZ changes, and document the public API surface.

Apply this diff:

-    public var dateFormatter: DateFormatter = {
+    /// Base formatter used when the date is neither today nor yesterday.
+    public lazy var dateFormatter: DateFormatter = {
         let formatter = DateFormatter()
         formatter.locale = .autoupdatingCurrent
+        formatter.timeZone = .autoupdatingCurrent
         formatter.dateStyle = .short
         formatter.timeStyle = .none
         return formatter
     }()

22-29: Tighten visibility and mirror TZ behavior.

This helper isn’t part of the public API; make it private and lazy, and keep TZ in sync with dateFormatter.

Apply this diff:

-    let dayFormatter: DateFormatter = {
+    private lazy var dayFormatter: DateFormatter = {
         let formatter = DateFormatter()
         formatter.locale = .autoupdatingCurrent
+        formatter.timeZone = .autoupdatingCurrent
         formatter.dateStyle = .short
         formatter.timeStyle = .none
         formatter.doesRelativeDateFormatting = true
         return formatter
     }()

31-31: Prefer an autoupdating calendar (and consider injection).

Use autoupdatingCurrent so “today/yesterday” tracks user setting changes; optional: add a convenience init for injecting a calendar in tests.

Apply this diff:

-    var calendar: StreamCalendar = Calendar.current
+    var calendar: StreamCalendar = Calendar.autoupdatingCurrent

Optionally add:

+    public convenience init(calendar: StreamCalendar) {
+        self.init()
+        self.calendar = calendar
+    }

35-45: Confirm product copy: do we need time-of-day for “Today”?

Current output for today/yesterday is just the relative day (“Today”/“Yesterday”). If UX expects a timestamp (e.g., “Today, 3:42 PM”), adjust here or in the consumer.

If needed, one option:

-        if calendar.isDateInToday(date) {
-            return dayFormatter.string(from: date)
-        }
+        if calendar.isDateInToday(date) {
+            // e.g., "Today, 3:42 PM"
+            let time = DateFormatter.localizedString(from: date, dateStyle: .none, timeStyle: .short)
+            return "\(dayFormatter.string(from: date)), \(time)"
+        }

Please confirm the expected format against the SwiftUI implementation and existing snapshots. If alignment is required, I can update both formatter and tests.

StreamChat.xcodeproj/project.pbxproj (1)

8955-8955: Optional: keep group children sorted to reduce future diff churn.

If the project prefers alphabetical order in groups, consider reordering the new file under Appearance+Formatters. Purely a nit.

Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (1)

150-164: Stability for older-date snapshot.

Hard-coded epoch is fine, but output still varies by locale/time zone since the formatter uses auto-updating settings. Ensure tests force a stable locale/time zone, or inject a stub formatter returning a fixed string.

Sources/StreamChatUI/Gallery/GalleryVC.swift (3)

51-53: Return non-optional from messageTimestampFormatter.

DefaultGalleryHeaderViewDateFormatter.format returns String (non-optional). Expose the property as (Date) -> String for clarity and to avoid implying nil is valid.

Apply:

-    open var messageTimestampFormatter: (Date) -> String? { appearance.formatters.galleryHeaderViewDateFormatter.format }
+    open var messageTimestampFormatter: (Date) -> String { appearance.formatters.galleryHeaderViewDateFormatter.format }

55-56: Update view when toggling showMessageTimestamp.

Without a didSet, changing this at runtime won’t refresh the header until the next lifecycle/update. Trigger an update to reflect the change immediately.

Apply:

-    public var showMessageTimestamp: Bool = true
+    public var showMessageTimestamp: Bool = true {
+        didSet { updateContentIfNeeded() }
+    }

276-288: Header update branch reads well; keep a11y in mind.

When showing timestamps, consider setting an accessibilityLabel (e.g., “Sent on ”) for dateLabel in a follow-up to improve VO clarity.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a7d104 and 15b5735.

⛔ Files ignored due to path filters (2)
  • Tests/StreamChatUITests/SnapshotTests/Gallery/__Snapshots__/GalleryVC_Tests/test_snapshotWithMessageTimestampOlderDate.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/Gallery/__Snapshots__/GalleryVC_Tests/test_snapshotWithMessageTimestampToday.default-light.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • CHANGELOG.md (1 hunks)
  • Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift (1 hunks)
  • Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1 hunks)
  • Sources/StreamChatUI/Gallery/GalleryVC.swift (2 hunks)
  • StreamChat.xcodeproj/project.pbxproj (5 hunks)
  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChatUI/Gallery/GalleryVC.swift
  • Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift
  • Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift
  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChatUI/Gallery/GalleryVC.swift
  • Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift
  • Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Update CHANGELOG for user-visible SDK changes

Files:

  • CHANGELOG.md
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Tests/{StreamChatTests,StreamChatUITests}/**/*.swift: Add or extend tests in the matching module’s Tests folder
Prefer using provided test fakes/mocks in tests when possible

Files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
Tests/StreamChatUITests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover view controllers and UI behaviors of StreamChatUI

Files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
🧠 Learnings (4)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • StreamChat.xcodeproj/project.pbxproj
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Prefer using provided test fakes/mocks in tests when possible

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
🧬 Code graph analysis (3)
Sources/StreamChatUI/Gallery/GalleryVC.swift (3)
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1)
  • format (35-45)
Sources/StreamChatUI/Appearance+Formatters/UserLastActivityFormatter.swift (1)
  • format (16-18)
Sources/StreamChatUI/Utils/DateUtils.swift (1)
  • timeAgo (9-52)
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1)
Tests/StreamChatUITests/Mocks/Utils/Calendar_Mock.swift (2)
  • isDateInToday (13-15)
  • isDateInYesterday (17-19)
Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (1)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/Attachments/ChatMessageImageAttachment_Mock.swift (1)
  • mock (10-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Test App and Frameworks
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Automated Code Review
  • GitHub Check: Metrics
  • GitHub Check: Metrics
🔇 Additional comments (9)
StreamChat.xcodeproj/project.pbxproj (4)

10938-10938: Looks like correct target membership; just confirm it’s not added to a test/host‑app target.

Assuming this Sources phase is the StreamChatUI framework, this entry is expected. Please cross‑check with the script above to ensure it’s not compiled into a unit‑test or demo app target.


13085-13085: Second Sources membership: confirm it’s the other UI target (e.g., another platform) rather than a host app.

Two targets compiling the file is fine; compiling into an app in addition to the framework would duplicate types. Validate with the mapping script.


4323-4323: PBXFileReference verified — file present and SPM pickup OK.
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift exists; Package.swift defines the StreamChatUI target; Appearance.Formatters registers galleryHeaderViewDateFormatter and the public/open declarations have triple‑slash doc comments.


1459-1460: Verify dual “in Sources” entries map to distinct targets (avoid duplicate compilation in a single target).

Two PBXBuildFile entries are expected if the file is compiled into two different targets. Please confirm they’re attached to different PBXSourcesBuildPhases and not duplicated within the same target.

Run this script to resolve which targets compile the file and flag duplicates:

Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift (1)

48-50: New formatter hook looks good; confirm docs and theming guides updated.

API shape and defaults align with existing formatters. Please ensure the public API docs and theming/customization guides reference galleryHeaderViewDateFormatter.

Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (4)

33-33: Good call setting the default off for legacy snapshots.


129-129: Consistent override in subclassing test.


174-186: Helper enhancement looks good; default createdAt is sensible.

The optional createdAt parameter improves test readability. No issues.


134-148: Make the “today” snapshot deterministic.

Replace Date() in test_snapshotWithMessageTimestampToday (Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift, lines 134–148) with a fixed Date or stub the date formatter / pin Locale/TimeZone (e.g. Locale(identifier: "en_US_POSIX"), TimeZone(secondsFromGMT: 0)) to avoid flakiness across locales/time zones and near midnight.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift (1)

48-50: Naming nit: drop “View” for consistency

Other formatter names avoid UI-type suffixes (e.g., channelName, userLastActivity). Consider galleryHeaderDate or galleryHeaderMessageTimestamp for alignment. Non-blocking.

Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1)

22-29: Use autoupdating Calendar and expose knobs for tests/customization

  • Prefer Calendar.autoupdatingCurrent so “today/yesterday” reacts to system changes.
  • Consider making dayFormatter and calendar open to enable subclass tweaks and easier test injection (you already expose dateFormatter).

Apply:

-    let dayFormatter: DateFormatter = {
+    open var dayFormatter: DateFormatter = {
         let formatter = DateFormatter()
         formatter.locale = .autoupdatingCurrent
         formatter.dateStyle = .short
         formatter.timeStyle = .none
         formatter.doesRelativeDateFormatting = true
         return formatter
     }()
 
-    var calendar: StreamCalendar = Calendar.current
+    open var calendar: StreamCalendar = Calendar.autoupdatingCurrent

Also applies to: 31-31

Sources/StreamChatUI/Gallery/GalleryVC.swift (1)

55-55: Refresh UI when toggling the flag

Ensure header updates if clients change showMessageTimestamp after the view is visible.

-    public var showMessageTimestamp: Bool = true
+    public var showMessageTimestamp: Bool = true {
+        didSet { if oldValue != showMessageTimestamp { updateContent() } }
+    }
Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (2)

134-148: Stabilize today-based snapshot across locales/timezones

“Today” string and short-date formats vary by locale/timezone and around midnight. Inject a fixed locale/timezone (and optionally a mock calendar) into the formatter to avoid flakiness.

Example tweak inside the test before asserting:

-        let vc = makeGalleryVC(content: content)
+        var appearance = Appearance()
+        let fmt = DefaultGalleryHeaderViewDateFormatter()
+        fmt.dateFormatter.locale = Locale(identifier: "en_US_POSIX")
+        fmt.dayFormatter.locale = Locale(identifier: "en_US_POSIX") // @testable gives access
+        fmt.dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
+        fmt.dayFormatter.timeZone = TimeZone(secondsFromGMT: 0)
+        appearance.formatters.galleryHeaderViewDateFormatter = fmt
+
+        let vc = makeGalleryVC(content: content)
+        vc.appearance = appearance
         vc.showMessageTimestamp = true

Optionally, use Calendar_Mock (from Tests/…/Calendar_Mock.swift) via fmt.calendar to force “today”.


150-164: Same determinism concern for older-date snapshot

Apply the same fixed locale/timezone (and no “today/yesterday” mock) to keep short-date output consistent on CI and dev machines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a7d104 and 605d65b.

⛔ Files ignored due to path filters (2)
  • Tests/StreamChatUITests/SnapshotTests/Gallery/__Snapshots__/GalleryVC_Tests/test_snapshotWithMessageTimestampOlderDate.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/Gallery/__Snapshots__/GalleryVC_Tests/test_snapshotWithMessageTimestampToday.default-light.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • CHANGELOG.md (1 hunks)
  • Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift (1 hunks)
  • Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1 hunks)
  • Sources/StreamChatUI/Gallery/GalleryVC.swift (2 hunks)
  • StreamChat.xcodeproj/project.pbxproj (5 hunks)
  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift
  • Sources/StreamChatUI/Gallery/GalleryVC.swift
  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
  • Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift
  • Sources/StreamChatUI/Gallery/GalleryVC.swift
  • Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Update CHANGELOG for user-visible SDK changes

Files:

  • CHANGELOG.md
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Tests/{StreamChatTests,StreamChatUITests}/**/*.swift: Add or extend tests in the matching module’s Tests folder
Prefer using provided test fakes/mocks in tests when possible

Files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
Tests/StreamChatUITests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover view controllers and UI behaviors of StreamChatUI

Files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
🧠 Learnings (4)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • CHANGELOG.md
  • StreamChat.xcodeproj/project.pbxproj
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to CHANGELOG.md : Update CHANGELOG for user-visible SDK changes

Applied to files:

  • CHANGELOG.md
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift
🧬 Code graph analysis (3)
Sources/StreamChatUI/Gallery/GalleryVC.swift (2)
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1)
  • format (35-45)
Sources/StreamChatUI/Appearance+Formatters/UserLastActivityFormatter.swift (1)
  • format (16-18)
Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (1)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/Attachments/ChatMessageImageAttachment_Mock.swift (1)
  • mock (10-42)
Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1)
Tests/StreamChatUITests/Mocks/Utils/Calendar_Mock.swift (2)
  • isDateInToday (13-15)
  • isDateInYesterday (17-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Integration
  • GitHub Check: Build Demo App + Example Apps
  • GitHub Check: Test UI (Debug)
  • GitHub Check: Test LLC (Debug)
🔇 Additional comments (12)
CHANGELOG.md (1)

12-15: Enrich CHANGELOG: document new public API and opt‑out

Add an "Added" entry for the new public API (formatters, Appearance hook, GalleryVC flags) and a one‑liner showing how to revert; update inline documentation for the new public API.

File: CHANGELOG.md (lines 12-15)

 ## StreamChatUI
-### 🔄 Changed
-- Change gallery header view to show message timestamp instead of online status [#3818](https://github.com/GetStream/stream-chat-swift/pull/3818)
+### ✅ Added
+- `Appearance.Formatters.galleryHeaderViewDateFormatter` to customize gallery header timestamps. Introduces `GalleryHeaderViewDateFormatter` and `DefaultGalleryHeaderViewDateFormatter`. [#3818](https://github.com/GetStream/stream-chat-swift/pull/3818)
+- `GalleryVC.showMessageTimestamp` (default `true`) and `GalleryVC.messageTimestampFormatter` for per‑screen overrides. [#3818](https://github.com/GetStream/stream-chat-swift/pull/3818)
+### 🔄 Changed
+- Gallery header now shows the message timestamp instead of the user's online status (parity with other SDKs). To restore the previous behavior, set `showMessageTimestamp = false`. [#3818](https://github.com/GetStream/stream-chat-swift/pull/3818)

Verify symbol names before publishing:

#!/bin/bash
rg -n -C2 -g '!**/Pods/**' -e '\bGalleryHeaderViewDateFormatter\b' -e '\bDefaultGalleryHeaderViewDateFormatter\b' -e '\bgalleryHeaderViewDateFormatter\b' -e '\bshowMessageTimestamp\b' -e '\bmessageTimestampFormatter\b'
StreamChat.xcodeproj/project.pbxproj (5)

8955-8955: Good group placement under Appearance+Formatters.

Organizationally consistent with other formatters.


10938-10938: Confirm target membership for this Sources entry.

This should belong to StreamChatUI targets only (not core StreamChat or test bundles). If it appears twice within the same target, remove the duplicate.

Use the script in the comment for Lines 1459–1460 to see the owning target of this build-file ID.


13085-13085: Second Sources entry — ensure it’s for a different UI target (e.g., static vs dynamic).

If both this and Line 10938 resolve to the same target, dedupe one of them.

Use the ownership script from Lines 1459–1460 to confirm.


4323-4323: PBXFileReference OK — file present and included by SPM/CocoaPods

GalleryHeaderViewDateFormatter.swift exists at Sources/StreamChatUI/Appearance+Formatters, is referenced in StreamChat.xcodeproj/project.pbxproj, StreamChatUI target in Package.swift only excludes Info.plist and Generated/L10n_template.stencil, and StreamChatUI.podspec uses Sources/StreamChatUI/**/*.swift (covers the file).


1459-1460: Confirmed — no duplicate inclusion; each PBXBuildFile entry belongs to a different target.
AD3DB8312E7C48BF0023D377 and AD3DB8322E7C48BF0023D377 map to different PBXSourcesBuildPhase/targets (verification: OK).

Sources/StreamChatUI/Appearance+Formatters/Appearance+Formatters.swift (1)

48-50: Formatter injection for gallery header — looks good

Public surface and docs are consistent with existing formatters.

Sources/StreamChatUI/Appearance+Formatters/GalleryHeaderViewDateFormatter.swift (1)

35-45: Default formatting behavior is sane

Relative “Today/Yesterday” else short date is a reasonable default.

Sources/StreamChatUI/Gallery/GalleryVC.swift (1)

276-288: Branching logic is clear

The timestamp path vs. status/last-seen fallback reads well.

Tests/StreamChatUITests/SnapshotTests/Gallery/GalleryVC_Tests.swift (3)

33-33: Keeps legacy snapshot stable

Disabling timestamps in setUp preserves existing “default appearance” baseline. Good call.


129-129: Subclassing snapshot baseline preserved

Explicitly disabling timestamps here is consistent with setUp.


174-184: Helper extension to pass createdAt — good

This makes tests concise and targeted.

Copy link

@nuno-vieira nuno-vieira merged commit d8862c8 into develop Sep 19, 2025
23 of 24 checks passed
@nuno-vieira nuno-vieira deleted the fix/gallery-header-view-subtitle-with-timestamp branch September 19, 2025 09:13
@Stream-SDK-Bot Stream-SDK-Bot mentioned this pull request Sep 22, 2025
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.

3 participants