mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
+9
-9
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+45
-23
@@ -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
|
||||
|
||||
+9
-9
@@ -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
|
||||
}
|
||||
|
||||
-2
@@ -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? {
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+3
-11
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -16,6 +16,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EncryptedHistoryRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
struct EncryptedHistoryRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
||||
let id: TimelineItemIdentifier
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,6 +16,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id = "paginationIndicatorTimelineItemIdentifier"
|
||||
struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
||||
let id = TimelineItemIdentifier(timelineID: "paginationIndicatorTimelineItemIdentifier")
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,6 +16,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ReadMarkerRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id = "readMarkerTimelineItemIdentifier"
|
||||
struct ReadMarkerRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
||||
let id = TimelineItemIdentifier(timelineID: "readMarkerTimelineItemIdentifier")
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
||||
let id: TimelineItemIdentifier
|
||||
let text: String
|
||||
}
|
||||
|
||||
+2
-2
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user