Switched to discrete timeline items that directly expose view builders.

This commit is contained in:
Stefan Ceriu
2022-03-11 11:27:08 +02:00
parent 8d4f6f301b
commit d413a67329
11 changed files with 175 additions and 106 deletions
@@ -10,8 +10,10 @@ import Foundation
import Combine
class MockRoomTimelineController: RoomTimelineControllerProtocol {
let timelineItems: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(id: UUID().uuidString, senderDisplayName: "Anne", text: "You rock!", originServerTs: .now),
TextRoomTimelineItem(id: UUID().uuidString, senderDisplayName: "Bob", text: "You rule!", originServerTs: .now)]
let timelineItems: [RoomTimelineItem] = [RoomTimelineItem.text(id: UUID().uuidString, senderDisplayName: "Anne", text: "You rock!", originServerTs: .now, shouldShowSenderDetails: true),
RoomTimelineItem.text(id: UUID().uuidString, senderDisplayName: "Anne", text: "Some other message from Anne", originServerTs: .now, shouldShowSenderDetails: false),
RoomTimelineItem.sectionTitle(id: UUID().uuidString, text: "The next day"),
RoomTimelineItem.text(id: UUID().uuidString, senderDisplayName: "Bob", text: "You rule!", originServerTs: .now, shouldShowSenderDetails: true)]
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
func paginateBackwards(_ count: UInt) {
@@ -14,13 +14,20 @@ enum RoomTimelineControllerCallback {
case updatedTimelineItems
}
private var sectionTitleDateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .none
return dateFormatter
}()
class RoomTimelineController: RoomTimelineControllerProtocol {
private let timelineProvider: RoomTimelineProvider
private var cancellables = Set<AnyCancellable>()
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
private(set) var timelineItems = [RoomTimelineItemProtocol]()
private(set) var timelineItems = [RoomTimelineItem]()
init(timelineProvider: RoomTimelineProvider) {
self.timelineProvider = timelineProvider
@@ -30,13 +37,36 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
switch callback {
case .updatedMessages:
self.timelineItems = self.timelineProvider.messages.map { message in
var newTimelineItems = [RoomTimelineItem]()
var previousMessage: Message?
var previousSender: String?
for message in self.timelineProvider.messages {
let timestamp = Date(timeIntervalSince1970: TimeInterval(message.originServerTs()))
return TextRoomTimelineItem(id: message.id(),
senderDisplayName: message.sender(),
text: message.content(),
originServerTs: timestamp)
let areMessagesFromTheSameDay = self.haveSameDay(lhs: previousMessage, rhs: message)
// let shouldAddSectionHeader = !areMessagesFromTheSameDay
//
// if shouldAddSectionHeader {
// newTimelineItems.append(RoomTimelineItem.sectionTitle(id: message.id(),
// text: sectionTitleDateFormatter.string(from: timestamp)))
// }
let areMessagesFromTheSameSender = previousSender == message.sender()
let shouldShowSenderDetails = !areMessagesFromTheSameSender || !areMessagesFromTheSameDay
newTimelineItems.append(RoomTimelineItem.text(id: message.id(),
senderDisplayName: message.sender(),
text: message.content(),
originServerTs: timestamp,
shouldShowSenderDetails: shouldShowSenderDetails))
previousMessage = message
previousSender = message.sender()
}
self.timelineItems = newTimelineItems
self.callbacks.send(.updatedTimelineItems)
}
}.store(in: &cancellables)
@@ -45,4 +75,18 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
func paginateBackwards(_ count: UInt) {
timelineProvider.paginateBackwards(count)
}
// MARK: - Private
private func haveSameDay(lhs: Message?, rhs: Message?) -> Bool {
guard let lhs = lhs, let rhs = rhs else {
return false
}
let lhsTimestamp = Date(timeIntervalSince1970: TimeInterval(lhs.originServerTs()))
let rhsTimestamp = Date(timeIntervalSince1970: TimeInterval(rhs.originServerTs()))
return Calendar.current.isDate(lhsTimestamp, inSameDayAs: rhsTimestamp)
}
}
@@ -10,7 +10,7 @@ import Foundation
import Combine
protocol RoomTimelineControllerProtocol {
var timelineItems: [RoomTimelineItemProtocol] { get }
var timelineItems: [RoomTimelineItem] { get }
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
func paginateBackwards(_ count: UInt)
@@ -0,0 +1,84 @@
//
// TextRoomTimelineItem.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import SwiftUI
private var dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .none
dateFormatter.timeStyle = .short
return dateFormatter
}()
enum RoomTimelineItem: Identifiable, Equatable {
case text(id: String, senderDisplayName: String, text: String, originServerTs: Date, shouldShowSenderDetails: Bool)
case sectionTitle(id: String, text: String)
var id: String {
switch self {
case .text(let id, _, _, _, _):
return id
case .sectionTitle(let id, _):
return id
}
}
}
extension RoomTimelineItem: View {
var body: some View {
switch self {
case .text(let id, let senderDisplayName, let text, let originServerTs, let shouldShowSenderDetails):
VStack(alignment: .leading) {
if shouldShowSenderDetails {
HStack {
Text(senderDisplayName)
.font(.footnote)
.bold()
Spacer()
Text(dateFormatter.string(from: originServerTs))
.font(.footnote)
}
Divider()
Spacer()
}
Text(text)
}
.listRowSeparator(.hidden)
.id(id)
case .sectionTitle(let id, let text):
LabelledDivider(label: text)
.id(id)
}
}
}
struct LabelledDivider: View {
let label: String
let color: Color
init(label: String, color: Color = .gray) {
self.label = label
self.color = color
}
var body: some View {
HStack {
line
Text(label)
.foregroundColor(color)
.fixedSize()
line
}
}
var line: some View {
VStack { Divider().background(color) }
}
}
@@ -1,16 +0,0 @@
//
// RoomTimelineItemProtocol.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
protocol RoomTimelineItemProtocol {
var id: String { get }
var senderDisplayName: String { get }
var text: String { get }
var originServerTs: Date { get }
}
@@ -1,16 +0,0 @@
//
// TextRoomTimelineItem.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
struct TextRoomTimelineItem: RoomTimelineItemProtocol {
let id: String
let senderDisplayName: String
let text: String
let originServerTs: Date
}