Low effort fix for timeline date separator glitches

This commit is contained in:
Stefan Ceriu
2023-05-08 18:18:34 +03:00
committed by Stefan Ceriu
parent faefb00626
commit 0d27ed1bbd
6 changed files with 19 additions and 13 deletions
@@ -44,8 +44,8 @@ struct CollapsibleRoomTimelineView: View {
struct CollapsibleRoomTimelineView_Previews: PreviewProvider { struct CollapsibleRoomTimelineView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let item = CollapsibleTimelineItem(items: [SeparatorRoomTimelineItem(text: "This is a separator"), let item = CollapsibleTimelineItem(items: [SeparatorRoomTimelineItem(id: "First separator", text: "This is a separator"),
SeparatorRoomTimelineItem(text: "This is another separator")]) SeparatorRoomTimelineItem(id: "Second separator", text: "This is another separator")])
CollapsibleRoomTimelineView(timelineItem: item) CollapsibleRoomTimelineView(timelineItem: item)
} }
} }
@@ -41,7 +41,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider {
static let item = ReadMarkerRoomTimelineItem() static let item = ReadMarkerRoomTimelineItem()
static var previews: some View { static var previews: some View {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
RoomTimelineViewProvider.separator(.init(text: "Today"), .single) RoomTimelineViewProvider.separator(.init(id: "Separator", text: "Today"), .single)
RoomTimelineViewProvider.text(.init(id: "", RoomTimelineViewProvider.text(.init(id: "",
timestamp: "", timestamp: "",
isOutgoing: true, isOutgoing: true,
@@ -51,7 +51,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider {
ReadMarkerRoomTimelineView(timelineItem: item) ReadMarkerRoomTimelineView(timelineItem: item)
RoomTimelineViewProvider.separator(.init(text: "Today"), .single) RoomTimelineViewProvider.separator(.init(id: "Separator", text: "Today"), .single)
RoomTimelineViewProvider.text(.init(id: "", RoomTimelineViewProvider.text(.init(id: "",
timestamp: "", timestamp: "",
isOutgoing: false, isOutgoing: false,
@@ -31,7 +31,7 @@ struct SeparatorRoomTimelineView: View {
struct SeparatorRoomTimelineView_Previews: PreviewProvider { struct SeparatorRoomTimelineView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let item = SeparatorRoomTimelineItem(text: "This is a separator") let item = SeparatorRoomTimelineItem(id: "Separator", text: "This is a separator")
SeparatorRoomTimelineView(timelineItem: item) SeparatorRoomTimelineView(timelineItem: item)
} }
} }
@@ -19,7 +19,7 @@ import Foundation
enum RoomTimelineItemFixtures { enum RoomTimelineItemFixtures {
/// The default timeline items used in Xcode previews etc. /// The default timeline items used in Xcode previews etc.
static var `default`: [RoomTimelineItemProtocol] = [ static var `default`: [RoomTimelineItemProtocol] = [
SeparatorRoomTimelineItem(text: "Yesterday"), SeparatorRoomTimelineItem(id: "Yesterday", text: "Yesterday"),
TextRoomTimelineItem(id: UUID().uuidString, TextRoomTimelineItem(id: UUID().uuidString,
timestamp: "10:10 AM", timestamp: "10:10 AM",
isOutgoing: false, isOutgoing: false,
@@ -46,7 +46,7 @@ enum RoomTimelineItemFixtures {
AggregatedReaction(key: "🙏", count: 1, isHighlighted: false), AggregatedReaction(key: "🙏", count: 1, isHighlighted: false),
AggregatedReaction(key: "🙌", count: 2, isHighlighted: true) AggregatedReaction(key: "🙌", count: 2, isHighlighted: true)
])), ])),
SeparatorRoomTimelineItem(text: "Today"), SeparatorRoomTimelineItem(id: "Today", text: "Today"),
TextRoomTimelineItem(id: UUID().uuidString, TextRoomTimelineItem(id: UUID().uuidString,
timestamp: "5 PM", timestamp: "5 PM",
isOutgoing: false, isOutgoing: false,
@@ -231,8 +231,13 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
for (index, collapsibleChunk) in collapsibleChunks.enumerated() { for (index, collapsibleChunk) in collapsibleChunks.enumerated() {
let isLastItem = index == collapsibleChunks.indices.last let isLastItem = index == collapsibleChunks.indices.last
// Try building a stable identifier for items that don't have one
// We need to avoid duplicates otherwise the diffable datasource will crash
let reversedIndex = collapsibleChunks.count - index
let items = collapsibleChunk.compactMap { itemProxy in let items = collapsibleChunk.compactMap { itemProxy in
let timelineItem = buildTimelineItem(for: itemProxy)
let timelineItem = buildTimelineItem(for: itemProxy, chunkIndex: reversedIndex)
if timelineItem is PaginationIndicatorRoomTimelineItem { if timelineItem is PaginationIndicatorRoomTimelineItem {
isBackPaginating = true isBackPaginating = true
@@ -267,17 +272,19 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
callbacks.send(.isBackPaginating(isBackPaginating)) callbacks.send(.isBackPaginating(isBackPaginating))
} }
private func buildTimelineItem(for itemProxy: TimelineItemProxy) -> RoomTimelineItemProtocol? { private func buildTimelineItem(for itemProxy: TimelineItemProxy, chunkIndex: Int) -> RoomTimelineItemProtocol? {
switch itemProxy { switch itemProxy {
case .event(let eventTimelineItem): case .event(let eventTimelineItem):
return timelineItemFactory.buildTimelineItem(for: eventTimelineItem) return timelineItemFactory.buildTimelineItem(for: eventTimelineItem)
case .virtual(let virtualItem): case .virtual(let virtualItem):
switch virtualItem { switch virtualItem {
case .dayDivider(let timestamp): case .dayDivider(let timestamp):
// These components will be replaced by a timestamp in upcoming releases
let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000)) let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
let dateString = date.formatted(date: .complete, time: .omitted) let dateString = date.formatted(date: .complete, time: .omitted)
return SeparatorRoomTimelineItem(text: dateString)
// Separators without stable identifiers cause UI glitches
let identifier = "\(chunkIndex)-\(dateString)"
return SeparatorRoomTimelineItem(id: identifier, text: dateString)
case .readMarker: case .readMarker:
return ReadMarkerRoomTimelineItem() return ReadMarkerRoomTimelineItem()
case .loadingIndicator: case .loadingIndicator:
@@ -17,7 +17,6 @@
import Foundation import Foundation
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable { struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
#warning("This should really be a stable identifier, ideally coming from the SDK, but unfortunately we receive duplicates for it at the moment, crashing our timeline data source") let id: String
let id: String = UUID().uuidString
let text: String let text: String
} }