SDK Changes Refactor (#1312)

* internal id

* notification refactor

* client proxy refactor

* required self

* better identifier system

* using the event id when required

* tests fixed

* tested some stuff

* fixed merge conflict

* improved the test wait

* animation disabled

* Bump Rust SDK version to 1.0.98-alpha (#1310)

* code improvement

* pause sync

* pr suggestions

* result

* Apply suggestions from code review

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>

* enum based debug identifier

---------

Co-authored-by: aringenbach <80891108+aringenbach@users.noreply.github.com>
Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>
This commit is contained in:
Mauro
2023-07-12 12:28:41 +02:00
committed by GitHub
parent adb253ff66
commit 76506752b4
83 changed files with 551 additions and 661 deletions
@@ -19,15 +19,15 @@ import Foundation
enum RoomTimelineItemFixtures {
/// The default timeline items used in Xcode previews etc.
static var `default`: [RoomTimelineItemProtocol] = [
SeparatorRoomTimelineItem(id: "Yesterday", text: "Yesterday"),
TextRoomTimelineItem(id: UUID().uuidString,
SeparatorRoomTimelineItem(id: .init(timelineID: "Yesterday"), text: "Yesterday"),
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "10:10 AM",
isOutgoing: false,
isEditable: false,
sender: .init(id: "", displayName: "Jacob"),
content: .init(body: "That looks so good!"),
properties: RoomTimelineItemProperties(isEdited: true)),
TextRoomTimelineItem(id: UUID().uuidString,
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "10:11 AM",
isOutgoing: false,
isEditable: false,
@@ -36,7 +36,7 @@ enum RoomTimelineItemFixtures {
properties: RoomTimelineItemProperties(reactions: [
AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: ["me"])
])),
TextRoomTimelineItem(id: UUID().uuidString,
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "10:11 AM",
isOutgoing: false,
isEditable: false,
@@ -46,21 +46,21 @@ enum RoomTimelineItemFixtures {
AggregatedReaction(accountOwnerID: "me", key: "🙏", senders: ["helena"]),
AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: ["me", "helena", "jacob"])
])),
SeparatorRoomTimelineItem(id: "Today", text: "Today"),
TextRoomTimelineItem(id: UUID().uuidString,
SeparatorRoomTimelineItem(id: .init(timelineID: "Today"), text: "Today"),
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "5 PM",
isOutgoing: false,
isEditable: false,
sender: .init(id: "", displayName: "Helena"),
content: .init(body: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Here’s the menu, let me know what you want it’s on me!"),
properties: RoomTimelineItemProperties(orderedReadReceipts: [ReadReceipt(userID: "alice", formattedTimestamp: nil)])),
TextRoomTimelineItem(id: UUID().uuidString,
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "5 PM",
isOutgoing: true,
isEditable: true,
sender: .init(id: "", displayName: "Bob"),
content: .init(body: "And John's speech was amazing!")),
TextRoomTimelineItem(id: UUID().uuidString,
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "5 PM",
isOutgoing: true,
isEditable: true,
@@ -71,7 +71,7 @@ enum RoomTimelineItemFixtures {
ReadReceipt(userID: "bob", formattedTimestamp: nil),
ReadReceipt(userID: "charlie", formattedTimestamp: nil),
ReadReceipt(userID: "dan", formattedTimestamp: nil)])),
TextRoomTimelineItem(id: UUID().uuidString,
TextRoomTimelineItem(id: .init(timelineID: UUID().uuidString),
timestamp: "5 PM",
isOutgoing: false,
isEditable: false,
@@ -210,7 +210,7 @@ enum RoomTimelineItemFixtures {
private extension TextRoomTimelineItem {
init(text: String, senderDisplayName: String) {
self.init(id: UUID().uuidString,
self.init(id: .init(timelineID: UUID().uuidString),
timestamp: "10:47 am",
isOutgoing: senderDisplayName == "Alice",
isEditable: false,
@@ -169,37 +169,47 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
}
private extension TimelineItem {
var debugIdentifier: String {
var debugIdentifier: DebugIdentifier {
if let virtualTimelineItem = asVirtual() {
return virtualTimelineItem.debugIdentifier
} else if let eventTimelineItem = asEvent() {
return eventTimelineItem.uniqueIdentifier()
return .event(timelineID: String(uniqueId()),
eventID: eventTimelineItem.eventId(),
transactionID: eventTimelineItem.transactionId())
}
return "UnknownTimelineItem"
return .unknown
}
}
private extension TimelineItemProxy {
var debugIdentifier: String {
var debugIdentifier: DebugIdentifier {
switch self {
case .event(let eventTimelineItem):
return eventTimelineItem.item.uniqueIdentifier()
return .event(timelineID: eventTimelineItem.id.timelineID,
eventID: eventTimelineItem.id.eventID,
transactionID: eventTimelineItem.id.transactionID)
case .virtual(let virtualTimelineItem):
return virtualTimelineItem.debugIdentifier
case .unknown:
return "UnknownTimelineItem"
return .unknown
}
}
}
private extension VirtualTimelineItem {
var debugIdentifier: String {
var debugIdentifier: DebugIdentifier {
switch self {
case .dayDivider(let timestamp):
return "DayDiviver(\(timestamp))"
return .virtual("DayDiviver(\(timestamp))")
case .readMarker:
return "ReadMarker"
return .virtual("ReadMarker")
}
}
}
enum DebugIdentifier {
case event(timelineID: String?, eventID: String?, transactionID: String?)
case virtual(String)
case unknown
}
@@ -51,23 +51,23 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
func markRoomAsRead() async -> Result<Void, RoomTimelineControllerError> { .success(()) }
func processItemAppearance(_ itemID: String) async { }
func processItemAppearance(_ itemID: TimelineItemIdentifier) async { }
func processItemDisappearance(_ itemID: String) async { }
func processItemDisappearance(_ itemID: TimelineItemIdentifier) async { }
func processItemTap(_ itemID: String) async -> RoomTimelineControllerAction { .none }
func processItemTap(_ itemID: TimelineItemIdentifier) async -> RoomTimelineControllerAction { .none }
func sendMessage(_ message: String, inReplyTo itemID: String?) async { }
func sendMessage(_ message: String, inReplyTo itemID: TimelineItemIdentifier?) async { }
func toggleReaction(_ reaction: String, to itemID: String) async { }
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { }
func editMessage(_ newMessage: String, original itemID: String) async { }
func editMessage(_ newMessage: String, original itemID: TimelineItemIdentifier) async { }
func redact(_ itemID: String) async { }
func redact(_ itemID: TimelineItemIdentifier) async { }
func cancelSend(_ transactionID: String) async { }
func cancelSend(_ itemID: TimelineItemIdentifier) async { }
func debugInfo(for itemID: String) -> TimelineItemDebugInfo {
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
.init(model: "Mock debug description", originalJSON: nil, latestEditJSON: nil)
}
@@ -84,7 +84,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
func markRoomAsRead() async -> Result<Void, RoomTimelineControllerError> {
guard roomProxy.hasUnreadNotifications,
let eventID = timelineItems.last?.id
let eventID = timelineItems.last?.id.eventID
else { return .success(()) }
switch await roomProxy.sendReadReceipt(for: eventID) {
@@ -95,7 +95,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func processItemAppearance(_ itemID: String) async {
func processItemAppearance(_ itemID: TimelineItemIdentifier) async {
guard let timelineItem = timelineItems.first(where: { $0.id == itemID }) else {
return
}
@@ -105,9 +105,9 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func processItemDisappearance(_ itemID: String) { }
func processItemDisappearance(_ itemID: TimelineItemIdentifier) { }
func processItemTap(_ itemID: String) async -> RoomTimelineControllerAction {
func processItemTap(_ itemID: TimelineItemIdentifier) async -> RoomTimelineControllerAction {
guard let timelineItem = timelineItems.first(where: { $0.id == itemID }) else {
return .none
}
@@ -121,14 +121,19 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func sendMessage(_ message: String, inReplyTo itemID: String?) async {
func sendMessage(_ message: String, inReplyTo itemID: TimelineItemIdentifier?) async {
var inReplyTo: String?
if itemID == nil {
MXLog.info("Send message in \(roomID)")
} else {
} else if let eventID = itemID?.eventID {
inReplyTo = eventID
MXLog.info("Send reply in \(roomID)")
} else {
MXLog.error("Send reply in \(roomID) failed: missing event ID")
return
}
switch await roomProxy.sendMessage(message, inReplyTo: itemID) {
switch await roomProxy.sendMessage(message, inReplyTo: inReplyTo) {
case .success:
MXLog.info("Finished sending message")
case .failure(let error):
@@ -136,9 +141,14 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func toggleReaction(_ reaction: String, to itemID: String) async {
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async {
MXLog.info("Toggle reaction in \(roomID)")
switch await roomProxy.toggleReaction(reaction, to: itemID) {
guard let eventID = itemID.eventID else {
MXLog.error("Failed toggling reaction: missing eventID")
return
}
switch await roomProxy.toggleReaction(reaction, to: eventID) {
case .success:
MXLog.info("Finished toggling reaction")
case .failure(let error):
@@ -146,28 +156,32 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func editMessage(_ newMessage: String, original itemID: String) async {
func editMessage(_ newMessage: String, original itemID: TimelineItemIdentifier) async {
MXLog.info("Edit message in \(roomID)")
if let timelineItem = timelineItems.first(where: { $0.id == itemID }),
let item = timelineItem as? EventBasedTimelineItemProtocol,
item.hasFailedToSend,
let transactionID = item.properties.transactionID {
item.hasFailedToSend {
MXLog.info("Editing a failed echo, will cancel and resend it as a new message")
await cancelSend(transactionID)
await cancelSend(itemID)
await sendMessage(newMessage)
} else {
switch await roomProxy.editMessage(newMessage, original: itemID) {
} else if let eventID = itemID.eventID {
switch await roomProxy.editMessage(newMessage, original: eventID) {
case .success:
MXLog.info("Finished editing message")
case .failure(let error):
MXLog.error("Failed editing message with error: \(error)")
}
} else {
MXLog.error("Editing failed: missing identifiers")
}
}
func redact(_ itemID: String) async {
func redact(_ itemID: TimelineItemIdentifier) async {
MXLog.info("Send redaction in \(roomID)")
switch await roomProxy.redact(itemID) {
guard let eventID = itemID.eventID else {
return
}
switch await roomProxy.redact(eventID) {
case .success:
MXLog.info("Finished redacting message")
case .failure(let error):
@@ -175,14 +189,18 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func cancelSend(_ transactionID: String) async {
func cancelSend(_ itemID: TimelineItemIdentifier) async {
guard let transactionID = itemID.transactionID else {
MXLog.error("Failed cancelling send, missing transaction ID")
return
}
MXLog.info("Cancelling send in \(roomID)")
await roomProxy.cancelSend(transactionID: transactionID)
}
// Handle this parallel to the timeline items so we're not forced
// to bundle the Rust side objects within them
func debugInfo(for itemID: String) -> TimelineItemDebugInfo {
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
for timelineItemProxy in timelineProvider.itemProxies {
switch timelineItemProxy {
case .event(let item):
@@ -339,7 +357,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
// Separators without stable identifiers cause UI glitches
let identifier = "\(chunkIndex)-\(dateString)"
return SeparatorRoomTimelineItem(id: identifier, text: dateString)
return SeparatorRoomTimelineItem(id: .init(timelineID: identifier), text: dateString)
case .readMarker:
return ReadMarkerRoomTimelineItem()
}
@@ -373,12 +391,16 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
private func fetchEventDetails(for timelineItem: EventBasedMessageTimelineItemProtocol, refetchOnError: Bool) {
guard let eventID = timelineItem.id.eventID else {
return
}
switch timelineItem.replyDetails {
case .notLoaded:
roomProxy.fetchDetails(for: timelineItem.id)
roomProxy.fetchDetails(for: eventID)
case .error:
if refetchOnError {
roomProxy.fetchDetails(for: timelineItem.id)
roomProxy.fetchDetails(for: eventID)
}
default:
break
@@ -41,27 +41,27 @@ protocol RoomTimelineControllerProtocol {
var timelineItems: [RoomTimelineItemProtocol] { get }
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
func processItemAppearance(_ itemID: String) async
func processItemAppearance(_ itemID: TimelineItemIdentifier) async
func processItemDisappearance(_ itemID: String) async
func processItemDisappearance(_ itemID: TimelineItemIdentifier) async
func processItemTap(_ itemID: String) async -> RoomTimelineControllerAction
func processItemTap(_ itemID: TimelineItemIdentifier) async -> RoomTimelineControllerAction
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError>
func markRoomAsRead() async -> Result<Void, RoomTimelineControllerError>
func sendMessage(_ message: String, inReplyTo itemID: String?) async
func sendMessage(_ message: String, inReplyTo itemID: TimelineItemIdentifier?) async
func editMessage(_ newMessage: String, original itemID: String) async
func editMessage(_ newMessage: String, original itemID: TimelineItemIdentifier) async
func toggleReaction(_ reaction: String, to itemID: String) async
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async
func redact(_ itemID: String) async
func redact(_ itemID: TimelineItemIdentifier) async
func cancelSend(_ transactionID: String) async
func cancelSend(_ itemID: TimelineItemIdentifier) async
func debugInfo(for itemID: String) -> TimelineItemDebugInfo
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo
func retryDecryption(for sessionID: String) async
}
@@ -26,6 +26,4 @@ struct RoomTimelineItemProperties: Hashable {
var deliveryStatus: TimelineItemDeliveryStatus?
/// The read receipts of the item, ordered from newest to oldest
var orderedReadReceipts: [ReadReceipt] = []
/// The original transaction id transmitted by the client
var transactionID: String?
}
@@ -17,6 +17,20 @@
import Foundation
import MatrixRustSDK
struct TimelineItemIdentifier: Hashable {
/// Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline.
/// It's value is consistent only per timeline instance, it should **not** be used to identify an item across timeline instances.
let timelineID: String
/// Uniquely identifies the timeline item from the server side.
/// Only available for EventTimelineItem and only when the item is returned by the server.
var eventID: String?
/// Uniquely identfies the local echo of the timeline item.
/// Only available for sent EventTimelineItem that have not been returned by the server yet.
var transactionID: String?
}
/// A light wrapper around timeline items returned from Rust.
enum TimelineItemProxy {
case event(EventTimelineItemProxy)
@@ -25,7 +39,7 @@ enum TimelineItemProxy {
init(item: MatrixRustSDK.TimelineItem) {
if let eventItem = item.asEvent() {
self = .event(EventTimelineItemProxy(item: eventItem))
self = .event(EventTimelineItemProxy(item: eventItem, id: item.uniqueId()))
} else if let virtualItem = item.asVirtual() {
self = .virtual(virtualItem)
} else {
@@ -44,17 +58,11 @@ enum TimelineItemDeliveryStatus: Hashable {
/// A light wrapper around event timeline items returned from Rust.
struct EventTimelineItemProxy {
let item: MatrixRustSDK.EventTimelineItem
let id: TimelineItemIdentifier
init(item: MatrixRustSDK.EventTimelineItem) {
init(item: MatrixRustSDK.EventTimelineItem, id: UInt64) {
self.item = item
}
var id: String {
item.uniqueIdentifier()
}
var transactionID: String? {
item.transactionId()
self.id = TimelineItemIdentifier(timelineID: String(id), eventID: item.eventId(), transactionID: item.transactionId())
}
var deliveryStatus: TimelineItemDeliveryStatus? {
@@ -16,8 +16,8 @@
import Foundation
struct AudioRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct AudioRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
@@ -16,8 +16,8 @@
import UIKit
struct EmoteRoomTimelineItem: TextBasedRoomTimelineItem, Identifiable, Hashable {
let id: String
struct EmoteRoomTimelineItem: TextBasedRoomTimelineItem, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
@@ -17,8 +17,8 @@
import UIKit
import UniformTypeIdentifiers
struct FileRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct FileRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
@@ -17,8 +17,8 @@
import UIKit
import UniformTypeIdentifiers
struct ImageRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct ImageRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
@@ -14,8 +14,8 @@
// limitations under the License.
//
struct LocationRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Hashable {
let id: String
struct LocationRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
@@ -16,8 +16,8 @@
import UIKit
struct NoticeRoomTimelineItem: TextBasedRoomTimelineItem, Identifiable, Hashable {
let id: String
struct NoticeRoomTimelineItem: TextBasedRoomTimelineItem, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
@@ -16,8 +16,8 @@
import UIKit
struct TextRoomTimelineItem: TextBasedRoomTimelineItem, Identifiable, Hashable {
let id: String
struct TextRoomTimelineItem: TextBasedRoomTimelineItem, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
@@ -17,8 +17,8 @@
import UIKit
import UniformTypeIdentifiers
struct VideoRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct VideoRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
@@ -16,10 +16,10 @@
import Foundation
struct CollapsibleTimelineItem: RoomTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct CollapsibleTimelineItem: RoomTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let items: [RoomTimelineItemProtocol]
let itemIDs: [String]
let itemIDs: [TimelineItemIdentifier]
init(items: [RoomTimelineItemProtocol]) {
self.items = items
@@ -38,12 +38,4 @@ struct CollapsibleTimelineItem: RoomTimelineItemProtocol, Identifiable, Hashable
// Technically not a correct implementation of equality as the items themselves could be updated.
lhs.id == rhs.id && lhs.itemIDs == rhs.itemIDs
}
// MARK: - Hashable
func hash(into hasher: inout Hasher) {
// Technically not a correct implementation of hashing as the items themselves could be updated.
hasher.combine(id)
hasher.combine(itemIDs)
}
}
@@ -16,14 +16,14 @@
import UIKit
struct EncryptedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct EncryptedRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable {
enum EncryptionType: Hashable {
case megolmV1AesSha2(sessionId: String)
case olmV1Curve25519AesSha2(senderKey: String)
case unknown
}
let id: String
let id: TimelineItemIdentifier
let body: String
let encryptionType: EncryptionType
let timestamp: String
@@ -17,8 +17,8 @@
import Foundation
import UIKit
struct RedactedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct RedactedRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let body: String
let timestamp: String
let isOutgoing: Bool
@@ -16,8 +16,8 @@
import UIKit
struct StateRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct StateRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let body: String
let timestamp: String
let isOutgoing: Bool
@@ -16,8 +16,8 @@
import UIKit
struct StickerRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct StickerRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let body: String
let timestamp: String
let isOutgoing: Bool
@@ -16,8 +16,8 @@
import UIKit
struct UnsupportedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct UnsupportedRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let body: String
let eventType: String
@@ -16,6 +16,6 @@
import Foundation
struct EncryptedHistoryRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct EncryptedHistoryRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
}
@@ -16,6 +16,6 @@
import Foundation
struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
let id = "paginationIndicatorTimelineItemIdentifier"
struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id = TimelineItemIdentifier(timelineID: "paginationIndicatorTimelineItemIdentifier")
}
@@ -16,6 +16,6 @@
import Foundation
struct ReadMarkerRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
let id = "readMarkerTimelineItemIdentifier"
struct ReadMarkerRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id = TimelineItemIdentifier(timelineID: "readMarkerTimelineItemIdentifier")
}
@@ -16,7 +16,7 @@
import Foundation
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
let id: String
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier
let text: String
}
@@ -16,7 +16,7 @@
import Foundation
struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
let id: String = UUID().uuidString
struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id = TimelineItemIdentifier(timelineID: UUID().uuidString)
let name: String?
}
@@ -138,8 +138,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
blurhash: imageInfo.blurhash,
properties: RoomTimelineItemProperties(reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildEncryptedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
@@ -190,8 +189,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildImageTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -208,8 +206,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildVideoTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -226,8 +223,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildAudioTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -244,8 +240,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildFileTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -262,8 +257,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildNoticeTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -280,8 +274,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildEmoteTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -298,8 +291,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func buildLocationTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -316,13 +308,12 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
transactionID: eventItemProxy.transactionID))
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts)))
}
private func aggregateReactions(_ reactions: [Reaction]) -> [AggregatedReaction] {
reactions.map { reaction in
AggregatedReaction(accountOwnerID: userID, key: reaction.key, senders: reaction.senders)
AggregatedReaction(accountOwnerID: userID, key: reaction.key, senders: reaction.senders.map(\.senderId))
}
.sorted { a, b in
// Sort by count and then by key for a consistence experience.
@@ -18,5 +18,5 @@ import Foundation
import UIKit
protocol RoomTimelineItemProtocol {
var id: String { get }
var id: TimelineItemIdentifier { get }
}
@@ -24,7 +24,7 @@ final class RoomTimelineItemViewModel: Identifiable, Equatable, ObservableObject
@Published var type: RoomTimelineItemType
@Published var groupStyle: TimelineGroupStyle
var id: String {
var id: TimelineItemIdentifier {
type.id
}
@@ -109,7 +109,7 @@ enum RoomTimelineItemType: Equatable {
}
}
var id: String {
var id: TimelineItemIdentifier {
switch self {
case .text(let item as RoomTimelineItemProtocol),
.separator(let item as RoomTimelineItemProtocol),